summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm/boards/kontron-samx6i/mem.c54
-rw-r--r--arch/arm/boards/nxp-imx8mm-evk/lowlevel.c5
-rw-r--r--arch/arm/dts/imx6dl-samx6i.dts36
-rw-r--r--arch/arm/dts/imx6q-samx6i.dts36
-rw-r--r--arch/arm/dts/imx6qdl-phytec-pfla02.dtsi24
-rw-r--r--arch/arm/dts/imx6qdl-smarc-samx6i.dtsi471
-rw-r--r--arch/arm/mach-imx/Makefile2
-rw-r--r--arch/arm/mach-imx/cpu_init.c4
-rw-r--r--arch/arm/mach-imx/imx8m.c6
-rw-r--r--arch/arm/mach-imx/include/mach/xload.h1
-rw-r--r--arch/arm/mach-imx/xload-gpmi-nand.c1217
-rw-r--r--common/imx-bbu-nand-fcb.c69
-rw-r--r--drivers/net/smc911x.c2
-rw-r--r--include/soc/imx/imx-nand-bcb.h80
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 = <&reg_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 */