diff options
-rw-r--r-- | arch/arm/boards/kontron-samx6i/mem.c | 54 | ||||
-rw-r--r-- | arch/arm/boards/nxp-imx8mm-evk/lowlevel.c | 5 | ||||
-rw-r--r-- | arch/arm/dts/imx6dl-samx6i.dts | 36 | ||||
-rw-r--r-- | arch/arm/dts/imx6q-samx6i.dts | 36 | ||||
-rw-r--r-- | arch/arm/dts/imx6qdl-phytec-pfla02.dtsi | 24 | ||||
-rw-r--r-- | arch/arm/dts/imx6qdl-smarc-samx6i.dtsi | 471 | ||||
-rw-r--r-- | arch/arm/mach-imx/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/mach-imx/cpu_init.c | 4 | ||||
-rw-r--r-- | arch/arm/mach-imx/imx8m.c | 6 | ||||
-rw-r--r-- | arch/arm/mach-imx/include/mach/xload.h | 1 | ||||
-rw-r--r-- | arch/arm/mach-imx/xload-gpmi-nand.c | 1217 | ||||
-rw-r--r-- | common/imx-bbu-nand-fcb.c | 69 | ||||
-rw-r--r-- | drivers/net/smc911x.c | 2 | ||||
-rw-r--r-- | include/soc/imx/imx-nand-bcb.h | 80 |
14 files changed, 1403 insertions, 604 deletions
diff --git a/arch/arm/boards/kontron-samx6i/mem.c b/arch/arm/boards/kontron-samx6i/mem.c index 3b9fbd464a..08dceb55c0 100644 --- a/arch/arm/boards/kontron-samx6i/mem.c +++ b/arch/arm/boards/kontron-samx6i/mem.c @@ -17,7 +17,6 @@ #include "mem.h" -#define PCBVERSION_PIN IMX_GPIO_NR(2, 2) #define PCBID0_PIN IMX_GPIO_NR(6, 7) #define PCBID1_PIN IMX_GPIO_NR(6, 9) @@ -25,62 +24,47 @@ IOMUX_PAD(0x0658, 0x0270, 5, 0x0000, 0, 0) #define MX6S_PAD_NANDF_WP_B__GPIO_6_9 \ IOMUX_PAD(0x0690, 0x02A8, 5, 0x0000, 0, 0) -#define MX6S_PAD_NANDF_D2__GPIO_2_2 \ - IOMUX_PAD(0x028c, 0x0674, 5, 0x0000, 0, 0) resource_size_t samx6i_get_size(void) { resource_size_t size = 0; - int ver, id0, id1; + int id0, id1; int cpu_type = __imx6_cpu_type(); void __iomem *iomuxbase = IOMEM(MX6_IOMUXC_BASE_ADDR); void __iomem *gpio6 = IOMEM(MX6_GPIO6_BASE_ADDR); - void __iomem *gpio2 = IOMEM(MX6_GPIO2_BASE_ADDR); if (cpu_type == IMX6_CPUTYPE_IMX6D || - cpu_type == IMX6_CPUTYPE_IMX6Q) { + cpu_type == IMX6_CPUTYPE_IMX6Q) { imx_setup_pad(iomuxbase, MX6Q_PAD_NANDF_CLE__GPIO_6_7); imx_setup_pad(iomuxbase, MX6Q_PAD_NANDF_WP_B__GPIO_6_9); - imx_setup_pad(iomuxbase, MX6Q_PAD_NANDF_D2__GPIO_2_2); } else if (cpu_type == IMX6_CPUTYPE_IMX6S || - cpu_type == IMX6_CPUTYPE_IMX6DL) { + cpu_type == IMX6_CPUTYPE_IMX6DL) { imx_setup_pad(iomuxbase, MX6S_PAD_NANDF_CLE__GPIO_6_7); imx_setup_pad(iomuxbase, MX6S_PAD_NANDF_WP_B__GPIO_6_9); - imx_setup_pad(iomuxbase, MX6S_PAD_NANDF_D2__GPIO_2_2); }; - imx6_gpio_direction_input(gpio6, 6); + imx6_gpio_direction_input(gpio6, 7); imx6_gpio_direction_input(gpio6, 9); - imx6_gpio_direction_input(gpio2, 2); - ver = imx6_gpio_val(gpio2, 2); id0 = imx6_gpio_val(gpio6, 7); id1 = imx6_gpio_val(gpio6, 9); - if (cpu_type == IMX6_CPUTYPE_IMX6D || - cpu_type == IMX6_CPUTYPE_IMX6Q) { - if (ver) - size = SZ_1G; - else if (id0 && id1) - size = SZ_2G; - else if (id0) - size = SZ_2G; - else if (id1) - size = SZ_1G; - else - size = SZ_512M; - } else if (cpu_type == IMX6_CPUTYPE_IMX6S || - cpu_type == IMX6_CPUTYPE_IMX6DL) { - if (ver) - size = SZ_512M; - if (id0 && id1) - size = SZ_2G; - else if (id0) - size = SZ_1G; - else if (id1) - size = SZ_512M; + /* Solo/DualLite module sizes */ + if (id0 && id1) + size = SZ_2G; + else if (id0) + size = SZ_1G; + else if (id1) + size = SZ_512M; + else + size = SZ_256M; + + /* Dual/Quad modules always have twice the size */ + if (cpu_type == IMX6_CPUTYPE_IMX6D || cpu_type == IMX6_CPUTYPE_IMX6Q) { + if (size == SZ_2G) + size = 0xf0000000; /* 4G on a 32bit system */ else - size = SZ_128M; + size *= 2; } return size; diff --git a/arch/arm/boards/nxp-imx8mm-evk/lowlevel.c b/arch/arm/boards/nxp-imx8mm-evk/lowlevel.c index 2297dc01e7..4bd29c2269 100644 --- a/arch/arm/boards/nxp-imx8mm-evk/lowlevel.c +++ b/arch/arm/boards/nxp-imx8mm-evk/lowlevel.c @@ -172,11 +172,6 @@ static __noreturn noinline void nxp_imx8mm_evk_start(void) ENTRY_FUNCTION(start_nxp_imx8mm_evk, r0, r1, r2) { - void __iomem *ccm = IOMEM(MX8M_CCM_BASE_ADDR); - - writel(IMX8M_CCM_CCGR_SETTINGn_NEEDED(0), - ccm + IMX8M_CCM_CCGRn_SET(IMX8M_CCM_CCGR_SCTR)); - imx8mm_cpu_lowlevel_init(); relocate_to_current_adr(); diff --git a/arch/arm/dts/imx6dl-samx6i.dts b/arch/arm/dts/imx6dl-samx6i.dts index d688b9c6ca..da648ef5b8 100644 --- a/arch/arm/dts/imx6dl-samx6i.dts +++ b/arch/arm/dts/imx6dl-samx6i.dts @@ -1,20 +1,34 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright 2018 (C) Pengutronix, Michael Grzeschik <mgr@pengutronix.de> - * - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html + * Copyright 2019 (C) Pengutronix, Marco Felsch <kernel@pengutronix.de> */ /dts-v1/; -#include <arm/imx6dl.dtsi> +#include <arm/imx6dl-kontron-samx6i.dtsi> #include "imx6dl.dtsi" #include "imx6qdl-smarc-samx6i.dtsi" -/ { - model = "Kontron sAMX6i"; - compatible = "kontron,imx6dl-samx6i", "fsl,imx6dl"; +&fec { + status = "okay"; +}; + +&ecspi4 { + status = "okay"; +}; + +&uart2 { + status = "okay"; +}; + +&usbotg { + status = "okay"; +}; + +&usbh1 { + status = "okay"; +}; + +&usdhc4 { + status = "okay"; }; diff --git a/arch/arm/dts/imx6q-samx6i.dts b/arch/arm/dts/imx6q-samx6i.dts index 83f19bcaf8..a2ea076edf 100644 --- a/arch/arm/dts/imx6q-samx6i.dts +++ b/arch/arm/dts/imx6q-samx6i.dts @@ -1,20 +1,34 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright 2018 (C) Pengutronix, Michael Grzeschik <mgr@pengutronix.de> - * - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html + * Copyright 2019 (C) Pengutronix, Marco Felsch <kernel@pengutronix.de> */ /dts-v1/; -#include <arm/imx6q.dtsi> +#include <arm/imx6q-kontron-samx6i.dtsi> #include "imx6q.dtsi" #include "imx6qdl-smarc-samx6i.dtsi" -/ { - model = "Kontron sAMX6i"; - compatible = "kontron,imx6q-samx6i", "fsl,imx6q"; +&fec { + status = "okay"; +}; + +&ecspi4 { + status = "okay"; +}; + +&uart2 { + status = "okay"; +}; + +&usbotg { + status = "okay"; +}; + +&usbh1 { + status = "okay"; +}; + +&usdhc4 { + status = "okay"; }; diff --git a/arch/arm/dts/imx6qdl-phytec-pfla02.dtsi b/arch/arm/dts/imx6qdl-phytec-pfla02.dtsi index f499ca5684..e1aa3183b3 100644 --- a/arch/arm/dts/imx6qdl-phytec-pfla02.dtsi +++ b/arch/arm/dts/imx6qdl-phytec-pfla02.dtsi @@ -127,30 +127,6 @@ MX6QDL_PAD_SD4_DAT7__GPIO2_IO15 0x80000000 >; }; - - pinctrl_gpmi_nand: gpminandgrp { - fsl,pins = < - MX6QDL_PAD_NANDF_CLE__NAND_CLE 0xb0b1 - MX6QDL_PAD_NANDF_ALE__NAND_ALE 0xb0b1 - MX6QDL_PAD_NANDF_WP_B__NAND_WP_B 0xb0b1 - MX6QDL_PAD_NANDF_RB0__NAND_READY_B 0xb000 - MX6QDL_PAD_NANDF_CS0__NAND_CE0_B 0xb0b1 - MX6QDL_PAD_NANDF_CS1__NAND_CE1_B 0xb0b1 - MX6QDL_PAD_NANDF_CS2__NAND_CE2_B 0xb0b1 - MX6QDL_PAD_NANDF_CS3__NAND_CE3_B 0xb0b1 - MX6QDL_PAD_SD4_CMD__NAND_RE_B 0xb0b1 - MX6QDL_PAD_SD4_CLK__NAND_WE_B 0xb0b1 - MX6QDL_PAD_NANDF_D0__NAND_DATA00 0xb0b1 - MX6QDL_PAD_NANDF_D1__NAND_DATA01 0xb0b1 - MX6QDL_PAD_NANDF_D2__NAND_DATA02 0xb0b1 - MX6QDL_PAD_NANDF_D3__NAND_DATA03 0xb0b1 - MX6QDL_PAD_NANDF_D4__NAND_DATA04 0xb0b1 - MX6QDL_PAD_NANDF_D5__NAND_DATA05 0xb0b1 - MX6QDL_PAD_NANDF_D6__NAND_DATA06 0xb0b1 - MX6QDL_PAD_NANDF_D7__NAND_DATA07 0xb0b1 - MX6QDL_PAD_SD4_DAT0__NAND_DQS 0x00b1 - >; - }; }; }; diff --git a/arch/arm/dts/imx6qdl-smarc-samx6i.dtsi b/arch/arm/dts/imx6qdl-smarc-samx6i.dtsi index 363da66ec7..504cd06de1 100644 --- a/arch/arm/dts/imx6qdl-smarc-samx6i.dtsi +++ b/arch/arm/dts/imx6qdl-smarc-samx6i.dtsi @@ -1,47 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 OR X11 /* * Copyright 2017 (C) Priit Laes <plaes@plaes.org> * Copyright 2018 (C) Pengutronix, Michael Grzeschik <mgr@pengutronix.de> + * Copyright 2019 (C) Pengutronix, Marco Felsch <kernel@pengutronix.de> * * Based on initial work by Nikita Yushchenko <nyushchenko at dev.rtsoft.ru> - * - * This file is dual-licensed: you can use it either under the terms - * of the GPL or the X11 license, at your option. Note that this dual - * licensing only applies to this file, and not this project as a - * whole. - * - * a) This file is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This file is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * Or, alternatively, - * - * b) Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * */ #include <dt-bindings/gpio/gpio.h> @@ -62,281 +25,39 @@ status = "disabled"; }; }; - - reg_3v3_s5: regulator@0 { - compatible = "regulator-fixed"; - regulator-name = "V_3V3_S5"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - regulator-boot-on; - regulator-always-on; - }; - - reg_1v8_s5: regulator@1 { - compatible = "regulator-fixed"; - regulator-name = "V_1V8_S5"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - regulator-boot-on; - regulator-always-on; - }; - - reg_3v3_s0: regulator@2 { - compatible = "regulator-fixed"; - regulator-name = "V_3V3_S0"; - regulator-min-microvolt = <3300000>; - regulator-max-microvolt = <3300000>; - regulator-boot-on; - regulator-always-on; - }; - - reg_1v0_s0: regulator@3 { - compatible = "regulator-fixed"; - regulator-name = "V_1V0_S0"; - regulator-min-microvolt = <1000000>; - regulator-max-microvolt = <1000000>; - regulator-boot-on; - regulator-always-on; - }; - - i2c_pfuze: i2c-gpio-0 { - compatible = "i2c-gpio"; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_i2c_gpio_0>; - sda-gpios = <&gpio1 28 0>; - scl-gpios = <&gpio1 30 0>; - #address-cells = <1>; - #size-cells = <0>; - i2c-gpio,delay-us = <2>; - }; -}; - -&can1 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_flexcan1>; }; -&can2 { +&gpio2 { pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_flexcan2>; + pinctrl-0 = <&pinctrl_gpio2_hog>; }; -&fec { +&gpio6 { pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_enet_smarc>; - phy-reset-gpios = <&gpio1 25 GPIO_ACTIVE_LOW>; - phy-mode = "rgmii"; - status = "okay"; + pinctrl-0 = <&pinctrl_gpio6_hog>; }; -&i2c_pfuze { - pfuze100@08 { - compatible = "fsl,pfuze100"; - reg = <0x08>; - - /* Looks unused by pfuze100 driver */ - interrupt-parent = <&gpio7>; - interrupts = <13 IRQ_TYPE_LEVEL_LOW>; - - regulators { - reg_v_core_s0: sw1ab { - regulator-name = "V_CORE_S0"; - regulator-min-microvolt = <300000>; - regulator-max-microvolt = <1875000>; - regulator-boot-on; - regulator-always-on; - }; - - reg_vddsoc_s0: sw1c { - regulator-name = "V_VDDSOC_S0"; - regulator-min-microvolt = <300000>; - regulator-max-microvolt = <1875000>; - regulator-boot-on; - regulator-always-on; - }; - - reg_3v15_s0: sw2 { - regulator-name = "V_3V15_S0"; - regulator-min-microvolt = <800000>; - regulator-max-microvolt = <3300000>; - regulator-boot-on; - regulator-always-on; - }; - - /* sw3a/b is used in dual mode, but driver does not - * support it? Although, there's no need to control - * DDR power - so just leaving dummy entries for sw3a - * and sw3b for now. - */ - sw3a { - regulator-min-microvolt = <400000>; - regulator-max-microvolt = <1975000>; - regulator-boot-on; - regulator-always-on; - }; - - sw3b { - regulator-min-microvolt = <400000>; - regulator-max-microvolt = <1975000>; - regulator-boot-on; - regulator-always-on; - }; - - reg_1v8_s0: sw4 { - regulator-name = "V_1V8_S0"; - regulator-min-microvolt = <800000>; - regulator-max-microvolt = <3300000>; - regulator-boot-on; - regulator-always-on; - }; - - /* Regulator for USB */ - reg_5v0_s0: swbst { - regulator-name = "V_5V0_S0"; - regulator-min-microvolt = <5000000>; - regulator-max-microvolt = <5150000>; - regulator-boot-on; - }; - - reg_vsnvs: vsnvs { - regulator-min-microvolt = <1000000>; - regulator-max-microvolt = <3000000>; - regulator-boot-on; - regulator-always-on; - }; - - reg_vrefddr: vrefddr { - regulator-boot-on; - regulator-always-on; - }; - - /* Per schematics, of all VGEN's, only VGEN5 has some - * usage ... but even that - over DNI resistor - */ - vgen1 { - regulator-min-microvolt = <800000>; - regulator-max-microvolt = <1550000>; - }; - - vgen2 { - regulator-min-microvolt = <800000>; - regulator-max-microvolt = <1550000>; - }; - - vgen3 { - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <3300000>; - }; - - vgen4 { - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <3300000>; - }; - - reg_2v5_s0: vgen5 { - regulator-name = "V_2V5_S0"; - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <3300000>; - }; +&smarc_flash { + #address-cells = <1>; + #size-cells = <1>; - vgen6 { - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <3300000>; - }; - }; + partition@0 { + reg = <0x0 0x0c0000>; + label = "bootloader"; }; -}; - -&ecspi4 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_ecspi4>; - fsl,spi-num-chipselects = <3>; - cs-gpios = <&gpio3 24 0>, <&gpio3 29 0>, <&gpio3 25 0>; - status = "okay"; - - flash: m25p80@0 { - compatible = "winbond,w25q16dw", "jedec,spi-nor"; - spi-max-frequency = <20000000>; - reg = <0>; - #address-cells = <1>; - #size-cells = <1>; - - partition@0 { - label = "bootloader"; - reg = <0x000000 0x0c0000>; - }; - - flash_bareboxenv: partition@c0000 { - label = "environment"; - reg = <0x0c0000 0x010000>; - }; - partition@d0000 { - label = "user"; - reg = <0x0d0000 0x130000>; - }; + flash_bareboxenv: partition@c0000 { + reg = <0x0c0000 0x010000>; + label = "environment"; }; -}; - -&i2c3 { - clock-frequency = <100000>; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_i2c3>; -}; - -&pcie { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_pcie>; - wake-up-gpio = <&gpio6 18 GPIO_ACTIVE_HIGH>; - reset-gpio = <&gpio3 13 GPIO_ACTIVE_HIGH>; -}; - -&uart1 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_uart1_smarc>; - fsl,uart-has-rtscts; -}; - -&uart2 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_uart2_smarc>; - status = "okay"; -}; -&uart4 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_uart4_smarc>; - fsl,uart-has-rtscts; -}; - -&uart5 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_uart5_smarc>; -}; - -&usbotg { - /* - * no 'imx6-usb-charger-detection' - * since USB_OTG_CHD_B pin is not wired - */ - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_usbotg>; - status = "okay"; -}; - -&usbh1 { - vbus-supply = <®_5v0_s0>; - status = "okay"; + partition@d0000 { + reg = <0x0d0000 0x130000>; + label = "user"; + }; }; &usdhc4 { - /* Internal eMMC, optional on some boards */ - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_usdhc4>; - bus-width = <8>; - no-1-8-v; - non-removable; - status = "okay"; #address-cells = <1>; #size-cells = <1>; @@ -352,158 +73,18 @@ }; &iomuxc { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_boot>; - - pinctrl_boot: boot { - fsl,pins = < - /* GPIOS for version and id detection */ - MX6QDL_PAD_NANDF_CLE__GPIO6_IO07 0x80000000 - MX6QDL_PAD_NANDF_WP_B__GPIO6_IO09 0x80000000 - MX6QDL_PAD_NANDF_D2__GPIO2_IO02 0x80000000 - >; - }; - - pinctrl_flexcan1: flexcan1-smarc { - fsl,pins = < - MX6QDL_PAD_GPIO_7__FLEXCAN1_TX 0x80000000 - MX6QDL_PAD_GPIO_8__FLEXCAN1_RX 0x80000000 - >; - }; - - pinctrl_flexcan2: flexcan2-smarc { - fsl,pins = < - MX6QDL_PAD_KEY_COL4__FLEXCAN2_TX 0x80000000 - MX6QDL_PAD_KEY_ROW4__FLEXCAN2_RX 0x80000000 - >; - }; - - pinctrl_enet_smarc: fecgrp-smarc { - fsl,pins = < - MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0 - MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0 - MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0 - MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 - MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 - MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 - MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 - MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 - MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 - MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 - MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 - MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 - MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 - MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 - MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 - MX6QDL_PAD_ENET_CRS_DV__GPIO1_IO25 0x80000000 - >; - }; - - pinctrl_i2c_gpio_0: i2c-gpio-0-smarc { - fsl,pins = < - /* SCL GPIO */ - MX6QDL_PAD_ENET_TXD0__GPIO1_IO30 0x80000000 - /* SDA GPIO */ - MX6QDL_PAD_ENET_TX_EN__GPIO1_IO28 0x80000000 - >; - }; - - pinctrl_i2c3: i2c3-smarc { - fsl,pins = < - MX6QDL_PAD_EIM_D17__I2C3_SCL 0x4001b8b1 - MX6QDL_PAD_EIM_D18__I2C3_SDA 0x4001b8b1 - >; - }; - - pinctrl_ecspi4: ecspi4-smarc { - fsl,pins = < - MX6QDL_PAD_EIM_D21__ECSPI4_SCLK 0x80000000 - MX6QDL_PAD_EIM_D28__ECSPI4_MOSI 0x80000000 - MX6QDL_PAD_EIM_D22__ECSPI4_MISO 0x80000000 - MX6QDL_PAD_EIM_D29__ECSPI4_SS0 0x80000000 - - /* In hardware, ECSPI4's SS0,SS1,SS3 are wired. - But spi-imx driver support only continuous - numbering, and only can use GPIOs (and not - ECSPI's hardware SS) for CS. So linux view - of CS numbers differs from hw view, and - pins are configured as GPIOs */ - - /* physical - CS2, in linux - CS0, either internal flash or SMARC CS0 */ - MX6QDL_PAD_EIM_D24__GPIO3_IO24 0x80000000 - /* physical - CS0, in linux - CS1, either SMARC CS0 or not-connected */ - MX6QDL_PAD_EIM_D29__GPIO3_IO29 0x80000000 - /* physical - CS3, in linux - CS2, SMARC CS1 */ - MX6QDL_PAD_EIM_D25__GPIO3_IO25 0x80000000 - >; - }; - - pinctrl_pcie: pcie-smarc { - fsl,pins = < - /* RST_PCIE_A# */ - MX6QDL_PAD_EIM_DA13__GPIO3_IO13 0x80000000 - /* PCIE_WAKE# */ - MX6QDL_PAD_SD3_DAT6__GPIO6_IO18 0x80000000 - >; - }; - - pinctrl_uart1_smarc: uart1grp-smarc { - fsl,pins = < - MX6QDL_PAD_CSI0_DAT11__UART1_RX_DATA 0x1b0b1 - MX6QDL_PAD_CSI0_DAT10__UART1_TX_DATA 0x1b0b1 - MX6QDL_PAD_EIM_D20__UART1_RTS_B 0x1b0b1 - MX6QDL_PAD_EIM_D19__UART1_CTS_B 0x1b0b1 - >; - }; - - pinctrl_uart2_smarc: uart2grp-smarc { - fsl,pins = < - MX6QDL_PAD_EIM_D27__UART2_RX_DATA 0x1b0b1 - MX6QDL_PAD_EIM_D26__UART2_TX_DATA 0x1b0b1 - >; - }; - - pinctrl_uart4_smarc: uart4grp-smarc { - fsl,pins = < - MX6QDL_PAD_CSI0_DAT13__UART4_RX_DATA 0x1b0b1 - MX6QDL_PAD_CSI0_DAT12__UART4_TX_DATA 0x1b0b1 - MX6QDL_PAD_CSI0_DAT16__UART4_RTS_B 0x1b0b1 - MX6QDL_PAD_CSI0_DAT17__UART4_CTS_B 0x1b0b1 - >; - }; - - pinctrl_uart5_smarc: uart5grp-smarc { - fsl,pins = < - MX6QDL_PAD_CSI0_DAT15__UART5_RX_DATA 0x1b0b1 - MX6QDL_PAD_CSI0_DAT14__UART5_TX_DATA 0x1b0b1 - >; - }; - - pinctrl_usbotg: usbotg-grp-smarc { + pinctrl_gpio2_hog: gpio2-hog { fsl,pins = < - MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x1f8b0 - /* TODO: Comment out power and OC gpio's for now, since - * these are not used by driver - */ - /* USB power */ - // MX6QDL_PAD_CSI0_PIXCLK__GPIO5_IO18 0x80000000 - /* USB OC */ - // MX6QDL_PAD_CSI0_DATA_EN__GPIO5_IO20 0x80000000 + /* GPIO for version detection */ + MX6QDL_PAD_NANDF_D2__GPIO2_IO02 0xb0b0 >; }; - pinctrl_usdhc4: usdhc4grp-smarc { + pinctrl_gpio6_hog: gpio6-hog { fsl,pins = < - MX6QDL_PAD_SD4_CLK__SD4_CLK 0x17059 - MX6QDL_PAD_SD4_CMD__SD4_CMD 0x17059 - MX6QDL_PAD_SD4_DAT0__SD4_DATA0 0x17059 - MX6QDL_PAD_SD4_DAT1__SD4_DATA1 0x17059 - MX6QDL_PAD_SD4_DAT2__SD4_DATA2 0x17059 - MX6QDL_PAD_SD4_DAT3__SD4_DATA3 0x17059 - MX6QDL_PAD_SD4_DAT4__SD4_DATA4 0x17059 - MX6QDL_PAD_SD4_DAT5__SD4_DATA5 0x17059 - MX6QDL_PAD_SD4_DAT6__SD4_DATA6 0x17059 - MX6QDL_PAD_SD4_DAT7__SD4_DATA7 0x17059 + /* GPIOs for ddr3 size detection */ + MX6QDL_PAD_NANDF_CLE__GPIO6_IO07 0xb0b0 + MX6QDL_PAD_NANDF_WP_B__GPIO6_IO09 0xb0b0 >; }; }; diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile index e45f758e9c..d94c846a13 100644 --- a/arch/arm/mach-imx/Makefile +++ b/arch/arm/mach-imx/Makefile @@ -26,4 +26,4 @@ obj-$(CONFIG_BAREBOX_UPDATE) += imx-bbu-internal.o obj-$(CONFIG_BAREBOX_UPDATE_IMX_EXTERNAL_NAND) += imx-bbu-external-nand.o obj-$(CONFIG_RESET_IMX_SRC) += src.o lwl-y += cpu_init.o -pbl-y += xload-spi.o xload-common.o xload-imx-nand.o +pbl-y += xload-spi.o xload-common.o xload-imx-nand.o xload-gpmi-nand.o diff --git a/arch/arm/mach-imx/cpu_init.c b/arch/arm/mach-imx/cpu_init.c index cc7a409e37..9d86353e22 100644 --- a/arch/arm/mach-imx/cpu_init.c +++ b/arch/arm/mach-imx/cpu_init.c @@ -20,6 +20,7 @@ #include <mach/generic.h> #include <mach/imx7-regs.h> #include <mach/imx8mq-regs.h> +#include <mach/imx8m-ccm-regs.h> #include <common.h> #include <io.h> #include <asm/syscounter.h> @@ -79,6 +80,9 @@ static void imx8m_cpu_lowlevel_init(void) void imx8mm_cpu_lowlevel_init(void) { + /* ungate system counter */ + imx8m_ccgr_clock_enable(IMX8M_CCM_CCGR_SCTR); + imx8m_cpu_lowlevel_init(); } diff --git a/arch/arm/mach-imx/imx8m.c b/arch/arm/mach-imx/imx8m.c index b9e01a1d18..350d203539 100644 --- a/arch/arm/mach-imx/imx8m.c +++ b/arch/arm/mach-imx/imx8m.c @@ -35,14 +35,14 @@ void imx8m_clock_set_target_val(int clock_id, u32 val) { - void *ccm = IOMEM(MX8M_CCM_BASE_ADDR); + void __iomem *ccm = IOMEM(MX8M_CCM_BASE_ADDR); writel(val, ccm + IMX8M_CCM_TARGET_ROOTn(clock_id)); } void imx8m_ccgr_clock_enable(int index) { - void *ccm = IOMEM(MX8M_CCM_BASE_ADDR); + void __iomem *ccm = IOMEM(MX8M_CCM_BASE_ADDR); writel(IMX8M_CCM_CCGR_SETTINGn_NEEDED(0), ccm + IMX8M_CCM_CCGRn_SET(index)); @@ -50,7 +50,7 @@ void imx8m_ccgr_clock_enable(int index) void imx8m_ccgr_clock_disable(int index) { - void *ccm = IOMEM(MX8M_CCM_BASE_ADDR); + void __iomem *ccm = IOMEM(MX8M_CCM_BASE_ADDR); writel(IMX8M_CCM_CCGR_SETTINGn_NEEDED(0), ccm + IMX8M_CCM_CCGRn_CLR(index)); diff --git a/arch/arm/mach-imx/include/mach/xload.h b/arch/arm/mach-imx/include/mach/xload.h index 94b2f37616..7187787f5b 100644 --- a/arch/arm/mach-imx/include/mach/xload.h +++ b/arch/arm/mach-imx/include/mach/xload.h @@ -5,6 +5,7 @@ int imx53_nand_start_image(void); int imx6_spi_load_image(int instance, unsigned int flash_offset, void *buf, int len); int imx6_spi_start_image(int instance); int imx6_esdhc_start_image(int instance); +int imx6_nand_start_image(void); int imx7_esdhc_start_image(int instance); int imx8m_esdhc_load_image(int instance, bool start); int imx8mp_esdhc_load_image(int instance, bool start); diff --git a/arch/arm/mach-imx/xload-gpmi-nand.c b/arch/arm/mach-imx/xload-gpmi-nand.c new file mode 100644 index 0000000000..b3fd479cb4 --- /dev/null +++ b/arch/arm/mach-imx/xload-gpmi-nand.c @@ -0,0 +1,1217 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#define pr_fmt(fmt) "xload-gpmi-nand: " fmt + +#include <common.h> +#include <stmp-device.h> +#include <asm-generic/io.h> +#include <linux/sizes.h> +#include <linux/mtd/nand.h> +#include <asm/cache.h> +#include <mach/xload.h> +#include <soc/imx/imx-nand-bcb.h> +#include <linux/mtd/rawnand.h> +#include <mach/imx6-regs.h> +#include <mach/clock-imx6.h> + +/* + * MXS DMA hardware command. + * + * This structure describes the in-memory layout of an entire DMA command, + * including space for the maximum number of PIO accesses. See the appropriate + * reference manual for a detailed description of what these fields mean to the + * DMA hardware. + */ +#define DMACMD_COMMAND_DMA_WRITE 0x1 +#define DMACMD_COMMAND_DMA_READ 0x2 +#define DMACMD_COMMAND_DMA_SENSE 0x3 +#define DMACMD_CHAIN (1 << 2) +#define DMACMD_IRQ (1 << 3) +#define DMACMD_NAND_LOCK (1 << 4) +#define DMACMD_NAND_WAIT_4_READY (1 << 5) +#define DMACMD_DEC_SEM (1 << 6) +#define DMACMD_WAIT4END (1 << 7) +#define DMACMD_HALT_ON_TERMINATE (1 << 8) +#define DMACMD_TERMINATE_FLUSH (1 << 9) +#define DMACMD_PIO_WORDS(words) ((words) << 12) +#define DMACMD_XFER_COUNT(x) ((x) << 16) + +struct mxs_dma_cmd { + unsigned long next; + unsigned long data; + unsigned long address; +#define APBH_DMA_PIO_WORDS 6 + unsigned long pio_words[APBH_DMA_PIO_WORDS]; +}; + +enum mxs_dma_id { + IMX23_DMA, + IMX28_DMA, +}; + +struct apbh_dma { + void __iomem *regs; + enum mxs_dma_id id; +}; + +struct mxs_dma_chan { + unsigned int flags; + int channel; + struct apbh_dma *apbh; +}; + +#define HW_APBHX_CTRL0 0x000 +#define BM_APBH_CTRL0_APB_BURST8_EN (1 << 29) +#define BM_APBH_CTRL0_APB_BURST_EN (1 << 28) +#define BP_APBH_CTRL0_CLKGATE_CHANNEL 8 +#define BP_APBH_CTRL0_RESET_CHANNEL 16 +#define HW_APBHX_CTRL1 0x010 +#define BP_APBHX_CTRL1_CH_CMDCMPLT_IRQ_EN 16 +#define HW_APBHX_CTRL2 0x020 +#define HW_APBHX_CHANNEL_CTRL 0x030 +#define BP_APBHX_CHANNEL_CTRL_RESET_CHANNEL 16 +#define BP_APBHX_VERSION_MAJOR 24 +#define HW_APBHX_CHn_NXTCMDAR_MX23(n) (0x050 + (n) * 0x70) +#define HW_APBHX_CHn_NXTCMDAR_MX28(n) (0x110 + (n) * 0x70) +#define HW_APBHX_CHn_SEMA_MX23(n) (0x080 + (n) * 0x70) +#define HW_APBHX_CHn_SEMA_MX28(n) (0x140 + (n) * 0x70) +#define NAND_ONFI_CRC_BASE 0x4f4e + +#define apbh_dma_is_imx23(aphb) ((apbh)->id == IMX23_DMA) + +/* udelay() is not available in PBL, need to improvise */ +static void __udelay(int us) +{ + volatile int i; + + for (i = 0; i < us * 4; i++); +} + +/* + * Enable a DMA channel. + * + * If the given channel has any DMA descriptors on its active list, this + * function causes the DMA hardware to begin processing them. + * + * This function marks the DMA channel as "busy," whether or not there are any + * descriptors to process. + */ +static int mxs_dma_enable(struct mxs_dma_chan *pchan, + struct mxs_dma_cmd *pdesc) +{ + struct apbh_dma *apbh = pchan->apbh; + int channel_bit; + int channel = pchan->channel; + + if (apbh_dma_is_imx23(apbh)) { + writel((uint32_t)pdesc, + apbh->regs + HW_APBHX_CHn_NXTCMDAR_MX23(channel)); + writel(1, apbh->regs + HW_APBHX_CHn_SEMA_MX23(channel)); + channel_bit = channel + BP_APBH_CTRL0_CLKGATE_CHANNEL; + } else { + writel((uint32_t)pdesc, + apbh->regs + HW_APBHX_CHn_NXTCMDAR_MX28(channel)); + writel(1, apbh->regs + HW_APBHX_CHn_SEMA_MX28(channel)); + channel_bit = channel; + } + + writel(1 << channel_bit, apbh->regs + + HW_APBHX_CTRL0 + STMP_OFFSET_REG_CLR); + + return 0; +} + +/* + * Resets the DMA channel hardware. + */ +static int mxs_dma_reset(struct mxs_dma_chan *pchan) +{ + struct apbh_dma *apbh = pchan->apbh; + + if (apbh_dma_is_imx23(apbh)) + writel(1 << (pchan->channel + BP_APBH_CTRL0_RESET_CHANNEL), + apbh->regs + HW_APBHX_CTRL0 + STMP_OFFSET_REG_SET); + else + writel(1 << (pchan->channel + + BP_APBHX_CHANNEL_CTRL_RESET_CHANNEL), + apbh->regs + HW_APBHX_CHANNEL_CTRL + + STMP_OFFSET_REG_SET); + + return 0; +} + +static int mxs_dma_wait_complete(struct mxs_dma_chan *pchan) +{ + struct apbh_dma *apbh = pchan->apbh; + int timeout = 1000000; + + while (1) { + if (readl(apbh->regs + HW_APBHX_CTRL1) & (1 << pchan->channel)) + return 0; + + if (!timeout--) + return -ETIMEDOUT; + } +} + +/* + * Execute the DMA channel + */ +static int mxs_dma_run(struct mxs_dma_chan *pchan, struct mxs_dma_cmd *pdesc, + int num) +{ + struct apbh_dma *apbh = pchan->apbh; + int i, ret; + + /* chain descriptors */ + for (i = 0; i < num - 1; i++) { + pdesc[i].next = (uint32_t)(&pdesc[i + 1]); + pdesc[i].data |= DMACMD_CHAIN; + } + + writel(1 << (pchan->channel + BP_APBHX_CTRL1_CH_CMDCMPLT_IRQ_EN), + apbh->regs + HW_APBHX_CTRL1 + STMP_OFFSET_REG_SET); + + ret = mxs_dma_enable(pchan, pdesc); + if (ret) { + pr_err("%s: Failed to enable dma channel: %d\n", + __func__, ret); + return ret; + } + + ret = mxs_dma_wait_complete(pchan); + if (ret) { + pr_err("%s: Failed to wait for completion: %d\n", + __func__, ret); + return ret; + } + + /* Shut the DMA channel down. */ + writel(1 << pchan->channel, apbh->regs + + HW_APBHX_CTRL1 + STMP_OFFSET_REG_CLR); + writel(1 << pchan->channel, apbh->regs + + HW_APBHX_CTRL2 + STMP_OFFSET_REG_CLR); + + mxs_dma_reset(pchan); + + writel(1 << (pchan->channel + BP_APBHX_CTRL1_CH_CMDCMPLT_IRQ_EN), + apbh->regs + HW_APBHX_CTRL1 + STMP_OFFSET_REG_CLR); + + return 0; +} + +/* ----------------------------- NAND driver part -------------------------- */ + +#define GPMI_CTRL0 0x00000000 +#define GPMI_CTRL0_RUN (1 << 29) +#define GPMI_CTRL0_DEV_IRQ_EN (1 << 28) +#define GPMI_CTRL0_UDMA (1 << 26) +#define GPMI_CTRL0_COMMAND_MODE_MASK (0x3 << 24) +#define GPMI_CTRL0_COMMAND_MODE_OFFSET 24 +#define GPMI_CTRL0_COMMAND_MODE_WRITE (0x0 << 24) +#define GPMI_CTRL0_COMMAND_MODE_READ (0x1 << 24) +#define GPMI_CTRL0_COMMAND_MODE_READ_AND_COMPARE (0x2 << 24) +#define GPMI_CTRL0_COMMAND_MODE_WAIT_FOR_READY (0x3 << 24) +#define GPMI_CTRL0_WORD_LENGTH (1 << 23) +#define GPMI_CTRL0_CS(cs) ((cs) << 20) +#define GPMI_CTRL0_ADDRESS_MASK (0x7 << 17) +#define GPMI_CTRL0_ADDRESS_OFFSET 17 +#define GPMI_CTRL0_ADDRESS_NAND_DATA (0x0 << 17) +#define GPMI_CTRL0_ADDRESS_NAND_CLE (0x1 << 17) +#define GPMI_CTRL0_ADDRESS_NAND_ALE (0x2 << 17) +#define GPMI_CTRL0_ADDRESS_INCREMENT (1 << 16) +#define GPMI_CTRL0_XFER_COUNT_MASK 0xffff +#define GPMI_CTRL0_XFER_COUNT_OFFSET 0 + +#define GPMI_ECCCTRL_ECC_CMD_DECODE (0x0 << 13) +#define GPMI_ECCCTRL_ENABLE_ECC (1 << 12) +#define GPMI_ECCCTRL_BUFFER_MASK_BCH_PAGE 0x1ff + +#define BCH_CTRL 0x00000000 +#define BCH_CTRL_COMPLETE_IRQ (1 << 0) + +#define MXS_NAND_DMA_DESCRIPTOR_COUNT 6 +#define MXS_NAND_CHUNK_DATA_CHUNK_SIZE 512 +#define MXS_NAND_METADATA_SIZE 10 +#define MXS_NAND_COMMAND_BUFFER_SIZE 128 + +struct mxs_nand_info { + void __iomem *io_base; + void __iomem *bch_base; + struct mxs_dma_chan *dma_channel; + int cs; + uint8_t *cmd_buf; + struct mxs_dma_cmd *desc; + struct fcb_block fcb; + int dbbt_num_entries; + u32 *dbbt; + struct nand_memory_organization organization; + unsigned long nand_size; +}; + +/** + * It was discovered that xloading barebox from NAND sometimes fails. Observed + * behaviour is similar to silicon errata ERR007117 for i.MX6. + * + * ERR007117 description: + * For raw NAND boot, ROM switches the source of enfc_clk_root from PLL2_PFD2 + * to PLL3. The root clock is required to be gated before switching the source + * clock. If the root clock is not gated, clock glitches might be passed to the + * divider that follows the clock mux, and the divider might behave + * unpredictably. This can cause the clock generation to fail and the chip will + * not boot successfully. + * + * Workaround solution for this errata: + * 1) gate all GPMI/BCH related clocks (CG15, G14, CG13, CG12 and CG6) + * 2) reconfigure clocks + * 3) ungate all GPMI/BCH related clocks + * + */ +static inline void imx6_errata_007117_enable(void) +{ + u32 reg; + + /* Gate (disable) the GPMI/BCH clocks in CCM_CCGR4 */ + reg = readl(MXC_CCM_CCGR4); + reg &= ~(0xFF003000); + writel(reg, MXC_CCM_CCGR4); + + /** + * Gate (disable) the enfc_clk_root before changing the enfc_clk_root + * source or dividers by clearing CCM_CCGR2[CG7] to 2'b00. This + * disables the iomux_ipt_clk_io_clk. + */ + reg = readl(MXC_CCM_CCGR2); + reg &= ~(0x3 << 14); + writel(reg, MXC_CCM_CCGR2); + + /* Configure CCM_CS2CDR for the new clock source configuration */ + reg = readl(MXC_CCM_CS2CDR); + reg &= ~(0x7FF0000); + writel(reg, MXC_CCM_CS2CDR); + reg |= 0xF0000; + writel(reg, MXC_CCM_CS2CDR); + + /** + * Enable enfc_clk_root by setting CCM_CCGR2[CG7] to 2'b11. This + * enables the iomux_ipt_clk_io_clk. + */ + reg = readl(MXC_CCM_CCGR2); + reg |= 0x3 << 14; + writel(reg, MXC_CCM_CCGR2); + + /* Ungate (enable) the GPMI/BCH clocks in CCM_CCGR4 */ + reg = readl(MXC_CCM_CCGR4); + reg |= 0xFF003000; + writel(reg, MXC_CCM_CCGR4); +} + +static uint32_t mxs_nand_aux_status_offset(void) +{ + return (MXS_NAND_METADATA_SIZE + 0x3) & ~0x3; +} + +static int mxs_nand_read_page(struct mxs_nand_info *info, int writesize, + int oobsize, int pagenum, void *databuf, int raw) +{ + void __iomem *bch_regs = info->bch_base; + unsigned column = 0; + struct mxs_dma_cmd *d; + int cmd_queue_len; + u8 *cmd_buf; + int ret; + uint8_t *status; + int i; + int timeout; + int descnum = 0; + int max_pagenum = info->nand_size / + info->organization.pagesize; + + memset(info->desc, 0, + sizeof(*info->desc) * MXS_NAND_DMA_DESCRIPTOR_COUNT); + + /* Compile DMA descriptor - read0 */ + cmd_buf = info->cmd_buf; + cmd_queue_len = 0; + d = &info->desc[descnum++]; + d->address = (dma_addr_t)(cmd_buf); + cmd_buf[cmd_queue_len++] = NAND_CMD_READ0; + cmd_buf[cmd_queue_len++] = column; + cmd_buf[cmd_queue_len++] = column >> 8; + cmd_buf[cmd_queue_len++] = pagenum; + cmd_buf[cmd_queue_len++] = pagenum >> 8; + + if ((max_pagenum - 1) >= SZ_64K) + cmd_buf[cmd_queue_len++] = pagenum >> 16; + + d->data = DMACMD_COMMAND_DMA_READ | + DMACMD_WAIT4END | + DMACMD_PIO_WORDS(1) | + DMACMD_XFER_COUNT(cmd_queue_len); + + d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_WRITE | + GPMI_CTRL0_WORD_LENGTH | + GPMI_CTRL0_CS(info->cs) | + GPMI_CTRL0_ADDRESS_NAND_CLE | + GPMI_CTRL0_ADDRESS_INCREMENT | + cmd_queue_len; + + /* Compile DMA descriptor - readstart */ + cmd_buf = &info->cmd_buf[8]; + cmd_queue_len = 0; + d = &info->desc[descnum++]; + d->address = (dma_addr_t)(cmd_buf); + + cmd_buf[cmd_queue_len++] = NAND_CMD_READSTART; + + d->data = DMACMD_COMMAND_DMA_READ | + DMACMD_WAIT4END | + DMACMD_PIO_WORDS(1) | + DMACMD_XFER_COUNT(cmd_queue_len); + + d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_WRITE | + GPMI_CTRL0_WORD_LENGTH | + GPMI_CTRL0_CS(info->cs) | + GPMI_CTRL0_ADDRESS_NAND_CLE | + GPMI_CTRL0_ADDRESS_INCREMENT | + cmd_queue_len; + + /* Compile DMA descriptor - wait for ready. */ + d = &info->desc[descnum++]; + d->data = DMACMD_CHAIN | + DMACMD_NAND_WAIT_4_READY | + DMACMD_WAIT4END | + DMACMD_PIO_WORDS(2); + + d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_WAIT_FOR_READY | + GPMI_CTRL0_WORD_LENGTH | + GPMI_CTRL0_CS(info->cs) | + GPMI_CTRL0_ADDRESS_NAND_DATA; + + if (raw) { + /* Compile DMA descriptor - read. */ + d = &info->desc[descnum++]; + d->data = DMACMD_WAIT4END | + DMACMD_PIO_WORDS(1) | + DMACMD_XFER_COUNT(writesize + oobsize) | + DMACMD_COMMAND_DMA_WRITE; + d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_READ | + GPMI_CTRL0_WORD_LENGTH | + GPMI_CTRL0_CS(info->cs) | + GPMI_CTRL0_ADDRESS_NAND_DATA | + (writesize + oobsize); + d->address = (dma_addr_t)databuf; + } else { + /* Compile DMA descriptor - enable the BCH block and read. */ + d = &info->desc[descnum++]; + d->data = DMACMD_WAIT4END | DMACMD_PIO_WORDS(6); + d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_READ | + GPMI_CTRL0_WORD_LENGTH | + GPMI_CTRL0_CS(info->cs) | + GPMI_CTRL0_ADDRESS_NAND_DATA | + (writesize + oobsize); + d->pio_words[1] = 0; + d->pio_words[2] = GPMI_ECCCTRL_ENABLE_ECC | + GPMI_ECCCTRL_ECC_CMD_DECODE | + GPMI_ECCCTRL_BUFFER_MASK_BCH_PAGE; + d->pio_words[3] = writesize + oobsize; + d->pio_words[4] = (dma_addr_t)databuf; + d->pio_words[5] = (dma_addr_t)(databuf + writesize); + + /* Compile DMA descriptor - disable the BCH block. */ + d = &info->desc[descnum++]; + d->data = DMACMD_NAND_WAIT_4_READY | + DMACMD_WAIT4END | + DMACMD_PIO_WORDS(3); + d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_WAIT_FOR_READY | + GPMI_CTRL0_WORD_LENGTH | + GPMI_CTRL0_CS(info->cs) | + GPMI_CTRL0_ADDRESS_NAND_DATA | + (writesize + oobsize); + } + + /* Compile DMA descriptor - de-assert the NAND lock and interrupt. */ + d = &info->desc[descnum++]; + d->data = DMACMD_IRQ | DMACMD_DEC_SEM; + + /* Execute the DMA chain. */ + ret = mxs_dma_run(info->dma_channel, info->desc, descnum); + if (ret) { + pr_err("DMA read error\n"); + goto err; + } + + if (raw) + return 0; + + timeout = 1000000; + + while (1) { + if (!timeout--) { + ret = -ETIMEDOUT; + goto err; + } + + if (readl(bch_regs + BCH_CTRL) & BCH_CTRL_COMPLETE_IRQ) + break; + } + + writel(BCH_CTRL_COMPLETE_IRQ, + bch_regs + BCH_CTRL + STMP_OFFSET_REG_CLR); + + /* Loop over status bytes, accumulating ECC status. */ + status = databuf + writesize + mxs_nand_aux_status_offset(); + for (i = 0; i < writesize / MXS_NAND_CHUNK_DATA_CHUNK_SIZE; i++) { + if (status[i] == 0xfe) { + ret = -EBADMSG; + goto err; + } + } + + ret = 0; +err: + return ret; +} + +static int mxs_nand_get_read_status(struct mxs_nand_info *info, void *databuf) +{ + int ret; + u8 *cmd_buf; + struct mxs_dma_cmd *d; + int descnum = 0; + int cmd_queue_len; + + memset(info->desc, 0, + sizeof(*info->desc) * MXS_NAND_DMA_DESCRIPTOR_COUNT); + + /* Compile DMA descriptor - READ STATUS */ + cmd_buf = info->cmd_buf; + cmd_queue_len = 0; + d = &info->desc[descnum++]; + d->address = (dma_addr_t)(cmd_buf); + cmd_buf[cmd_queue_len++] = NAND_CMD_STATUS; + + d->data = DMACMD_COMMAND_DMA_READ | + DMACMD_WAIT4END | + DMACMD_PIO_WORDS(1) | + DMACMD_XFER_COUNT(cmd_queue_len); + + d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_WRITE | + GPMI_CTRL0_WORD_LENGTH | + GPMI_CTRL0_CS(info->cs) | + GPMI_CTRL0_ADDRESS_NAND_CLE | + GPMI_CTRL0_ADDRESS_INCREMENT | + cmd_queue_len; + + /* Compile DMA descriptor - read. */ + d = &info->desc[descnum++]; + d->data = DMACMD_WAIT4END | + DMACMD_PIO_WORDS(1) | + DMACMD_XFER_COUNT(1) | + DMACMD_COMMAND_DMA_WRITE; + d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_READ | + GPMI_CTRL0_WORD_LENGTH | + GPMI_CTRL0_CS(info->cs) | + GPMI_CTRL0_ADDRESS_NAND_DATA | + (1); + d->address = (dma_addr_t)databuf; + + /* Compile DMA descriptor - de-assert the NAND lock and interrupt. */ + d = &info->desc[descnum++]; + d->data = DMACMD_IRQ | DMACMD_DEC_SEM; + + /* Execute the DMA chain. */ + ret = mxs_dma_run(info->dma_channel, info->desc, descnum); + if (ret) { + pr_err("DMA read error\n"); + return ret; + } + + return ret; +} + +static int mxs_nand_reset(struct mxs_nand_info *info, void *databuf) +{ + int ret, i; + u8 *cmd_buf; + struct mxs_dma_cmd *d; + int descnum = 0; + int cmd_queue_len; + u8 read_status; + + memset(info->desc, 0, + sizeof(*info->desc) * MXS_NAND_DMA_DESCRIPTOR_COUNT); + + /* Compile DMA descriptor - RESET */ + cmd_buf = info->cmd_buf; + cmd_queue_len = 0; + d = &info->desc[descnum++]; + d->address = (dma_addr_t)(cmd_buf); + cmd_buf[cmd_queue_len++] = NAND_CMD_RESET; + + d->data = DMACMD_COMMAND_DMA_READ | + DMACMD_WAIT4END | + DMACMD_PIO_WORDS(1) | + DMACMD_XFER_COUNT(cmd_queue_len); + + d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_WRITE | + GPMI_CTRL0_WORD_LENGTH | + GPMI_CTRL0_CS(info->cs) | + GPMI_CTRL0_ADDRESS_NAND_CLE | + GPMI_CTRL0_ADDRESS_INCREMENT | + cmd_queue_len; + + /* Compile DMA descriptor - de-assert the NAND lock and interrupt. */ + d = &info->desc[descnum++]; + d->data = DMACMD_IRQ | DMACMD_DEC_SEM; + + /* Execute the DMA chain. */ + ret = mxs_dma_run(info->dma_channel, info->desc, descnum); + if (ret) { + pr_err("DMA read error\n"); + return ret; + } + + /* Wait for NAND to wake up */ + for (i = 0; i < 10; i++) { + __udelay(50000); + ret = mxs_nand_get_read_status(info, databuf); + if (ret) + return ret; + memcpy(&read_status, databuf, 1); + if (read_status & NAND_STATUS_READY) + return 0; + } + + pr_warn("NAND Reset failed\n"); + return -1; +} + +/* function taken from nand_onfi.c */ +static u16 onfi_crc16(u16 crc, u8 const *p, size_t len) +{ + int i; + + while (len--) { + crc ^= *p++ << 8; + for (i = 0; i < 8; i++) + crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0); + } + return crc; +} + +static int mxs_nand_get_onfi(struct mxs_nand_info *info, void *databuf) +{ + int ret; + u8 *cmd_buf; + u16 crc; + struct mxs_dma_cmd *d; + int descnum = 0; + int cmd_queue_len; + struct nand_onfi_params nand_params; + + memset(info->desc, 0, + sizeof(*info->desc) * MXS_NAND_DMA_DESCRIPTOR_COUNT); + + /* Compile DMA descriptor - READ PARAMETER PAGE */ + cmd_buf = info->cmd_buf; + cmd_queue_len = 0; + d = &info->desc[descnum++]; + d->address = (dma_addr_t)(cmd_buf); + cmd_buf[cmd_queue_len++] = NAND_CMD_PARAM; + cmd_buf[cmd_queue_len++] = 0x00; + + d->data = DMACMD_COMMAND_DMA_READ | + DMACMD_WAIT4END | + DMACMD_PIO_WORDS(1) | + DMACMD_XFER_COUNT(cmd_queue_len); + + d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_WRITE | + GPMI_CTRL0_WORD_LENGTH | + GPMI_CTRL0_CS(info->cs) | + GPMI_CTRL0_ADDRESS_NAND_CLE | + GPMI_CTRL0_ADDRESS_INCREMENT | + cmd_queue_len; + + /* Compile DMA descriptor - wait for ready. */ + d = &info->desc[descnum++]; + d->data = DMACMD_CHAIN | + DMACMD_NAND_WAIT_4_READY | + DMACMD_WAIT4END | + DMACMD_PIO_WORDS(2); + + d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_WAIT_FOR_READY | + GPMI_CTRL0_WORD_LENGTH | + GPMI_CTRL0_CS(info->cs) | + GPMI_CTRL0_ADDRESS_NAND_DATA; + + /* Compile DMA descriptor - read. */ + d = &info->desc[descnum++]; + d->data = DMACMD_WAIT4END | + DMACMD_PIO_WORDS(1) | + DMACMD_XFER_COUNT(sizeof(struct nand_onfi_params)) | + DMACMD_COMMAND_DMA_WRITE; + d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_READ | + GPMI_CTRL0_WORD_LENGTH | + GPMI_CTRL0_CS(info->cs) | + GPMI_CTRL0_ADDRESS_NAND_DATA | + (sizeof(struct nand_onfi_params)); + d->address = (dma_addr_t)databuf; + + /* Compile DMA descriptor - de-assert the NAND lock and interrupt. */ + d = &info->desc[descnum++]; + d->data = DMACMD_IRQ | DMACMD_DEC_SEM; + + /* Execute the DMA chain. */ + ret = mxs_dma_run(info->dma_channel, info->desc, descnum); + if (ret) { + pr_err("DMA read error\n"); + return ret; + } + + memcpy(&nand_params, databuf, sizeof(struct nand_onfi_params)); + + crc = onfi_crc16(NAND_ONFI_CRC_BASE, (u8 *)&nand_params, 254); + pr_debug("ONFI CRC: 0x%x, CALC. CRC 0x%x\n", nand_params.crc, crc); + if (crc != le16_to_cpu(nand_params.crc)) { + pr_debug("ONFI CRC mismatch!\n"); + ret = -EUCLEAN; + return ret; + } + + /* Fill the NAND organization struct with data */ + info->organization.bits_per_cell = nand_params.bits_per_cell; + info->organization.pagesize = le32_to_cpu(nand_params.byte_per_page); + info->organization.oobsize = + le16_to_cpu(nand_params.spare_bytes_per_page); + info->organization.pages_per_eraseblock = + le32_to_cpu(nand_params.pages_per_block); + info->organization.eraseblocks_per_lun = + le32_to_cpu(nand_params.blocks_per_lun); + info->organization.max_bad_eraseblocks_per_lun = + le16_to_cpu(nand_params.bb_per_lun); + info->organization.luns_per_target = nand_params.lun_count; + info->nand_size = info->organization.pagesize * + info->organization.pages_per_eraseblock * + info->organization.eraseblocks_per_lun * + info->organization.luns_per_target; + + return ret; +} + +static int mxs_nand_check_onfi(struct mxs_nand_info *info, void *databuf) +{ + int ret; + u8 *cmd_buf; + struct mxs_dma_cmd *d; + int descnum = 0; + int cmd_queue_len; + + struct onfi_header { + u8 byte0; + u8 byte1; + u8 byte2; + u8 byte3; + } onfi_head; + + memset(info->desc, 0, + sizeof(*info->desc) * MXS_NAND_DMA_DESCRIPTOR_COUNT); + + /* Compile DMA descriptor - READID + 0x20 (ADDR) */ + cmd_buf = info->cmd_buf; + cmd_queue_len = 0; + d = &info->desc[descnum++]; + d->address = (dma_addr_t)(cmd_buf); + cmd_buf[cmd_queue_len++] = NAND_CMD_READID; + cmd_buf[cmd_queue_len++] = 0x20; + + d->data = DMACMD_COMMAND_DMA_READ | + DMACMD_WAIT4END | + DMACMD_PIO_WORDS(1) | + DMACMD_XFER_COUNT(cmd_queue_len); + + d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_WRITE | + GPMI_CTRL0_WORD_LENGTH | + GPMI_CTRL0_CS(info->cs) | + GPMI_CTRL0_ADDRESS_NAND_CLE | + GPMI_CTRL0_ADDRESS_INCREMENT | + cmd_queue_len; + + /* Compile DMA descriptor - read. */ + d = &info->desc[descnum++]; + d->data = DMACMD_WAIT4END | + DMACMD_PIO_WORDS(1) | + DMACMD_XFER_COUNT(sizeof(struct onfi_header)) | + DMACMD_COMMAND_DMA_WRITE; + d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_READ | + GPMI_CTRL0_WORD_LENGTH | + GPMI_CTRL0_CS(info->cs) | + GPMI_CTRL0_ADDRESS_NAND_DATA | + (sizeof(struct onfi_header)); + d->address = (dma_addr_t)databuf; + + /* Compile DMA descriptor - de-assert the NAND lock and interrupt. */ + d = &info->desc[descnum++]; + d->data = DMACMD_IRQ | DMACMD_DEC_SEM; + + /* Execute the DMA chain. */ + ret = mxs_dma_run(info->dma_channel, info->desc, descnum); + if (ret) { + pr_err("DMA read error\n"); + return ret; + } + + memcpy(&onfi_head, databuf, sizeof(struct onfi_header)); + + pr_debug("ONFI Byte0: 0x%x\n", onfi_head.byte0); + pr_debug("ONFI Byte1: 0x%x\n", onfi_head.byte1); + pr_debug("ONFI Byte2: 0x%x\n", onfi_head.byte2); + pr_debug("ONFI Byte3: 0x%x\n", onfi_head.byte3); + + /* check if returned values correspond to ascii characters "ONFI" */ + if (onfi_head.byte0 != 0x4f || onfi_head.byte1 != 0x4e || + onfi_head.byte2 != 0x46 || onfi_head.byte3 != 0x49) + return 1; + + return 0; +} + +static int mxs_nand_get_readid(struct mxs_nand_info *info, void *databuf) +{ + int ret; + u8 *cmd_buf; + struct mxs_dma_cmd *d; + int descnum = 0; + int cmd_queue_len; + + struct readid_data { + u8 byte0; + u8 byte1; + u8 byte2; + u8 byte3; + u8 byte4; + } id_data; + + memset(info->desc, 0, + sizeof(*info->desc) * MXS_NAND_DMA_DESCRIPTOR_COUNT); + + /* Compile DMA descriptor - READID */ + cmd_buf = info->cmd_buf; + cmd_queue_len = 0; + d = &info->desc[descnum++]; + d->address = (dma_addr_t)(cmd_buf); + cmd_buf[cmd_queue_len++] = NAND_CMD_READID; + cmd_buf[cmd_queue_len++] = 0x00; + + d->data = DMACMD_COMMAND_DMA_READ | + DMACMD_WAIT4END | + DMACMD_PIO_WORDS(1) | + DMACMD_XFER_COUNT(cmd_queue_len); + + d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_WRITE | + GPMI_CTRL0_WORD_LENGTH | + GPMI_CTRL0_CS(info->cs) | + GPMI_CTRL0_ADDRESS_NAND_CLE | + GPMI_CTRL0_ADDRESS_INCREMENT | + cmd_queue_len; + + /* Compile DMA descriptor - read. */ + d = &info->desc[descnum++]; + d->data = DMACMD_WAIT4END | + DMACMD_PIO_WORDS(1) | + DMACMD_XFER_COUNT(sizeof(struct readid_data)) | + DMACMD_COMMAND_DMA_WRITE; + d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_READ | + GPMI_CTRL0_WORD_LENGTH | + GPMI_CTRL0_CS(info->cs) | + GPMI_CTRL0_ADDRESS_NAND_DATA | + (sizeof(struct readid_data)); + d->address = (dma_addr_t)databuf; + + /* Compile DMA descriptor - de-assert the NAND lock and interrupt. */ + d = &info->desc[descnum++]; + d->data = DMACMD_IRQ | DMACMD_DEC_SEM; + + /* Execute the DMA chain. */ + ret = mxs_dma_run(info->dma_channel, info->desc, descnum); + if (ret) { + pr_err("DMA read error\n"); + return ret; + } + + memcpy(&id_data, databuf, sizeof(struct readid_data)); + + pr_debug("NAND Byte0: 0x%x\n", id_data.byte0); + pr_debug("NAND Byte1: 0x%x\n", id_data.byte1); + pr_debug("NAND Byte2: 0x%x\n", id_data.byte2); + pr_debug("NAND Byte3: 0x%x\n", id_data.byte3); + pr_debug("NAND Byte4: 0x%x\n", id_data.byte4); + + if (id_data.byte0 == 0xff || id_data.byte1 == 0xff || + id_data.byte2 == 0xff || id_data.byte3 == 0xff || + id_data.byte4 == 0xff) { + pr_err("\"READ ID\" returned 0xff, possible error!\n"); + return -EOVERFLOW; + } + + /* Fill the NAND organization struct with data */ + info->organization.bits_per_cell = + (1 << ((id_data.byte2 >> 2) & 0x3)) * 2; + info->organization.pagesize = + (1 << (id_data.byte3 & 0x3)) * SZ_1K; + info->organization.oobsize = id_data.byte3 & 0x4 ? + info->organization.pagesize / 512 * 16 : + info->organization.pagesize / 512 * 8; + info->organization.pages_per_eraseblock = + (1 << ((id_data.byte3 >> 4) & 0x3)) * SZ_64K / + info->organization.pagesize; + info->organization.planes_per_lun = + 1 << ((id_data.byte4 >> 2) & 0x3); + info->nand_size = info->organization.planes_per_lun * + (1 << ((id_data.byte4 >> 4) & 0x7)) * SZ_8M; + info->organization.eraseblocks_per_lun = info->nand_size / + (info->organization.pages_per_eraseblock * + info->organization.pagesize); + + return ret; +} + +static int mxs_nand_get_info(struct mxs_nand_info *info, void *databuf) +{ + int ret, i; + + ret = mxs_nand_check_onfi(info, databuf); + if (ret) { + if (ret != 1) + return ret; + pr_info("ONFI not supported, try \"READ ID\"...\n"); + } else { + /* + * Some NAND's don't return the correct data the first time + * "READ PARAMETER PAGE" is returned. Execute the command + * multimple times + */ + for (i = 0; i < 3; i++) { + /* + * Some NAND's need to be reset before "READ PARAMETER + * PAGE" can be successfully executed. + */ + ret = mxs_nand_reset(info, databuf); + if (ret) + return ret; + ret = mxs_nand_get_onfi(info, databuf); + if (ret) + pr_err("ONFI error: %d\n", ret); + else + break; + } + if (!ret) + goto succ; + } + + /* + * If ONFI is not supported or if it fails try to get NAND's info from + * "READ ID" command. + */ + ret = mxs_nand_reset(info, databuf); + if (ret) + return ret; + pr_debug("Trying \"READ ID\" command...\n"); + ret = mxs_nand_get_readid(info, databuf); + if (ret) { + pr_err("xloader supports only ONFI and generic \"READ ID\" " \ + "supported NANDs\n"); + return -1; + } +succ: + pr_debug("NAND page_size: %d\n", info->organization.pagesize); + pr_debug("NAND block_size: %d\n", + info->organization.pages_per_eraseblock + * info->organization.pagesize); + pr_debug("NAND oob_size: %d\n", info->organization.oobsize); + pr_debug("NAND nand_size: %lu\n", info->nand_size); + pr_debug("NAND bits_per_cell: %d\n", info->organization.bits_per_cell); + pr_debug("NAND planes_per_lun: %d\n", + info->organization.planes_per_lun); + pr_debug("NAND luns_per_target: %d\n", + info->organization.luns_per_target); + pr_debug("NAND eraseblocks_per_lun: %d\n", + info->organization.eraseblocks_per_lun); + pr_debug("NAND ntargets: %d\n", info->organization.ntargets); + + + return 0; +} + +/* ---------------------------- BCB handling part -------------------------- */ + +static uint32_t calc_chksum(void *buf, size_t size) +{ + u32 chksum = 0; + u8 *bp = buf; + size_t i; + + for (i = 0; i < size; i++) + chksum += bp[i]; + + return ~chksum; +} + +static int get_fcb(struct mxs_nand_info *info, void *databuf) +{ + int i, pagenum, ret; + uint32_t checksum; + struct fcb_block *fcb = &info->fcb; + + /* First page read fails, this shouldn't be necessary */ + mxs_nand_read_page(info, info->organization.pagesize, + info->organization.oobsize, 0, databuf, 1); + + for (i = 0; i < 4; i++) { + pagenum = info->organization.pages_per_eraseblock * i; + + ret = mxs_nand_read_page(info, info->organization.pagesize, + info->organization.oobsize, pagenum, databuf, 1); + if (ret) + continue; + + memcpy(fcb, databuf + mxs_nand_aux_status_offset(), + sizeof(*fcb)); + + if (fcb->FingerPrint != FCB_FINGERPRINT) { + pr_err("No FCB found on page %d\n", pagenum); + continue; + } + + checksum = calc_chksum((void *)fcb + + sizeof(uint32_t), sizeof(*fcb) - sizeof(uint32_t)); + + if (checksum != fcb->Checksum) { + pr_err("FCB on page %d has invalid checksum. " \ + "Expected: 0x%08x, calculated: 0x%08x", + pagenum, fcb->Checksum, checksum); + continue; + } + + pr_debug("Found FCB:\n"); + pr_debug("PageDataSize: 0x%08x\n", fcb->PageDataSize); + pr_debug("TotalPageSize: 0x%08x\n", fcb->TotalPageSize); + pr_debug("SectorsPerBlock: 0x%08x\n", fcb->SectorsPerBlock); + pr_debug("FW1_startingPage: 0x%08x\n", + fcb->Firmware1_startingPage); + pr_debug("PagesInFW1: 0x%08x\n", fcb->PagesInFirmware1); + pr_debug("FW2_startingPage: 0x%08x\n", + fcb->Firmware2_startingPage); + pr_debug("PagesInFW2: 0x%08x\n", fcb->PagesInFirmware2); + + return 0; + } + + return -EINVAL; +} + +static int get_dbbt(struct mxs_nand_info *info, void *databuf) +{ + int i, ret; + int page; + int startpage = info->fcb.DBBTSearchAreaStartAddress; + struct dbbt_block dbbt; + + for (i = 0; i < 4; i++) { + page = startpage + i * info->organization.pages_per_eraseblock; + + ret = mxs_nand_read_page(info, info->organization.pagesize, + info->organization.oobsize, page, databuf, 0); + if (ret) + continue; + + memcpy(&dbbt, databuf, sizeof(struct dbbt_block)); + + if (*(u32 *)(databuf + sizeof(u32)) != DBBT_FINGERPRINT) + continue; + + /* Version check */ + if (be32_to_cpup(databuf + 2 * sizeof(u32)) < 1) + return -ENOENT; + + ret = mxs_nand_read_page(info, info->organization.pagesize, + info->organization.oobsize, page + 4, databuf, 0); + if (ret) + continue; + + info->dbbt_num_entries = *(u32 *)(databuf + sizeof(u32)); + + pr_debug("Found DBBT with %d entries\n", + info->dbbt_num_entries); + pr_debug("Checksum = 0x%08x\n", dbbt.Checksum); + pr_debug("FingerPrint = 0x%08x\n", dbbt.FingerPrint); + pr_debug("Version = 0x%08x\n", dbbt.Version); + pr_debug("numberBB = 0x%08x\n", dbbt.numberBB); + pr_debug("DBBTNumOfPages= 0x%08x\n", dbbt.DBBTNumOfPages); + + for (i = 0; i < info->dbbt_num_entries; i++) + pr_debug("badblock %d at block %d\n", i, + *(u32 *)(databuf + (2 + i) * sizeof(u32))); + + info->dbbt = databuf + 2 * sizeof(u32); + + return 0; + } + + return -ENOENT; +} + +static int block_is_bad(struct mxs_nand_info *info, int blocknum) +{ + int i; + u32 *dbbt = info->dbbt; + + if (!dbbt) + return 0; + + for (i = 0; i < info->dbbt_num_entries; i++) { + if (dbbt[i] == blocknum) + return 1; + } + + return 0; +} + +static int read_firmware(struct mxs_nand_info *info, int startpage, + void *dest, int len) +{ + int curpage = startpage; + struct fcb_block *fcb = &info->fcb; + int pagesperblock = fcb->SectorsPerBlock; + int numpages = (len / fcb->PageDataSize) + 1; + int ret; + int pagesize = fcb->PageDataSize; + int oobsize = fcb->TotalPageSize - pagesize; + + pr_debug("Reading %d pages starting from page %d\n", + numpages, startpage); + + if (block_is_bad(info, curpage / pagesperblock)) + curpage = ALIGN_DOWN(curpage + pagesperblock, pagesperblock); + + while (numpages) { + if (!(curpage & (pagesperblock - 1))) { + /* Check for bad blocks on each block boundary */ + if (block_is_bad(info, curpage / pagesperblock)) { + pr_debug("Skipping bad block at page %d\n", + curpage); + curpage += pagesperblock; + continue; + } + } + + ret = mxs_nand_read_page(info, pagesize, oobsize, + curpage, dest, 0); + if (ret) { + pr_debug("Failed to read page %d\n", curpage); + return ret; + } + + *((u8 *)dest + fcb->BadBlockMarkerByte) = + *(u8 *)(dest + pagesize); + + numpages--; + dest += pagesize; + curpage++; + } + + return 0; +} + +static int __maybe_unused imx6_nand_load_image(void *cmdbuf, void *descs, + void *databuf, void *dest, int len) +{ + struct mxs_nand_info info = { + .io_base = (void *)0x00112000, + .bch_base = (void *)0x00114000, + }; + struct apbh_dma apbh = { + .id = IMX28_DMA, + .regs = (void *)0x00110000, + }; + struct mxs_dma_chan pchan = { + .channel = 0, /* MXS: MXS_DMA_CHANNEL_AHB_APBH_GPMI0 */ + .apbh = &apbh, + }; + int ret; + struct fcb_block *fcb; + + info.dma_channel = &pchan; + + pr_debug("cmdbuf: 0x%p descs: 0x%p databuf: 0x%p dest: 0x%p\n", + cmdbuf, descs, databuf, dest); + + /* Command buffers */ + info.cmd_buf = cmdbuf; + info.desc = descs; + + ret = mxs_nand_get_info(&info, databuf); + if (ret) + return ret; + + ret = get_fcb(&info, databuf); + if (ret) + return ret; + + fcb = &info.fcb; + + get_dbbt(&info, databuf); + + ret = read_firmware(&info, fcb->Firmware1_startingPage, dest, len); + if (ret) { + pr_err("Failed to read firmware1, trying firmware2\n"); + ret = read_firmware(&info, fcb->Firmware2_startingPage, + dest, len); + if (ret) { + pr_err("Failed to also read firmware2\n"); + return ret; + } + } + + return 0; +} + +int imx6_nand_start_image(void) +{ + int ret; + void *sdram = (void *)0x10000000; + void __noreturn (*bb)(void); + void *cmdbuf, *databuf, *descs; + + cmdbuf = sdram; + descs = sdram + MXS_NAND_COMMAND_BUFFER_SIZE; + databuf = descs + + sizeof(struct mxs_dma_cmd) * MXS_NAND_DMA_DESCRIPTOR_COUNT; + bb = (void *)PAGE_ALIGN((unsigned long)databuf + SZ_8K); + + /* Apply ERR007117 workaround */ + imx6_errata_007117_enable(); + + ret = imx6_nand_load_image(cmdbuf, descs, databuf, + bb, imx_image_size()); + if (ret) { + pr_err("Loading image failed: %d\n", ret); + return ret; + } + + pr_debug("Starting barebox image at 0x%p\n", bb); + + arm_early_mmu_cache_invalidate(); + barrier(); + + bb(); +} diff --git a/common/imx-bbu-nand-fcb.c b/common/imx-bbu-nand-fcb.c index f8c4578056..0e008c6bc2 100644 --- a/common/imx-bbu-nand-fcb.c +++ b/common/imx-bbu-nand-fcb.c @@ -25,6 +25,7 @@ #include <crc.h> #include <mach/generic.h> #include <mtd/mtd-peb.h> +#include <soc/imx/imx-nand-bcb.h> #ifdef CONFIG_ARCH_IMX6 #include <mach/imx6.h> @@ -39,74 +40,6 @@ static inline int fcb_is_bch_encoded(void) } #endif -struct dbbt_block { - uint32_t Checksum; - uint32_t FingerPrint; - uint32_t Version; - uint32_t numberBB; /* reserved on i.MX6 */ - uint32_t DBBTNumOfPages; -}; - -struct fcb_block { - uint32_t Checksum; /* First fingerprint in first byte */ - uint32_t FingerPrint; /* 2nd fingerprint at byte 4 */ - uint32_t Version; /* 3rd fingerprint at byte 8 */ - uint8_t DataSetup; - uint8_t DataHold; - uint8_t AddressSetup; - uint8_t DSAMPLE_TIME; - /* These are for application use only and not for ROM. */ - uint8_t NandTimingState; - uint8_t REA; - uint8_t RLOH; - uint8_t RHOH; - uint32_t PageDataSize; /* 2048 for 2K pages, 4096 for 4K pages */ - uint32_t TotalPageSize; /* 2112 for 2K pages, 4314 for 4K pages */ - uint32_t SectorsPerBlock; /* Number of 2K sections per block */ - uint32_t NumberOfNANDs; /* Total Number of NANDs - not used by ROM */ - uint32_t TotalInternalDie; /* Number of separate chips in this NAND */ - uint32_t CellType; /* MLC or SLC */ - uint32_t EccBlockNEccType; /* Type of ECC, can be one of BCH-0-20 */ - uint32_t EccBlock0Size; /* Number of bytes for Block0 - BCH */ - uint32_t EccBlockNSize; /* Block size in bytes for all blocks other than Block0 - BCH */ - uint32_t EccBlock0EccType; /* Ecc level for Block 0 - BCH */ - uint32_t MetadataBytes; /* Metadata size - BCH */ - uint32_t NumEccBlocksPerPage; /* Number of blocks per page for ROM use - BCH */ - uint32_t EccBlockNEccLevelSDK; /* Type of ECC, can be one of BCH-0-20 */ - uint32_t EccBlock0SizeSDK; /* Number of bytes for Block0 - BCH */ - uint32_t EccBlockNSizeSDK; /* Block size in bytes for all blocks other than Block0 - BCH */ - uint32_t EccBlock0EccLevelSDK; /* Ecc level for Block 0 - BCH */ - uint32_t NumEccBlocksPerPageSDK;/* Number of blocks per page for SDK use - BCH */ - uint32_t MetadataBytesSDK; /* Metadata size - BCH */ - uint32_t EraseThreshold; /* To set into BCH_MODE register */ - uint32_t BootPatch; /* 0 for normal boot and 1 to load patch starting next to FCB */ - uint32_t PatchSectors; /* Size of patch in sectors */ - uint32_t Firmware1_startingPage;/* Firmware image starts on this sector */ - uint32_t Firmware2_startingPage;/* Secondary FW Image starting Sector */ - uint32_t PagesInFirmware1; /* Number of sectors in firmware image */ - uint32_t PagesInFirmware2; /* Number of sector in secondary FW image */ - uint32_t DBBTSearchAreaStartAddress; /* Page address where dbbt search area begins */ - uint32_t BadBlockMarkerByte; /* Byte in page data that have manufacturer marked bad block marker, */ - /* this will be swapped with metadata[0] to complete page data. */ - uint32_t BadBlockMarkerStartBit;/* For BCH ECC sizes other than 8 and 16 the bad block marker does not */ - /* start at 0th bit of BadBlockMarkerByte. This field is used to get to */ - /* the start bit of bad block marker byte with in BadBlockMarkerByte */ - uint32_t BBMarkerPhysicalOffset;/* FCB value that gives byte offset for bad block marker on physical NAND page */ - uint32_t BCHType; - - uint32_t TMTiming2_ReadLatency; - uint32_t TMTiming2_PreambleDelay; - uint32_t TMTiming2_CEDelay; - uint32_t TMTiming2_PostambleDelay; - uint32_t TMTiming2_CmdAddPause; - uint32_t TMTiming2_DataPause; - uint32_t TMSpeed; - uint32_t TMTiming1_BusyTimeout; - - uint32_t DISBBM; /* the flag to enable (1)/disable(0) bi swap */ - uint32_t BBMarkerPhysicalOffsetInSpareData; /* The swap position of main area in spare area */ -}; - struct imx_nand_fcb_bbu_handler { struct bbu_handler handler; diff --git a/drivers/net/smc911x.c b/drivers/net/smc911x.c index ea7cea5f1b..1edc16ce44 100644 --- a/drivers/net/smc911x.c +++ b/drivers/net/smc911x.c @@ -541,7 +541,7 @@ static int smc911x_probe(struct device_d *dev) * forbidden while this bit isn't set. Try for 100ms */ ret = wait_on_timeout(100 * MSECOND, smc911x_reg_read(priv, PMT_CTRL) & PMT_CTRL_READY); - if (!ret) { + if (ret) { dev_err(dev, "Device not READY in 100ms aborting\n"); return -ENODEV; } diff --git a/include/soc/imx/imx-nand-bcb.h b/include/soc/imx/imx-nand-bcb.h new file mode 100644 index 0000000000..b60205bd59 --- /dev/null +++ b/include/soc/imx/imx-nand-bcb.h @@ -0,0 +1,80 @@ +#ifndef __MACH_IMX_NAND_BCB_H +#define __MACH_IMX_NAND_BCB_H + +#define FCB_FINGERPRINT 0x20424346 /* 'FCB' */ +#define FCB_VERSION_1 0x01000000 +#define FCB_FINGERPRINT_OFF 0x4 /* FCB fingerprint offset*/ + +#define DBBT_FINGERPRINT 0x54424244 /* 'DBBT' */ +#define DBBT_VERSION_1 0x01000000 +#define DBBT_FINGERPRINT_OFF 0x4 /* DBBT fingerprint offset*/ + +struct dbbt_block { + uint32_t Checksum; + uint32_t FingerPrint; + uint32_t Version; + uint32_t numberBB; /* reserved on i.MX6 */ + uint32_t DBBTNumOfPages; +}; + +struct fcb_block { + uint32_t Checksum; /* First fingerprint in first byte */ + uint32_t FingerPrint; /* 2nd fingerprint at byte 4 */ + uint32_t Version; /* 3rd fingerprint at byte 8 */ + uint8_t DataSetup; + uint8_t DataHold; + uint8_t AddressSetup; + uint8_t DSAMPLE_TIME; + /* These are for application use only and not for ROM. */ + uint8_t NandTimingState; + uint8_t REA; + uint8_t RLOH; + uint8_t RHOH; + uint32_t PageDataSize; /* 2048 for 2K pages, 4096 for 4K pages */ + uint32_t TotalPageSize; /* 2112 for 2K pages, 4314 for 4K pages */ + uint32_t SectorsPerBlock; /* Number of 2K sections per block */ + uint32_t NumberOfNANDs; /* Total Number of NANDs - not used by ROM */ + uint32_t TotalInternalDie; /* Number of separate chips in this NAND */ + uint32_t CellType; /* MLC or SLC */ + uint32_t EccBlockNEccType; /* Type of ECC, can be one of BCH-0-20 */ + uint32_t EccBlock0Size; /* Number of bytes for Block0 - BCH */ + uint32_t EccBlockNSize; /* Block size in bytes for all blocks other than Block0 - BCH */ + uint32_t EccBlock0EccType; /* Ecc level for Block 0 - BCH */ + uint32_t MetadataBytes; /* Metadata size - BCH */ + uint32_t NumEccBlocksPerPage; /* Number of blocks per page for ROM use - BCH */ + uint32_t EccBlockNEccLevelSDK; /* Type of ECC, can be one of BCH-0-20 */ + uint32_t EccBlock0SizeSDK; /* Number of bytes for Block0 - BCH */ + uint32_t EccBlockNSizeSDK; /* Block size in bytes for all blocks other than Block0 - BCH */ + uint32_t EccBlock0EccLevelSDK; /* Ecc level for Block 0 - BCH */ + uint32_t NumEccBlocksPerPageSDK;/* Number of blocks per page for SDK use - BCH */ + uint32_t MetadataBytesSDK; /* Metadata size - BCH */ + uint32_t EraseThreshold; /* To set into BCH_MODE register */ + uint32_t BootPatch; /* 0 for normal boot and 1 to load patch starting next to FCB */ + uint32_t PatchSectors; /* Size of patch in sectors */ + uint32_t Firmware1_startingPage;/* Firmware image starts on this sector */ + uint32_t Firmware2_startingPage;/* Secondary FW Image starting Sector */ + uint32_t PagesInFirmware1; /* Number of sectors in firmware image */ + uint32_t PagesInFirmware2; /* Number of sector in secondary FW image */ + uint32_t DBBTSearchAreaStartAddress; /* Page address where dbbt search area begins */ + uint32_t BadBlockMarkerByte; /* Byte in page data that have manufacturer marked bad block marker, */ + /* this will be swapped with metadata[0] to complete page data. */ + uint32_t BadBlockMarkerStartBit;/* For BCH ECC sizes other than 8 and 16 the bad block marker does not */ + /* start at 0th bit of BadBlockMarkerByte. This field is used to get to */ + /* the start bit of bad block marker byte with in BadBlockMarkerByte */ + uint32_t BBMarkerPhysicalOffset;/* FCB value that gives byte offset for bad block marker on physical NAND page */ + uint32_t BCHType; + + uint32_t TMTiming2_ReadLatency; + uint32_t TMTiming2_PreambleDelay; + uint32_t TMTiming2_CEDelay; + uint32_t TMTiming2_PostambleDelay; + uint32_t TMTiming2_CmdAddPause; + uint32_t TMTiming2_DataPause; + uint32_t TMSpeed; + uint32_t TMTiming1_BusyTimeout; + + uint32_t DISBBM; /* the flag to enable (1)/disable(0) bi swap */ + uint32_t BBMarkerPhysicalOffsetInSpareData; /* The swap position of main area in spare area */ +}; + +#endif /* __MACH_IMX_NAND_BCB_H */ |