[PATCH] ARM: i.MX: Add support for Skov i.MX6 boards

Sascha Hauer s.hauer at pengutronix.de
Tue Apr 13 13:17:50 BST 2021


From: Juergen Borleis <jbe at pengutronix.de>

The Skov platform potentially consists of many variants of CPUs (i.MX6S/DL and
i.MX6D/Q), NAND memories (size differs from 256 MiB to 8 GiB) and populated
SDRAM (256 MiB up to 2 GiB). All these variants share one PCB.

Some bootstrap resistors are provided which decode the variant and which allow
for it's detection at runtime. This information is needed to choose the bootspec
entry with the matching devicetree to boot the system. Based upon this mechanism
usage of a single Barebox image and Linux root filesystem for all known board
variants is made possible.

Signed-off-by: Juergen Borleis <jbe at pengutronix.de>
Signed-off-by: Enrico Jorns <ejo at pengutronix.de>
Signed-off-by: Ulrich Ölmann <u.oelmann at pengutronix.de>
Signed-off-by: Søren Andersen <san at skov.dk>
Signed-off-by: Sam Ravnborg <srn at skov.dk>
---
 arch/arm/boards/Makefile                      |   1 +
 arch/arm/boards/skov-imx6/Makefile            |   3 +
 arch/arm/boards/skov-imx6/board.c             | 528 ++++++++++++++
 arch/arm/boards/skov-imx6/env/init/automount  |  14 +
 arch/arm/boards/skov-imx6/env/nv/boot.default |   1 +
 .../flash-header-mx6-skov-imx6.imxcfg         |   4 +
 arch/arm/boards/skov-imx6/lowlevel.c          | 664 ++++++++++++++++++
 arch/arm/dts/Makefile                         |   1 +
 arch/arm/dts/imx6dl-skov-imx6.dts             |  24 +
 arch/arm/dts/imx6q-skov-imx6.dts              |  28 +
 arch/arm/dts/imx6qdl-skov-imx6.dtsi           | 546 ++++++++++++++
 arch/arm/mach-imx/Kconfig                     |   6 +
 images/Makefile.imx                           |   2 +
 13 files changed, 1822 insertions(+)
 create mode 100644 arch/arm/boards/skov-imx6/Makefile
 create mode 100644 arch/arm/boards/skov-imx6/board.c
 create mode 100644 arch/arm/boards/skov-imx6/env/init/automount
 create mode 100644 arch/arm/boards/skov-imx6/env/nv/boot.default
 create mode 100644 arch/arm/boards/skov-imx6/flash-header-mx6-skov-imx6.imxcfg
 create mode 100644 arch/arm/boards/skov-imx6/lowlevel.c
 create mode 100644 arch/arm/dts/imx6dl-skov-imx6.dts
 create mode 100644 arch/arm/dts/imx6q-skov-imx6.dts
 create mode 100644 arch/arm/dts/imx6qdl-skov-imx6.dtsi

diff --git a/arch/arm/boards/Makefile b/arch/arm/boards/Makefile
index 9ccb75e27b..3ef4de7b62 100644
--- a/arch/arm/boards/Makefile
+++ b/arch/arm/boards/Makefile
@@ -114,6 +114,7 @@ obj-$(CONFIG_MACH_RPI_COMMON)			+= raspberry-pi/
 obj-$(CONFIG_MACH_SABRELITE)			+= freescale-mx6-sabrelite/
 obj-$(CONFIG_MACH_SABRESD)			+= freescale-mx6-sabresd/
 obj-$(CONFIG_MACH_AC_SXB)			+= ac-sxb/
+obj-$(CONFIG_MACH_SKOV_IMX6)			+= skov-imx6/
 obj-$(CONFIG_MACH_FREESCALE_IMX6SX_SABRESDB)	+= freescale-mx6sx-sabresdb/
 obj-$(CONFIG_MACH_SAMA5D27_GIANTBOARD)		+= sama5d27-giantboard/
 obj-$(CONFIG_MACH_SAMA5D27_SOM1)		+= sama5d27-som1/
diff --git a/arch/arm/boards/skov-imx6/Makefile b/arch/arm/boards/skov-imx6/Makefile
new file mode 100644
index 0000000000..a5e85bc1e1
--- /dev/null
+++ b/arch/arm/boards/skov-imx6/Makefile
@@ -0,0 +1,3 @@
+obj-y += board.o
+lwl-y += lowlevel.o
+obj-pbl-y += version.o
diff --git a/arch/arm/boards/skov-imx6/board.c b/arch/arm/boards/skov-imx6/board.c
new file mode 100644
index 0000000000..a58172b2b1
--- /dev/null
+++ b/arch/arm/boards/skov-imx6/board.c
@@ -0,0 +1,528 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define pr_fmt(fmt) "skov-imx6: " fmt
+
+#include <common.h>
+#include <init.h>
+#include <mach/bbu.h>
+#include <environment.h>
+#include <bootsource.h>
+#include <globalvar.h>
+#include <net.h>
+#include <of_gpio.h>
+#include <gpio.h>
+
+#include "version.h"
+
+static int eth_of_fixup_node(struct device_node *root, const char *node_path,
+			     const u8 *ethaddr)
+{
+	struct device_node *node;
+	int ret;
+
+	if (!is_valid_ether_addr(ethaddr)) {
+		unsigned char ethaddr_str[sizeof("xx:xx:xx:xx:xx:xx")];
+
+		ethaddr_to_string(ethaddr, ethaddr_str);
+		pr_err("The mac-address %s is invalid.\n", ethaddr_str);
+		return -EINVAL;
+	}
+
+	node = of_find_node_by_path_from(root, node_path);
+	if (!node) {
+		pr_err("Did not find node %s to fix up with stored mac-address.\n",
+		       node_path);
+		return -ENOENT;
+	}
+
+	ret = of_set_property(node, "mac-address", ethaddr, ETH_ALEN, 1);
+	if (ret)
+		pr_err("Setting mac-address property of %s failed with: %s.\n",
+		       node->full_name, strerror(-ret));
+
+	return ret;
+}
+
+static int eth_of_fixup_node_from_eth_device(struct device_node *root,
+					     const char *node_path,
+					     const char *ethname)
+{
+	struct eth_device *edev;
+
+	edev = eth_get_byname(ethname);
+	if (!edev) {
+		pr_err("Did not find eth device \"%s\" to copy mac-address from.\n", ethname);
+		return -ENOENT;
+	}
+
+	return eth_of_fixup_node(root, node_path, edev->ethaddr);
+}
+
+static int get_mac_address_from_env_variable(const char *env, u8 ethaddr[ETH_ALEN])
+{
+	const char *ethaddr_str;
+	int ret;
+
+	ethaddr_str = getenv(env);
+	if (!ethaddr_str) {
+		pr_err("State variable %s storing the mac-address not found.\n", env);
+		return -ENOENT;
+	}
+
+	ret = string_to_ethaddr(ethaddr_str, ethaddr);
+	if (ret < 0) {
+		pr_err("Could not convert \"%s\" in state variable %s into mac-address.\n",
+		       ethaddr_str, env);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int get_default_mac_address_from_state_node(const char *state_node_path,
+						   u8 ethaddr[ETH_ALEN])
+{
+	struct device_node *node;
+	int ret;
+
+	node = of_find_node_by_path(state_node_path);
+	if (!node) {
+		pr_err("Node %s defining the state variable not found.\n", state_node_path);
+		return -ENOENT;
+	}
+
+	ret = of_property_read_u8_array(node, "default", ethaddr, ETH_ALEN);
+	if (ret) {
+		pr_err("Node %s has no property \"default\" of proper type.\n", state_node_path);
+		return -ENOENT;
+	}
+
+	return 0;
+}
+
+static int eth2_of_fixup_node_individually(struct device_node *root,
+					   const char *node_path,
+					   const char *ethname,
+					   const char *env,
+					   const char *state_node_path)
+{
+	u8 env_ethaddr[ETH_ALEN], default_ethaddr[ETH_ALEN];
+	int ret;
+
+	ret = get_mac_address_from_env_variable(env, env_ethaddr);
+	if (ret)
+		goto copy_mac_from_eth0;
+
+	ret = get_default_mac_address_from_state_node(state_node_path, default_ethaddr);
+	if (ret)
+		goto copy_mac_from_eth0;
+
+	/*
+	 * As the default is bogus copy the MAC address from eth0 if
+	 * the state variable has not been set to a different variant
+	 */
+	if (memcmp(env_ethaddr, default_ethaddr, ETH_ALEN) == 0)
+		goto copy_mac_from_eth0;
+
+	return eth_of_fixup_node(root, node_path, env_ethaddr);
+
+copy_mac_from_eth0:
+	return eth_of_fixup_node_from_eth_device(root, node_path, ethname);
+}
+
+#define MAX_V_GPIO 8
+
+struct board_description {
+	const char *variant;
+	const char *revision;
+	const char *soc;
+	const char *dts_compatible;
+	const char *display;
+	unsigned flags;
+};
+
+#define SKOV_NEED_ENABLE_RMII	BIT(0)
+#define SKOV_DISPLAY_PARALLEL	BIT(1)
+#define SKOV_ENABLE_MMC_POWER	BIT(2)
+#define SKOV_DISPLAY_LVDS	BIT(3)
+
+static const struct board_description imx6_variants[] = {
+	[0] = {
+		.variant = "high performance",
+		.revision = "A",
+		.soc = "i.MX6Q",
+		.dts_compatible = "skov,imx6-imxq-revA",
+		.flags = SKOV_NEED_ENABLE_RMII | SKOV_DISPLAY_PARALLEL,
+	},
+	[1] = {
+		.variant = "low cost",
+		.revision = "A",
+		.soc = "i.MX6S",
+		.dts_compatible = "skov,imx6-imxdl-revA",
+		.flags = SKOV_NEED_ENABLE_RMII | SKOV_DISPLAY_PARALLEL,
+	},
+	[2] = {
+		.variant = "high performance",
+		.revision = "A",
+		.soc = "i.MX6Q",
+		.dts_compatible = "skov,imx6-imxq-revA",
+		.flags = SKOV_NEED_ENABLE_RMII | SKOV_DISPLAY_PARALLEL,
+	},
+	[4] = {
+		.variant = "low cost",
+		.revision = "A",
+		.soc = "i.MX6S",
+		.dts_compatible = "skov,imx6-imxdl-revA",
+		.flags = SKOV_NEED_ENABLE_RMII | SKOV_DISPLAY_PARALLEL,
+	},
+	[8] = {
+		.variant = "high performance",
+		.revision = "A",
+		.soc = "i.MX6Q",
+		.dts_compatible = "skov,imx6-imxq-revA",
+		.flags = SKOV_NEED_ENABLE_RMII | SKOV_DISPLAY_PARALLEL,
+	},
+	[9] = {
+		.variant = "minimum cost",
+		.revision = "B",
+		.soc = "i.MX6S",
+		.dts_compatible = "skov,imx6-imxdl-revB",
+		.flags = SKOV_DISPLAY_PARALLEL,
+	},
+	[10] = {
+		.variant = "low cost",
+		.revision = "B",
+		.soc = "i.MX6S",
+		.dts_compatible = "skov,imx6-imxdl-revB",
+		.flags = SKOV_DISPLAY_PARALLEL,
+	},
+	[11] = {
+		.variant = "high performance",
+		.revision = "B",
+		.soc = "i.MX6Q",
+		.dts_compatible = "skov,imx6-imxq-revB",
+		.flags = SKOV_DISPLAY_PARALLEL,
+	},
+	[12] = {
+		/* FIXME this one is a revision 'C' according to the schematics */
+		.variant = "max performance",
+		.revision = "B",
+		.soc = "i.MX6Q",
+		.dts_compatible = "skov,imx6q-skov-revc-lt2",
+		.display = "l2rt",
+		.flags = SKOV_DISPLAY_PARALLEL,
+	},
+	[13] = {
+		.variant = "low cost",
+		.revision = "C",
+		.soc = "i.MX6S",
+		.dts_compatible = "skov,imx6dl-skov-revc-lt2",
+		.display = "l2rt",
+		.flags = SKOV_ENABLE_MMC_POWER | SKOV_DISPLAY_PARALLEL,
+	},
+	[14] = {
+		.variant = "high performance",
+		.revision = "C",
+		.soc = "i.MX6Q",
+		.dts_compatible = "skov,imx6q-skov-revc-lt2",
+		.display = "l2rt",
+		.flags = SKOV_ENABLE_MMC_POWER | SKOV_DISPLAY_PARALLEL,
+	},
+	[15] = {
+		.variant = "middle performance",
+		.revision = "C",
+		.soc = "i.MX6S",
+		.dts_compatible = "skov,imx6dl-skov-revc-lt2",
+		.display = "l2rt",
+		.flags = SKOV_ENABLE_MMC_POWER | SKOV_DISPLAY_PARALLEL,
+	},
+	[16] = {
+		.variant = "Solo_R512M_F4G",
+		.revision = "C",
+		.soc = "i.MX6S",
+		.dts_compatible = "skov,imx6dl-skov-revc-lt2",
+		.display = "l2rt",
+		.flags = SKOV_ENABLE_MMC_POWER | SKOV_DISPLAY_PARALLEL,
+	},
+	[17] = {
+		.variant = "Quad_R2G_F8G",
+		.revision = "C",
+		.soc = "i.MX6Q",
+		.dts_compatible = "skov,imx6q-skov-revc-lt2",
+		.display = "l2rt",
+		.flags = SKOV_ENABLE_MMC_POWER | SKOV_DISPLAY_PARALLEL,
+	},
+	[18] = {
+		.variant = "QuadPlus_R4G_F16G",
+		.revision = "C",
+		.soc = "i.MX6Q+",
+		.dts_compatible = "skov,imx6q-skov-revc-lt2",
+		.display = "l2rt",
+		.flags = SKOV_ENABLE_MMC_POWER | SKOV_DISPLAY_PARALLEL,
+	},
+	[19] = {
+		.variant = "Solo_R512M_F2G",
+		.revision = "C",
+		.soc = "i.MX6S",
+		.dts_compatible = "skov,imx6dl-skov-revc-lt2",
+		.display = "l2rt",
+		.flags = SKOV_ENABLE_MMC_POWER | SKOV_DISPLAY_PARALLEL,
+	},
+	[20] = {
+		.variant = "Quad_R1G_F4G",
+		.revision = "C",
+		.soc = "i.MX6Q",
+		.dts_compatible = "skov,imx6q-skov-revc-lt2",
+		.display = "l2rt",
+		.flags = SKOV_ENABLE_MMC_POWER | SKOV_DISPLAY_PARALLEL,
+	},
+	[21] = {
+		.variant = "Solo_R512M_F2G",
+		.revision = "C",
+		.soc = "i.MX6S",
+		.dts_compatible = "skov,imx6dl-skov-revc-lt6",
+		.display = "l6whrt",
+		.flags = SKOV_ENABLE_MMC_POWER | SKOV_DISPLAY_PARALLEL,
+	},
+	[22] = {
+		.variant = "Quad_R1G_F4G",
+		.revision = "C",
+		.soc = "i.MX6Q",
+		.dts_compatible = "skov,imx6q-skov-revc-lt6",
+		.display = "l6whrt",
+		.flags = SKOV_ENABLE_MMC_POWER | SKOV_DISPLAY_PARALLEL,
+	},
+	[24] = {
+		.variant = "Quad_R1G_F4G",
+		.revision = "E",
+		.soc = "i.MX6Q",
+		.dts_compatible = "skov,imx6q-skov-reve-mi1010ait-1cp1",
+		.display = "mi1010ait-1cp1",
+		.flags = SKOV_ENABLE_MMC_POWER | SKOV_DISPLAY_LVDS,
+	},
+};
+
+static int skov_board_no = -1;
+
+static int skov_imx6_fixup(struct device_node *root, void *unused)
+{
+	int ret;
+	const char *val;
+	uint32_t brightness;
+	struct device_node *node;
+	struct device_node *chosen = of_create_node(root, "/chosen");
+
+	eth_of_fixup_node_from_eth_device(root,
+			"/mdio-gpio/ksz8873 at 3/ports/ports at 0", "eth0");
+	eth2_of_fixup_node_individually(root,
+		"/mdio-gpio/ksz8873 at 3/ports/ports at 1", "eth0",
+		"state.ethaddr.eth2", "/state/ethaddr/eth2");
+
+	switch (bootsource_get()) {
+	case BOOTSOURCE_MMC:
+		/* use default variant of state variable defined in devicetree */
+		brightness = 8;
+		break;
+	default:
+		val = getenv("state.display.brightness");
+		if (!val) {
+			pr_err("could not get default display brightness\n");
+			return 0;
+		}
+
+		brightness = simple_strtoul(val, NULL, 0);
+		break;
+	}
+
+	for_each_compatible_node_from(node, root, NULL, "pwm-backlight") {
+		ret = of_property_write_u32(node, "default-brightness-level", brightness);
+		if (ret)
+			pr_err("error %d while setting default-brightness-level property on node %s to %d\n",
+				ret, node->name, brightness);
+	}
+
+	of_property_write_u32(chosen, "skov,imx6-board-version", skov_board_no);
+	of_property_write_string(chosen, "skov,imx6-board-variant",
+				 imx6_variants[skov_board_no].variant);
+
+	return 0;
+}
+
+/*
+ * Some variants need tweaks to make them work
+ *
+ * Revision A has no backlight control, since revision B it is present (GPIO6/23)
+ * Revision A needs GPIO1/24 to be low to make network working
+ * Revision C can control the SD main power supply
+ */
+static void skov_init_board(const struct board_description *variant)
+{
+	struct device_node *np;
+	char *environment_path, *envdev;
+	int ret;
+
+	imx6_bbu_internal_spi_i2c_register_handler("spiflash", "/dev/m25p0.barebox",
+		BBU_HANDLER_FLAG_DEFAULT);
+
+	of_register_fixup(skov_imx6_fixup, NULL);
+
+	switch (bootsource_get()) {
+	case BOOTSOURCE_MMC:
+		environment_path = "/chosen/environment-sd";
+		envdev = "MMC";
+		break;
+	default:
+		environment_path = "/chosen/environment-spinor";
+		envdev = "SPI NOR flash";
+		break;
+	}
+
+	pr_notice("Using environment in %s\n", envdev);
+
+	ret = of_device_enable_path(environment_path);
+	if (ret < 0)
+		pr_warn("Failed to enable environment partition '%s' (%d)\n",
+			environment_path, ret);
+
+	if (variant->flags & SKOV_NEED_ENABLE_RMII) {
+		/*
+		 * MX6QDL_PAD_ENET_RX_ER__GPIO1_IO24 is a gpio which must be
+		 * low to enable the RMII from the switch point of view
+		 */
+		gpio_request(24, "must_be_low");
+		gpio_direction_output(24, 0);
+	}
+
+	/* SD card handling */
+	gpio_request(205, "mmc io supply");
+	gpio_direction_output(205, 0); /* select 3.3 V IO voltage */
+
+	if (variant->flags & SKOV_ENABLE_MMC_POWER) {
+		/*
+		 * keep in sync with devicetree's 'regulator-boot-on' setting for
+		 * this regulator
+		 */
+		gpio_request(200, "mmc power supply");
+		gpio_direction_output(200, 0); /* switch on */
+		mdelay(1);
+		gpio_direction_output(200, 1); /* switch on */
+	}
+
+	if (variant->flags & SKOV_DISPLAY_PARALLEL) {
+		np = of_find_compatible_node(NULL, NULL, "fsl,imx-parallel-display");
+		if (np)
+			of_device_enable_and_register(np);
+		else
+			pr_err("Cannot find \"fsl,imx-parallel-display\" node\n");
+	}
+
+	if (variant->flags & SKOV_DISPLAY_LVDS) {
+		np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-ldb");
+		if (np)
+			of_device_enable_and_register(np);
+		else
+			pr_err("Cannot find \"fsl,imx6q-ldb\" node\n");
+
+		/* ... as well as its channel 0 */
+		np = of_find_node_by_name(np, "lvds-channel at 0");
+		if (np)
+			of_device_enable(np);
+		else
+			pr_err("Cannot find \"lvds-channel at 0\" node\n");
+	}
+}
+
+static void fixup_machine_compatible(const char *compat)
+{
+	const char *curcompat;
+	struct device_node *root;
+	int cclen = 0, clen = strlen(compat) + 1;
+	void *buf;
+
+	root = of_get_root_node();
+	if (!root)
+		return;
+
+	curcompat = of_get_property(root, "compatible", &cclen);
+
+	buf = xzalloc(cclen + clen);
+
+	memcpy(buf, compat, clen);
+	memcpy(buf + clen, curcompat, cclen);
+
+	/*
+	 * Prepend the compatible from board entry to the machine compatible.
+	 * Used to match bootspec entries against it.
+	 */
+	of_set_property(root, "compatible", buf, cclen + clen, true);
+
+	free(buf);
+}
+
+static int skov_imx6_probe(struct device_d *dev)
+{
+	unsigned v = 0;
+	const struct board_description *variant;
+
+	v = skov_imx6_get_version();
+
+	if (v >= ARRAY_SIZE(imx6_variants)) {
+		dev_err(dev, "Invalid variant %u\n", v);
+		return -EINVAL;
+	}
+
+	variant = &imx6_variants[v];
+
+	if (!variant->variant) {
+		dev_err(dev, "Invalid variant %u\n", v);
+		return -EINVAL;
+	}
+
+	skov_board_no = v;
+
+	globalvar_add_simple_int("board.no", &skov_board_no, "%u");
+	globalvar_add_simple("board.variant", variant->variant);
+	globalvar_add_simple("board.revision",variant->revision);
+	globalvar_add_simple("board.soc", variant->soc);
+	globalvar_add_simple("board.dts", variant->dts_compatible);
+	globalvar_add_simple("board.display", variant->display ?: NULL);
+
+	fixup_machine_compatible(variant->dts_compatible);
+
+	skov_init_board(variant);
+
+	return 0;
+}
+
+static __maybe_unused struct of_device_id skov_version_ids[] = {
+	{
+		.compatible = "skov,imx6",
+	}, {
+		/* sentinel */
+	}
+};
+
+static struct driver_d skov_version_driver = {
+	.name = "skov-imx6",
+	.probe = skov_imx6_probe,
+	.of_compatible = DRV_OF_COMPAT(skov_version_ids),
+};
+coredevice_platform_driver(skov_version_driver);
+
+static void skov_imx6_devices_shutdown(void)
+{
+	const char *external;
+
+	if (skov_board_no < 0)
+		return;
+
+	external = getenv("state.display.external");
+	if (!external) {
+		pr_err("could not get state variable display.external\n");
+		return;
+	}
+
+	if (!strcmp(external, "0"))
+		setenv("backlight0.brightness", "0");
+}
+predevshutdown_exitcall(skov_imx6_devices_shutdown);
diff --git a/arch/arm/boards/skov-imx6/env/init/automount b/arch/arm/boards/skov-imx6/env/init/automount
new file mode 100644
index 0000000000..bc9ff48a0a
--- /dev/null
+++ b/arch/arm/boards/skov-imx6/env/init/automount
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+if [ "$1" = menu ]; then
+	init-menu-add-entry "$0" "Automountpoints"
+	exit
+fi
+
+# development support
+mkdir -p /mnt/tftp
+automount /mnt/tftp 'ifup eth0 && mount -t tftp $eth0.serverip /mnt/tftp'
+
+# regular SD card based boot procedure
+mkdir -p /mnt/sd
+automount -d /mnt/sd 'mmc2.probe=1 && mount -t ext4 /dev/mmc2.0 /mnt/sd'
diff --git a/arch/arm/boards/skov-imx6/env/nv/boot.default b/arch/arm/boards/skov-imx6/env/nv/boot.default
new file mode 100644
index 0000000000..fe1a363da1
--- /dev/null
+++ b/arch/arm/boards/skov-imx6/env/nv/boot.default
@@ -0,0 +1 @@
+bootchooser
diff --git a/arch/arm/boards/skov-imx6/flash-header-mx6-skov-imx6.imxcfg b/arch/arm/boards/skov-imx6/flash-header-mx6-skov-imx6.imxcfg
new file mode 100644
index 0000000000..4bb615ebb0
--- /dev/null
+++ b/arch/arm/boards/skov-imx6/flash-header-mx6-skov-imx6.imxcfg
@@ -0,0 +1,4 @@
+soc imx6
+loadaddr 0x00907000
+max_load_size 0x11000
+ivtofs 0x400
diff --git a/arch/arm/boards/skov-imx6/lowlevel.c b/arch/arm/boards/skov-imx6/lowlevel.c
new file mode 100644
index 0000000000..eab797faa1
--- /dev/null
+++ b/arch/arm/boards/skov-imx6/lowlevel.c
@@ -0,0 +1,664 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define pr_fmt(fmt) "skov-imx6: " fmt
+
+#include <common.h>
+#include <mach/generic.h>
+#include <asm/barebox-arm-head.h>
+#include <asm/barebox-arm.h>
+#include <debug_ll.h>
+#include <io.h>
+#include <mach/imx6-mmdc.h>
+#include <mach/imx6-ddr-regs.h>
+#include <mach/imx6.h>
+#include <mach/xload.h>
+#include <mach/esdctl.h>
+#include <serial/imx-uart.h>
+#include <mach/iomux-mx6.h>
+#include <mach/imx-gpio.h>
+#include "version.h"
+
+static void __udelay(int us)
+{
+	volatile int i;
+
+	for (i = 0; i < us * 4; i++);
+}
+
+/* ------------------------------------------------------------------------ */
+
+/*
+ * Micron MT41K512M16HA-125 IT:E ->  8 GBit = 64 Meg x 16 x 8 banks
+ *
+ * Speed Grade   Data Rate (MT/s)  tRCD-tRP-CL   tRCD(ns)  tRP(ns)  CL(ns)
+ *    -125          1600            11-11-11      13.75     13.75   13.75
+ *               (=800 MHz)
+ *
+ * Memory configuration used by variant:
+ * - "Max Performance", 64 bit data bus, 1066 MHz, 4 GiB memory
+ */
+static const struct mx6_ddr3_cfg skov_imx6_cfg_4x512Mb_1066MHz = {
+	.mem_speed = 1066,
+	.density = 8, /* GiBit */
+	.width = 16, /* 16 bit data per device */
+	.banks = 8,
+	.rowaddr = 16, /* 64 k */
+	.coladdr = 10, /* 1 k */
+	.pagesz = 2, /* [kiB] */
+	.trcd = 1375, /* 13.75 ns = 11 clocks @ 1.6 GHz */
+	.trcmin = 4875, /* 48.75 ns = 39 clocks @ 1.6 GHz */
+	.trasmin = 3500, /* 35 ns = 28 clocks @ 1.6 GHz */
+	.SRT = 0,
+};
+
+static const struct mx6_ddr_sysinfo skov_imx6_sysinfo_4x512Mb_1066MHz = {
+	.dsize = 2, /* 64 bit wide = 4 devices, 16 bit each */
+	.cs_density = 32, /* four 8 GBit devices connected */
+	.ncs = 1, /* one CS line for all devices */
+	.cs1_mirror = 1, /* TODO */
+	.bi_on = 1, /* TODO */
+	.rtt_nom = 1, /* MX6_MMDC_P0_MPODTCTRL -> 0x00022227 */
+	.rtt_wr = 0, /* is LW_EN is 0 in their code */
+	.ralat = 5, /* TODO */
+	.walat = 1, /* TODO */
+	.mif3_mode = 3, /* TODO */
+	.rst_to_cke = 0x23, /* used in their code as well */
+	.sde_to_rst = 0x10, /* used in their code as well */
+	.pd_fast_exit = 0, /* TODO */
+};
+
+static const struct mx6_mmdc_calibration skov_imx6_calib_4x512Mb_1066MHz = {
+	.p0_mpwldectrl0 = 0x001a0017,
+	.p0_mpwldectrl1 = 0x001F001F,
+	.p0_mpdgctrl0 = 0x43040319,
+	.p0_mpdgctrl1 = 0x03040279,
+	.p0_mprddlctl = 0x4d434248,
+	.p0_mpwrdlctl = 0x34424543,
+
+	.p1_mpwldectrl0 = 0x00170027,
+	.p1_mpwldectrl1 = 0x000a001f,
+	.p1_mpdgctrl0 = 0x43040321,
+	.p1_mpdgctrl1 = 0x03030251,
+	.p1_mprddlctl = 0x42413c4d,
+	.p1_mpwrdlctl = 0x49324933,
+};
+
+/* ------------------------------------------------------------------------ */
+
+/*
+ * Micron MT41K256M16HA-125 IT:E ->  4 GBit = 32 Meg x 16 x 8 banks
+ *
+ * Speed Grade   Data Rate (MT/s)  tRCD-tRP-CL   tRCD(ns)  tRP(ns)  CL(ns)
+ *    -125          1600            11-11-11      13.75     13.75   13.75
+ *               (=800 MHz)
+ *
+ * Memory configuration used by variant:
+ * - "Max Performance", 64 bit data bus, 1066 MHz, 2 GiB memory
+ */
+static const struct mx6_ddr3_cfg skov_imx6_cfg_4x256Mb_1066MHz = {
+	.mem_speed = 1066,
+	.density = 4, /* GiBit */
+	.width = 16, /* 16 bit data per device */
+	.banks = 8,
+	.rowaddr = 15, /* 32 k */
+	.coladdr = 10, /* 1 k */
+	.pagesz = 2, /* [kiB] */
+	.trcd = 1375, /* 13.75 ns = 11 clocks @ 1.6 GHz */
+	.trcmin = 4875, /* 48.75 ns = 39 clocks @ 1.6 GHz */
+	.trasmin = 3500, /* 35 ns = 28 clocks @ 1.6 GHz */
+	.SRT = 0,
+};
+
+static const struct mx6_ddr_sysinfo skov_imx6_sysinfo_4x256Mb_1066MHz = {
+	.dsize = 2, /* 64 bit wide = 4 devices, 16 bit each */
+	.cs_density = 16, /* four 4 GBit devices connected */
+	.ncs = 1, /* one CS line for all devices */
+	.cs1_mirror = 1, /* TODO */
+	.bi_on = 1, /* TODO */
+	.rtt_nom = 1, /* MX6_MMDC_P0_MPODTCTRL -> 0x00022227 */
+	.rtt_wr = 0, /* is LW_EN is 0 in their code */
+	.ralat = 5, /* TODO */
+	.walat = 1, /* TODO */
+	.mif3_mode = 3, /* TODO */
+	.rst_to_cke = 0x23, /* used in their code as well */
+	.sde_to_rst = 0x10, /* used in their code as well */
+	.pd_fast_exit = 0, /* TODO */
+};
+
+static const struct mx6_mmdc_calibration skov_imx6_calib_4x256Mb_1066MHz = {
+	.p0_mpwldectrl0 = 0x001a0017,
+	.p0_mpwldectrl1 = 0x001F001F,
+	.p0_mpdgctrl0 = 0x43040319,
+	.p0_mpdgctrl1 = 0x03040279,
+	.p0_mprddlctl = 0x4d434248,
+	.p0_mpwrdlctl = 0x34424543,
+
+	.p1_mpwldectrl0 = 0x00170027,
+	.p1_mpwldectrl1 = 0x000a001f,
+	.p1_mpdgctrl0 = 0x43040321,
+	.p1_mpdgctrl1 = 0x03030251,
+	.p1_mprddlctl = 0x42413c4d,
+	.p1_mpwrdlctl = 0x49324933,
+};
+
+/* ------------------------------------------------------------------------ */
+
+/*
+ * Micron MT41K128M16JT-125 IT:K ->  2 GBit = 16 Meg x 16 x 8 banks
+ *
+ * Speed Grade   Data Rate (MT/s)  tRCD-tRP-CL   tRCD(ns)  tRP(ns)  CL(ns)
+ *    -125 ¹²       1600            11-11-11      13.75     13.75   13.75
+ *               (=800 MHz)
+ *
+ * ¹ Backward compatible to 1066 (=533 MHz), CL = 7
+ * ² Backward compatible to 1333 (=667 MHz), CL = 9
+ *
+ * Memory configuration used by variant
+ * - "High Performance", 64 bit data bus, 1066 MHz, 1 GiB memory
+ */
+static const struct mx6_ddr3_cfg skov_imx6_cfg_4x128Mb_1066MHz = {
+	.mem_speed = 1066,
+	.density = 2, /* GiBit */
+	.width = 16, /* 16 bit data per device */
+	.banks = 8,
+	.rowaddr = 14, /* 16 k */
+	.coladdr = 10, /* 1 k */
+	.pagesz = 2, /* [kiB] */
+	.trcd = 1375, /* 13.75 ns = 11 clocks @ 1.6 GHz */
+	.trcmin = 4875, /* 48.75 ns = 39 clocks @ 1.6 GHz */
+	.trasmin = 3500, /* 35 ns = 28 clocks @ 1.6 GHz */
+	.SRT = 0,
+};
+
+static const struct mx6_ddr_sysinfo skov_imx6_sysinfo_4x128Mb_1066MHz = {
+	.dsize = 2, /* 64 bit wide = 4 devices, 16 bit each */
+	.cs_density = 8, /* four 2 GBit devices connected */
+	.ncs = 1, /* one CS line for all devices */
+	.cs1_mirror = 1, /* TODO */
+	.bi_on = 1, /* TODO */
+	.rtt_nom = 1, /* MX6_MMDC_P0_MPODTCTRL -> 0x00022227 */
+	.rtt_wr = 0, /* is LW_EN is 0 in their code */
+	.ralat = 5, /* TODO */
+	.walat = 1, /* TODO */
+	.mif3_mode = 3, /* TODO */
+	.rst_to_cke = 0x23, /* used in their code as well */
+	.sde_to_rst = 0x10, /* used in their code as well */
+	.pd_fast_exit = 0, /* TODO */
+};
+
+/* calibration info for the "max performance" and "high performance" */
+static const struct mx6_mmdc_calibration skov_imx6_calib_4x128Mb_1066MHz = {
+	.p0_mpwldectrl0 = 0x0011000E,
+	.p0_mpwldectrl1 = 0x000E001B,
+	.p0_mpdgctrl0 = 0x42720306,
+	.p0_mpdgctrl1 = 0x026F0266,
+	.p0_mprddlctl = 0x45393B3E,
+	.p0_mpwrdlctl = 0x40434541,
+
+	.p1_mpwldectrl0 = 0x00190015,
+	.p1_mpwldectrl1 = 0x00070018,
+	.p1_mpdgctrl0 = 0x4273030A,
+	.p1_mpdgctrl1 = 0x02740240,
+	.p1_mprddlctl = 0x403A3747,
+	.p1_mpwrdlctl = 0x473E4A3B,
+};
+
+/* ------------------------------------------------------------------------ */
+
+static struct mx6dq_iomux_ddr_regs ddr_iomux_q = {
+	.dram_sdqs0 = 0x00000030,
+	.dram_sdqs1 = 0x00000030,
+	.dram_sdqs2 = 0x00000030,
+	.dram_sdqs3 = 0x00000030,
+	.dram_sdqs4 = 0x00000030,
+	.dram_sdqs5 = 0x00000030,
+	.dram_sdqs6 = 0x00000030,
+	.dram_sdqs7 = 0x00000030,
+	.dram_dqm0 = 0x00020030,
+	.dram_dqm1 = 0x00020030,
+	.dram_dqm2 = 0x00020030,
+	.dram_dqm3 = 0x00020030,
+	.dram_dqm4 = 0x00020030,
+	.dram_dqm5 = 0x00020030,
+	.dram_dqm6 = 0x00020030,
+	.dram_dqm7 = 0x00020030,
+	.dram_cas = 0x00020030,
+	.dram_ras = 0x00020030,
+	.dram_sdclk_0 = 0x00020030,
+	.dram_sdclk_1 = 0x00020030,
+	.dram_sdcke0 = 0x00003000,
+	.dram_sdcke1 = 0x00003000,
+	.dram_reset = 0x00020030,
+	.dram_sdba2 = 0x00000000,
+	.dram_sdodt0 = 0x00003030,
+	.dram_sdodt1 = 0x00003030,
+};
+
+static struct mx6dq_iomux_grp_regs grp_iomux_q = {
+	.grp_b0ds = 0x00000030,
+	.grp_b1ds = 0x00000030,
+	.grp_b2ds = 0x00000030,
+	.grp_b3ds = 0x00000030,
+	.grp_b4ds = 0x00000030,
+	.grp_b5ds = 0x00000030,
+	.grp_b6ds = 0x00000030,
+	.grp_b7ds = 0x00000030,
+	.grp_addds = 0x00000030,
+	.grp_ddrmode_ctl = 0x00020000,
+	.grp_ddrpke = 0x00000000,
+	.grp_ddrmode = 0x00020000,
+	.grp_ctlds = 0x00000030,
+	.grp_ddr_type = 0x000C0000,
+};
+
+static void spl_imx6q_dram_init(const struct mx6_ddr_sysinfo *si,
+				const struct mx6_mmdc_calibration *cb,
+				const struct mx6_ddr3_cfg *cfg)
+{
+	mx6dq_dram_iocfg(64, &ddr_iomux_q, &grp_iomux_q);
+	mx6_dram_cfg(si, cb, cfg);
+	__udelay(100);
+}
+
+/* ------------------------------------------------------------------------ */
+/*
+ * Device Information: Varies per DDR3 part number and speed grade
+ * Note: this SDRAM type is used on the "Low Cost" variant
+ *
+ * Micron MT41K128M16JT-125 IT:K ->  2 GBit = 16 Meg x 16 x 8 banks
+ *
+ * Speed Grade   Data Rate (MT/s)  tRCD-tRP-CL   tRCD(ns)  tRP(ns)  CL(ns)
+ *    -125 ¹²       1600            11-11-11      13.75     13.75   13.75
+ *               (=800 MHz)
+ *
+ * ¹ Backward compatible to 1066 (=533 MHz), CL = 7
+ * ² Backward compatible to 1333 (=667 MHz), CL = 9
+ *
+ * Memory configuration used by variant
+ * - "Low Cost", 32 bit data bus, 800 MHz, 512 MiB memory
+ */
+static const struct mx6_ddr3_cfg skov_imx6_cfg_2x128Mb_800MHz = {
+	.mem_speed = 800,
+	.density = 2, /* GiBit */
+	.width = 16, /* 16 bit data per device */
+	.banks = 8,
+	.rowaddr = 14, /* 16 k */
+	.coladdr = 10, /* 1 k */
+	.pagesz = 2, /* [kiB] */
+	.trcd = 1375, /* 13.75 ns = 11 clocks @ 1.6 GHz */
+	.trcmin = 4875, /* 48.75 ns = 39 clocks @ 1.6 GHz */
+	.trasmin = 3500, /* 35 ns = 28 clocks @ 1.6 GHz */
+	.SRT = 0,
+};
+
+static const struct mx6_ddr_sysinfo skov_imx6_sysinfo_2x128Mb_800MHz = {
+	.dsize = 1, /* 32 bit wide = 2 devices, 16 bit each */
+	.cs_density = 4, /* two 2 GBit devices connected */
+	.ncs = 1, /* one CS line for all devices */
+	.cs1_mirror = 1, /* TODO */
+	.bi_on = 1, /* TODO */
+	.rtt_nom = 1, /* MX6_MMDC_P0_MPODTCTRL -> 0x00022227 */
+	.rtt_wr = 0, /* is LW_EN is 0 in their code */
+	.ralat = 5, /* TODO */
+	.walat = 1, /* TODO */
+	.mif3_mode = 3, /* TODO */
+	.rst_to_cke = 0x23, /* used in their code as well */
+	.sde_to_rst = 0x10, /* used in their code as well */
+	.pd_fast_exit = 0, /* TODO */
+};
+
+static const struct mx6_mmdc_calibration skov_imx6_calib_2x128Mb_800MHz = {
+	.p0_mpwldectrl0 = 0x0040003C,
+	.p0_mpwldectrl1 = 0x0032003E,
+	.p0_mpdgctrl0 = 0x42350231,
+	.p0_mpdgctrl1 = 0x021A0218,
+	.p0_mprddlctl = 0x4B4B4E49,
+	.p0_mpwrdlctl = 0x3F3F3035,
+};
+
+/* ------------------------------------------------------------------------ */
+
+static const struct mx6sdl_iomux_ddr_regs ddr_iomux_s = {
+	.dram_sdqs0 = 0x00000030,
+	.dram_sdqs1 = 0x00000030,
+	.dram_sdqs2 = 0x00000030,
+	.dram_sdqs3 = 0x00000030,
+	.dram_sdqs4 = 0x00000030,
+	.dram_sdqs5 = 0x00000030,
+	.dram_sdqs6 = 0x00000030,
+	.dram_sdqs7 = 0x00000030,
+	.dram_dqm0 = 0x00020030,
+	.dram_dqm1 = 0x00020030,
+	.dram_dqm2 = 0x00020030,
+	.dram_dqm3 = 0x00020030,
+	.dram_dqm4 = 0x00020030,
+	.dram_dqm5 = 0x00020030,
+	.dram_dqm6 = 0x00020030,
+	.dram_dqm7 = 0x00020030,
+	.dram_cas = 0x00020030,
+	.dram_ras = 0x00020030,
+	.dram_sdclk_0 = 0x00020030,
+	.dram_sdclk_1 = 0x00020030,
+	.dram_sdcke0 = 0x00003000,
+	.dram_sdcke1 = 0x00003000,
+	.dram_reset = 0x00020030,
+	.dram_sdba2 = 0x00000000,
+	.dram_sdodt0 = 0x00003030,
+	.dram_sdodt1 = 0x00003030,
+};
+
+static const struct mx6sdl_iomux_grp_regs grp_iomux_s = { /* TODO */
+	.grp_b0ds = 0x00000030,
+	.grp_b1ds = 0x00000030,
+	.grp_b2ds = 0x00000030,
+	.grp_b3ds = 0x00000030,
+	.grp_b4ds = 0x00000030,
+	.grp_b5ds = 0x00000030,
+	.grp_b6ds = 0x00000030,
+	.grp_b7ds = 0x00000030,
+	.grp_addds = 0x00000030,
+	.grp_ddrmode_ctl = 0x00020000,
+	.grp_ddrpke = 0x00000000,
+	.grp_ddrmode = 0x00020000,
+	.grp_ctlds = 0x00000030,
+	.grp_ddr_type = 0x000C0000,
+};
+
+static void spl_imx6sdl_dram_init(const struct mx6_ddr_sysinfo *si,
+				const struct mx6_mmdc_calibration *cb,
+				const struct mx6_ddr3_cfg *cfg)
+{
+	mx6sdl_dram_iocfg(64, &ddr_iomux_s, &grp_iomux_s);
+	mx6_dram_cfg(si, cb, cfg);
+	__udelay(100);
+}
+
+/* ------------------------------------------------------------------------ */
+
+#define BKLGT_PWR_PAD_CTRL MX6_PAD_CTL_SPEED_LOW | MX6_PAD_CTL_DSE_80ohm | MX6_PAD_CTL_SRE_SLOW
+
+static inline void init_backlight_gpios(int cpu_type, unsigned board_variant)
+{
+	void __iomem *iomuxbase = IOMEM(MX6_IOMUXC_BASE_ADDR);
+	void __iomem *gpio6base = IOMEM(MX6_GPIO6_BASE_ADDR);
+	void __iomem *gpio1base = IOMEM(MX6_GPIO1_BASE_ADDR);
+
+	/*
+	 * since revision B a backlight switch is present which can help to
+	 * prevent any kind of flicker when switching on the board. Use it.
+	 * GPIO6/23 controls the backlight. High switches off the backlight.
+	 */
+	switch (board_variant) {
+	case 0 ... 8:
+		break;
+	default:
+		imx6_gpio_direction_output(gpio6base, 23, 1);
+
+		switch (cpu_type) {
+		case IMX6_CPUTYPE_IMX6S:
+		case IMX6_CPUTYPE_IMX6DL:
+			writel(IOMUX_CONFIG_SION | 0x05, iomuxbase + 0x2D0);
+			writel(BKLGT_PWR_PAD_CTRL, iomuxbase + 0x6B8);
+			break;
+		case IMX6_CPUTYPE_IMX6D:
+		case IMX6_CPUTYPE_IMX6Q:
+			writel(IOMUX_CONFIG_SION | 0x05, iomuxbase + 0x068);
+			writel(BKLGT_PWR_PAD_CTRL, iomuxbase + 0x37C);
+			break;
+		}
+	}
+
+	/*
+	 * switch brightness to the lowest available value. This is what we
+	 * can do for revision A boards
+	 * GPIO1/1 controls (via PWM) the brightness. A static low means
+	 * a very dark backlight
+	 */
+	imx6_gpio_direction_output(gpio1base, 1, 0);
+
+	switch (cpu_type) {
+	case IMX6_CPUTYPE_IMX6S:
+	case IMX6_CPUTYPE_IMX6DL:
+		writel(IOMUX_CONFIG_SION | 0x05, iomuxbase + 0x210);
+		writel(BKLGT_PWR_PAD_CTRL, iomuxbase + 0x5E0);
+		break;
+	case IMX6_CPUTYPE_IMX6D:
+	case IMX6_CPUTYPE_IMX6Q:
+		writel(IOMUX_CONFIG_SION | 0x05, iomuxbase + 0x224);
+		writel(BKLGT_PWR_PAD_CTRL, iomuxbase + 0x5F4);
+		break;
+	}
+}
+
+#define LED_PAD_CTRL MX6_PAD_CTL_SPEED_LOW | MX6_PAD_CTL_DSE_240ohm | MX6_PAD_CTL_SRE_SLOW
+
+static inline void setup_leds(int cpu_type)
+{
+	void __iomem *iomuxbase = IOMEM(MX6_IOMUXC_BASE_ADDR);
+	void __iomem *gpiobase = IOMEM(MX6_GPIO1_BASE_ADDR);
+
+	switch (cpu_type) {
+	case IMX6_CPUTYPE_IMX6S:
+	case IMX6_CPUTYPE_IMX6DL:
+		writel(0x05, iomuxbase + 0x20C); /* LED1 (GPIO0) */
+		writel(LED_PAD_CTRL, iomuxbase + 0x5DC);
+		writel(0x05, iomuxbase + 0x224); /* LED2 (GPIO2) */
+		writel(LED_PAD_CTRL, iomuxbase + 0x5F4);
+		writel(0x05, iomuxbase + 0x22C); /* LED3 (GPIO4) */
+		writel(LED_PAD_CTRL, iomuxbase + 0x5FC);
+		break;
+	case IMX6_CPUTYPE_IMX6D:
+	case IMX6_CPUTYPE_IMX6Q:
+		writel(0x05, iomuxbase + 0x220); /* LED1 (GPIO0) */
+		writel(LED_PAD_CTRL, iomuxbase + 0x5f0);
+		writel(0x05, iomuxbase + 0x234); /* LED2 (GPIO2) */
+		writel(LED_PAD_CTRL, iomuxbase + 0x604);
+		writel(0x05, iomuxbase + 0x238); /* LED3 (GPIO4) */
+		writel(LED_PAD_CTRL, iomuxbase + 0x608);
+		break;
+	}
+
+	/* Turn off all LEDS */
+	imx6_gpio_direction_output(gpiobase, 1, 0);
+	imx6_gpio_direction_output(gpiobase, 4, 0);
+	imx6_gpio_direction_output(gpiobase, 16, 0);
+}
+
+static inline void setup_uart(int cpu_type)
+{
+	void __iomem *iomuxbase = IOMEM(MX6_IOMUXC_BASE_ADDR);
+
+	/* UART TxD output is pin EIM/D26, e.g. UART is in DTE mode */
+	switch (cpu_type) {
+	case IMX6_CPUTYPE_IMX6S:
+	case IMX6_CPUTYPE_IMX6DL:
+		writel(0x0, iomuxbase + 0x904); /*  IOMUXC_UART2_UART_RX_DATA_SELECT_INPUT */
+		writel(0x4, iomuxbase + 0x16c); /*  IOMUXC_SW_MUX_CTL_PAD_EIM_DATA26 */
+		break;
+	case IMX6_CPUTYPE_IMX6D:
+	case IMX6_CPUTYPE_IMX6Q:
+		writel(0x0, iomuxbase + 0x928); /*  IOMUXC_UART2_UART_RX_DATA_SELECT_INPUT */
+		writel(0x4, iomuxbase + 0x0bc); /*  IOMUXC_SW_MUX_CTL_PAD_EIM_DATA26 */
+		break;
+	}
+
+	imx6_ungate_all_peripherals();
+	imx6_uart_setup(IOMEM(MX6_UART2_BASE_ADDR));
+	pbl_set_putc(imx_uart_putc, IOMEM(MX6_UART2_BASE_ADDR));
+
+	pr_debug("\n");
+}
+
+/*
+ * Hardware marked board revisions and deployments
+ *
+ *   count      board    ram       flash       CPU
+ *               rev.
+ * 00000000       A    1024 MiB   1024 MiB     i.MX6Q
+ * 00000001       A     512 MiB    256 MiB     i.MX6S
+ * 00000010       A    1024 MiB    512 MiB     i.MX6Q
+ * 00000011       ---- not defined ----
+ * 00000100       A     512 MiB    256 MiB     i.MX6S
+ * 00000101       ---- not defined ----
+ * 00000110       ---- not defined ----
+ * 00000111       ---- not defined ----
+ * 00001000       A    1024 MiB    512 MiB     i.MX6Q
+ * 00001001       B     256 MiB     16 MiB     i.MX6S
+ * 00001010       B     256 MiB    256 MiB     i.MX6S
+ * 00001011       B    1024 MiB    256 MiB     i.MX6Q
+ * 00001100       B    2048 MiB      8 GiB     i.MX6Q
+ * 00001101       C     256 MiB    256 MiB     i.MX6S
+ * 00001110       C    1024 MiB    256 MiB     i.MX6Q
+ * 00001111       C     512 MiB    256 MiB     i.MX6S
+ * 00010000       C     512 MiB      4 GiB     i.MX6S
+ * 00010001       C    2048 MiB      8 GiB     i.MX6Q
+ * 00010010       C    4096 MiB     16 GiB     i.MX6Q+
+ * 00010011       C     512 MiB      2 GiB     i.MX6S
+ * 00010100       C    1024 MiB      4 GiB     i.MX6Q
+ * 00010101       D     512 MiB      2 GIB     i.MX6S
+ * 00010110       D    1024 MiB      4 GIB     i.MX6Q
+ * 00010111       ---- not defined ----
+ * 00011000       E    1024 MiB      4 GIB     i.MX6Q
+ *
+ * This routine does not return if starting the image from SD card or NOR
+ * was successful. It restarts skov_imx6_start() instead
+ */
+static void skov_imx6_init(int cpu_type, unsigned board_variant)
+{
+	enum bootsource bootsrc;
+	int instance;
+
+	switch (board_variant) {
+	case 12: /* P2 i.MX6Q, max performance */
+		if (cpu_type != IMX6_CPUTYPE_IMX6Q) {
+			pr_err("Invalid SoC! i.MX6Q expected\n");
+			return;
+		}
+		pr_debug("Initializing a P2 max performance system...\n");
+		spl_imx6q_dram_init(&skov_imx6_sysinfo_4x256Mb_1066MHz,
+					&skov_imx6_calib_4x256Mb_1066MHz,
+					&skov_imx6_cfg_4x256Mb_1066MHz);
+		break;
+	case 18: /* i.MX6Q+ */
+		if (cpu_type != IMX6_CPUTYPE_IMX6Q) {
+			pr_err("Invalid SoC! i.MX6Q expected\n");
+			return;
+		}
+		pr_debug("Initializing board variant 18\n");
+		spl_imx6q_dram_init(&skov_imx6_sysinfo_4x512Mb_1066MHz,
+					&skov_imx6_calib_4x512Mb_1066MHz,
+					&skov_imx6_cfg_4x512Mb_1066MHz);
+		break;
+	case 19: /* i.MX6S "Solo_R512M_F2G" */
+		if (cpu_type != IMX6_CPUTYPE_IMX6S) {
+			pr_err("Invalid SoC! i.MX6S expected\n");
+			return;
+		}
+		pr_debug("Initializing board variant 19\n");
+		spl_imx6sdl_dram_init(&skov_imx6_sysinfo_2x128Mb_800MHz,
+					&skov_imx6_calib_2x128Mb_800MHz,
+					&skov_imx6_cfg_2x128Mb_800MHz);
+		break;
+	case 20: /* i.MX6Q, "Quad_R1G_F2G" */
+		if (cpu_type != IMX6_CPUTYPE_IMX6Q) {
+			pr_err("Invalid SoC! i.MX6Q expected\n");
+			return;
+		}
+		pr_debug("Initializing board variant 20\n");
+		spl_imx6q_dram_init(&skov_imx6_sysinfo_4x128Mb_1066MHz,
+					&skov_imx6_calib_4x128Mb_1066MHz,
+					&skov_imx6_cfg_4x128Mb_1066MHz);
+		break;
+	case 21: /* i.MX6S "Solo_R512M_F2G" */
+		if (cpu_type != IMX6_CPUTYPE_IMX6S) {
+			pr_err("Invalid SoC! i.MX6S expected\n");
+			return;
+		}
+		pr_debug("Initializing board variant 21\n");
+		spl_imx6sdl_dram_init(&skov_imx6_sysinfo_2x128Mb_800MHz,
+					&skov_imx6_calib_2x128Mb_800MHz,
+					&skov_imx6_cfg_2x128Mb_800MHz);
+		break;
+	case 22: /* i.MX6Q, "Quad_R1G_F4G" */
+		if (cpu_type != IMX6_CPUTYPE_IMX6Q) {
+			pr_err("Invalid SoC! i.MX6Q expected\n");
+			return;
+		}
+		pr_debug("Initializing board variant 22\n");
+		spl_imx6q_dram_init(&skov_imx6_sysinfo_4x128Mb_1066MHz,
+					&skov_imx6_calib_4x128Mb_1066MHz,
+					&skov_imx6_cfg_4x128Mb_1066MHz);
+		break;
+	case 24: /* i.MX6Q, "Quad_R1G_F4G" */
+		if (cpu_type != IMX6_CPUTYPE_IMX6Q) {
+			pr_err("Invalid SoC! i.MX6Q expected\n");
+			return;
+		}
+		pr_debug("Initializing board variant 24\n");
+		spl_imx6q_dram_init(&skov_imx6_sysinfo_4x128Mb_1066MHz,
+					&skov_imx6_calib_4x128Mb_1066MHz,
+					&skov_imx6_cfg_4x128Mb_1066MHz);
+		break;
+	default:
+		pr_err("Unsupported board variant: 0x%x\n", board_variant);
+		/* don't continue */
+		while(1);
+		break;
+	}
+
+	imx6_get_boot_source(&bootsrc, &instance);
+	if (bootsrc == BOOTSOURCE_SPI_NOR) {
+		pr_info("Loading bootloader image from SPI flash...");
+		imx6_spi_start_image(0);
+	} else {
+		pr_info("Loading bootloader image from SD card...");
+		imx6_esdhc_start_image(instance);
+	}
+}
+
+extern char __dtb_z_imx6q_skov_imx6_start[];
+extern char __dtb_z_imx6dl_skov_imx6_start[];
+
+/* called twice: once for SDRAM setup only, second for devicetree setup */
+static noinline void skov_imx6_start(void)
+{
+	int cpu_type = __imx6_cpu_type();
+	unsigned board_variant = skov_imx6_get_version();
+
+	setup_uart(cpu_type);
+
+	if (get_pc() <= MX6_MMDC_PORT01_BASE_ADDR) {
+		/* first call: do the lowlevel things first */
+		init_backlight_gpios(cpu_type, board_variant);
+		setup_leds(cpu_type);
+		pr_info("Starting to init IMX6 system...\n");
+		skov_imx6_init(cpu_type, board_variant);
+		pr_err("Unable to start bootloader\n");
+		while (1);
+	}
+
+	/* boot this platform (second call) */
+	switch (cpu_type) {
+	case IMX6_CPUTYPE_IMX6S:
+	case IMX6_CPUTYPE_IMX6DL:
+		pr_debug("Startup i.MX6S/DL based system...\n");
+		imx6q_barebox_entry(__dtb_z_imx6dl_skov_imx6_start);
+		break;
+	case IMX6_CPUTYPE_IMX6D:
+	case IMX6_CPUTYPE_IMX6Q:
+		pr_debug("Startup i.MX6Q based system...\n");
+		imx6q_barebox_entry(__dtb_z_imx6q_skov_imx6_start);
+		break;
+	}
+}
+
+ENTRY_FUNCTION(start_imx6_skov_imx6, r0, r1, r2)
+{
+	arm_cpu_lowlevel_init();
+
+	relocate_to_current_adr();
+	setup_c();
+	barrier();
+
+	skov_imx6_start();
+}
diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile
index d5f61768a5..bb8a51647d 100644
--- a/arch/arm/dts/Makefile
+++ b/arch/arm/dts/Makefile
@@ -110,6 +110,7 @@ lwl-$(CONFIG_MACH_SOLIDRUN_CUBOX) += dove-cubox-bb.dtb.o
 lwl-$(CONFIG_MACH_SOLIDRUN_MICROSOM) += imx6dl-hummingboard.dtb.o imx6q-hummingboard.dtb.o \
 				imx6dl-hummingboard2.dtb.o imx6q-hummingboard2.dtb.o \
 				imx6q-h100.dtb.o
+lwl-$(CONFIG_MACH_SKOV_IMX6) += imx6dl-skov-imx6.dtb.o imx6q-skov-imx6.dtb.o
 lwl-$(CONFIG_MACH_SEEED_ODYSSEY) += stm32mp157c-odyssey.dtb.o
 lwl-$(CONFIG_MACH_STM32MP15XX_DKX) += stm32mp157c-dk2.dtb.o stm32mp157a-dk1.dtb.o
 lwl-$(CONFIG_MACH_LXA_MC1) += stm32mp157c-lxa-mc1.dtb.o
diff --git a/arch/arm/dts/imx6dl-skov-imx6.dts b/arch/arm/dts/imx6dl-skov-imx6.dts
new file mode 100644
index 0000000000..c2dac68204
--- /dev/null
+++ b/arch/arm/dts/imx6dl-skov-imx6.dts
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2015 Juergen Borleis, Pengutronix <kernel at 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
+ */
+
+/dts-v1/;
+#include <arm/imx6dl.dtsi>
+#include "imx6dl.dtsi"
+#include "imx6qdl-skov-imx6.dtsi"
+
+/ {
+	model = "Skov IMX6";
+	compatible = "skov,imx6", "fsl,imx6dl";
+
+	chosen {
+		stdout-path = &uart2;
+	};
+};
diff --git a/arch/arm/dts/imx6q-skov-imx6.dts b/arch/arm/dts/imx6q-skov-imx6.dts
new file mode 100644
index 0000000000..fea84cb498
--- /dev/null
+++ b/arch/arm/dts/imx6q-skov-imx6.dts
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2015 Juergen Borleis, Pengutronix <kernel at 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
+ */
+
+/dts-v1/;
+#include <arm/imx6q.dtsi>
+#include "imx6q.dtsi"
+#include "imx6qdl-skov-imx6.dtsi"
+
+/ {
+	model = "Skov IMX6";
+	compatible = "skov,imx6", "fsl,imx6q";
+
+	chosen {
+		stdout-path = &uart2;
+	};
+};
+
+&i2c2 {
+	status = "okay";
+};
diff --git a/arch/arm/dts/imx6qdl-skov-imx6.dtsi b/arch/arm/dts/imx6qdl-skov-imx6.dtsi
new file mode 100644
index 0000000000..03f3cb02fc
--- /dev/null
+++ b/arch/arm/dts/imx6qdl-skov-imx6.dtsi
@@ -0,0 +1,546 @@
+/*
+ * Copyright 2015 Juergen Borleis, Pengutronix <kernel at 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
+ */
+
+#include <dt-bindings/gpio/gpio.h>
+
+/ {
+	aliases {
+		state = &state;
+	};
+
+	chosen {
+		environment-sd {
+			compatible = "barebox,environment";
+			device-path = &usdhc3, "partname:2";
+			status = "disabled";
+		};
+
+		environment-spinor {
+			compatible = "barebox,environment";
+			device-path = &barebox_env;
+			status = "disabled";
+		};
+	};
+
+	leds {
+		compatible = "gpio-leds";
+
+		led0: D1 {
+			label = "D1";
+			gpios = <&gpio1 2 0>;
+			default-state = "on";
+			linux,default-trigger = "heartbeat";
+		};
+
+		led1: D2 {
+			label = "D2";
+			gpios = <&gpio1 0 0>;
+			default-state = "off";
+		};
+
+		led2: D3 {
+			label = "D3";
+			gpios = <&gpio1 4 0>;
+			default-state = "on";
+		};
+	};
+
+	/* State: mutable part */
+	state: state {
+		magic = <0x34a0fc27>;
+		compatible = "barebox,state";
+		backend-type = "raw";
+		backend = <&state_storage>;
+		backend-stridesize = <0x40>;
+
+		bootstate {
+			#address-cells = <1>;
+			#size-cells = <1>;
+
+			system0 { /* the node's name here must match the subnode's name in the 'bootstate' node */
+				#address-cells = <1>;
+				#size-cells = <1>;
+
+				remaining_attempts at 0 {
+					reg = <0x0 0x4>;
+					type = "uint32";
+					default = <3>;
+				};
+				priority at 4 {
+					reg = <0x4 0x4>;
+					type = "uint32";
+					default = <30>;
+				};
+			};
+
+			system1 { /* the node's name here must match the subnode's name in the 'bootstate' node */
+				#address-cells = <1>;
+				#size-cells = <1>;
+
+				remaining_attempts at 8 {
+					reg = <0x8 0x4>;
+					type = "uint32";
+					default = <3>;
+				};
+				priority at C {
+					reg = <0xC 0x4>;
+					type = "uint32";
+					default = <20>;
+				};
+			};
+
+			last_chosen at 10 {
+				reg = <0x10 0x4>;
+				type = "uint32";
+			};
+		};
+
+		display {
+			#address-cells = <1>;
+			#size-cells = <1>;
+
+			xres at 14 {
+				reg = <0x14 0x4>;
+				type = "uint32";
+				default = <0>;
+			};
+
+			yres at 18 {
+				reg = <0x18 0x4>;
+				type = "uint32";
+				default = <0>;
+			};
+
+			brightness at 1C {
+				reg = <0x1C 0x1>;
+				type = "uint8";
+				default = <8>;
+			};
+
+			external at 1D {
+				reg = <0x1D 0x1>;
+				type = "uint8";
+				default = <0>;
+			};
+		};
+
+		ethaddr {
+			#address-cells = <1>;
+			#size-cells = <1>;
+
+			eth2 {
+				reg = <0x1E 0x6>;
+				type = "mac";
+				default = [00 11 22 33 44 55];
+			};
+		};
+	};
+
+	backlight_lcd: backlight_lcd {
+		compatible = "pwm-backlight";
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinctrl_backlight>;
+		pwms = <&pwm2 0 20000 0>;
+		brightness-levels = <0 16 32 48 64 80 96 112 128 144 160 176 192 208 224 240 255>;
+		default-brightness-level = <8>;
+		enable-gpios = <&gpio6 23 GPIO_ACTIVE_LOW>;
+		status = "okay";
+	};
+
+	display {
+		status = "disabled";
+		compatible = "fsl,imx-parallel-display";
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinctrl_ipu1_4>;
+		interface-pix-fmt = "rgb24";
+
+		port {
+			display0_in: endpoint {
+				remote-endpoint = <&ipu1_di0_disp0>;
+			};
+		};
+
+		display-timings {
+			native-mode = &l2rt;
+
+			l2rt: l2rt {
+				native-mode;
+				clock-frequency = <33000000>;
+				hactive = <800>;
+				vactive = <480>;
+				hback-porch = <85>;
+				hfront-porch = <112>;
+				vback-porch = <29>;
+				vfront-porch = <38>;
+				hsync-len = <3>;
+				vsync-len = <3>;
+				pixelclk-active = <1>;
+				hsync-active = <0>;
+				vsync-active = <0>;
+				de-active = <1>;
+			};
+
+			l6whrt: l6whrt {
+				native-mode;
+				clock-frequency = <33000000>;
+				hactive = <800>;
+				vactive = <480>;
+				hback-porch = <43>;
+				hfront-porch = <154>;
+				vback-porch = <20>;
+				vfront-porch = <47>;
+				hsync-len = <3>;
+				vsync-len = <3>;
+				pixelclk-active = <1>;
+				hsync-active = <0>;
+				vsync-active = <0>;
+				de-active = <1>;
+			};
+		};
+	};
+
+	panel {
+		compatible = "simple-panel";
+		backlight = <&backlight_lcd>;
+		/* power-supply = <&reg_3p3v>; */
+
+		display-timings {
+			native-mode = &mi1010ait_1cp1;
+
+			mi1010ait_1cp1: mi1010ait-1cp1 {
+				native-mode;
+				clock-frequency = <70000000>;
+				hactive = <1280>;
+				vactive = <800>;
+				hback-porch = <60>;
+				hfront-porch = <60>;
+				vback-porch = <10>;
+				vfront-porch = <10>;
+				hsync-len = <10>;
+				vsync-len = <6>;
+				/* pixelclk-active = <>; */
+				hsync-active = <0>;
+				vsync-active = <0>;
+				de-active = <1>;
+			};
+		};
+
+		port {
+			panel_in: endpoint {
+				remote-endpoint = <&lvds0_out>;
+			};
+		};
+	};
+};
+
+&pwm2 {
+	/* used for backlight brightness */
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_pwm2_2>;
+	status = "okay";
+};
+
+&pwm3 {
+	/* used for LCD contrast control */
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_pwm3_2>;
+	status = "okay";
+};
+
+&i2c2 {
+	clock-frequency = <100000>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_i2c2_2>;
+};
+
+&hdmi {
+	status = "okay";
+	ddc-i2c-bus = <&i2c2>;
+};
+
+/delete-node/&ipu1_di0_hdmi;
+/delete-node/&hdmi_mux_0;
+
+&ipu1_di0_disp0 {
+	remote-endpoint = <&display0_in>;
+};
+
+&iomuxc {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_hog>;
+
+	pinctrl_hog: hoggrp {
+		/* we need a few pins as GPIOs */
+		fsl,pins = <
+			/* MMC IO voltage select */
+			MX6QDL_PAD_GPIO_18__GPIO7_IO13 0x40000058
+			/* MMC Power Supply Switch (since revision C)
+			MX6QDL_PAD_SD3_RST__GPIO7_IO08 0x40000058
+			/* Backlight Power Supply Switch (since revision B)
+			MX6QDL_PAD_RGMII_TD3__GPIO6_IO23 0x40000058
+			/* Backlight Brightness */
+			MX6QDL_PAD_GPIO_1__GPIO1_IO01 0x40000058
+			/* must be high */
+			MX6QDL_PAD_ENET_RX_ER__GPIO1_IO24 0x40000058
+		>;
+	};
+
+	pinctrl_uart2: uart2grp {
+		fsl,pins = <
+			MX6QDL_PAD_EIM_D26__UART2_TX_DATA 0x1b0b1
+			MX6QDL_PAD_EIM_D27__UART2_RX_DATA 0x1b0b1
+		>;
+	};
+
+	pinctrl_ecspi1: ecspi1grp {
+		fsl,pins = <
+			MX6QDL_PAD_EIM_D17__ECSPI1_MISO 0x100b1
+			MX6QDL_PAD_EIM_D18__ECSPI1_MOSI 0x100b1
+			MX6QDL_PAD_EIM_D16__ECSPI1_SCLK 0x100b1
+			MX6QDL_PAD_EIM_D24__GPIO3_IO24 0x40000058 /* CS# signal */
+		>;
+	};
+
+	/* pins for eth0 */
+	pinctrl_enet: enetgrp {
+		fsl,pins = <
+			MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x100b0
+			MX6QDL_PAD_ENET_MDC__ENET_MDC 0x100b0
+			MX6QDL_PAD_ENET_CRS_DV__ENET_RX_EN 0x100b0
+			MX6QDL_PAD_ENET_TX_EN__ENET_TX_EN 0x100b0
+			MX6QDL_PAD_ENET_RXD0__ENET_RX_DATA0 0x100b0
+			MX6QDL_PAD_ENET_RXD1__ENET_RX_DATA1 0x100b0
+			MX6QDL_PAD_ENET_TXD0__ENET_TX_DATA0 0x100b0
+			MX6QDL_PAD_ENET_TXD1__ENET_TX_DATA1 0x100b0
+			MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x400000c0
+		>;
+	};
+
+	pinctrl_usdhc3: usdhc3grp {
+		fsl,pins = <
+			MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059
+			MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059
+			MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059
+			MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059
+			MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059
+			MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059
+			MX6QDL_PAD_SD3_DAT4__GPIO7_IO01 0x1b040 /* WP */
+			MX6QDL_PAD_SD3_DAT5__GPIO7_IO00 0x1b040 /* CD */
+		>;
+	};
+
+	pinctrl_gpmi_nand: gpminandgrp {
+		fsl,pins = <
+			MX6QDL_PAD_NANDF_CLE__NAND_CLE 0xb0b1
+			MX6QDL_PAD_NANDF_ALE__NAND_ALE 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_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
+		>;
+	};
+
+	pinctrl_i2c2_2: i2c2grp-2 {
+		fsl,pins = <
+			/* internal 22 k pull up required */
+			MX6QDL_PAD_KEY_COL3__I2C2_SCL		0x4001F878
+			/* internal 22 k pull up required */
+			MX6QDL_PAD_KEY_ROW3__I2C2_SDA		0x4001F878
+		>;
+	};
+
+	pinctrl_ipu1_4: ipu1grp-4 {
+		fsl,pins = <
+			MX6QDL_PAD_DI0_DISP_CLK__IPU1_DI0_DISP_CLK	0x10
+			MX6QDL_PAD_DI0_PIN15__IPU1_DI0_PIN15		0x10
+			MX6QDL_PAD_DI0_PIN2__IPU1_DI0_PIN02		0x10
+			MX6QDL_PAD_DI0_PIN3__IPU1_DI0_PIN03		0x10
+			MX6QDL_PAD_DISP0_DAT0__IPU1_DISP0_DATA00	0x10
+			MX6QDL_PAD_DISP0_DAT1__IPU1_DISP0_DATA01	0x10
+			MX6QDL_PAD_DISP0_DAT2__IPU1_DISP0_DATA02	0x10
+			MX6QDL_PAD_DISP0_DAT3__IPU1_DISP0_DATA03	0x10
+			MX6QDL_PAD_DISP0_DAT4__IPU1_DISP0_DATA04	0x10
+			MX6QDL_PAD_DISP0_DAT5__IPU1_DISP0_DATA05	0x10
+			MX6QDL_PAD_DISP0_DAT6__IPU1_DISP0_DATA06	0x10
+			MX6QDL_PAD_DISP0_DAT7__IPU1_DISP0_DATA07	0x10
+			MX6QDL_PAD_DISP0_DAT8__IPU1_DISP0_DATA08	0x10
+			MX6QDL_PAD_DISP0_DAT9__IPU1_DISP0_DATA09	0x10
+			MX6QDL_PAD_DISP0_DAT10__IPU1_DISP0_DATA10	0x10
+			MX6QDL_PAD_DISP0_DAT11__IPU1_DISP0_DATA11	0x10
+			MX6QDL_PAD_DISP0_DAT12__IPU1_DISP0_DATA12	0x10
+			MX6QDL_PAD_DISP0_DAT13__IPU1_DISP0_DATA13	0x10
+			MX6QDL_PAD_DISP0_DAT14__IPU1_DISP0_DATA14	0x10
+			MX6QDL_PAD_DISP0_DAT15__IPU1_DISP0_DATA15	0x10
+			MX6QDL_PAD_DISP0_DAT16__IPU1_DISP0_DATA16	0x10
+			MX6QDL_PAD_DISP0_DAT17__IPU1_DISP0_DATA17	0x10
+			MX6QDL_PAD_DISP0_DAT18__IPU1_DISP0_DATA18	0x10
+			MX6QDL_PAD_DISP0_DAT19__IPU1_DISP0_DATA19	0x10
+			MX6QDL_PAD_DISP0_DAT20__IPU1_DISP0_DATA20	0x10
+			MX6QDL_PAD_DISP0_DAT21__IPU1_DISP0_DATA21	0x10
+			MX6QDL_PAD_DISP0_DAT22__IPU1_DISP0_DATA22	0x10
+			MX6QDL_PAD_DISP0_DAT23__IPU1_DISP0_DATA23	0x10
+		>;
+	};
+
+	pinctrl_backlight: backlight_grp {
+		fsl,pins = <
+			MX6QDL_PAD_RGMII_TD3__GPIO6_IO23	0x40000058
+		>;
+	};
+
+	pinctrl_pwm2_2: pwm2grp-2 {
+		fsl,pins = <
+			MX6QDL_PAD_GPIO_1__PWM2_OUT		0x00058
+		>;
+	};
+
+	pinctrl_pwm3_2: pwm3grp-2 {
+		fsl,pins = <
+			MX6QDL_PAD_SD1_DAT1__PWM3_OUT		0x00058
+		>;
+	};
+};
+
+&clks {
+	assigned-clocks = <&clks IMX6QDL_CLK_LDB_DI0_SEL>,
+			  <&clks IMX6QDL_CLK_LDB_DI1_SEL>;
+	assigned-clock-parents = <&clks IMX6QDL_CLK_PLL5_VIDEO_DIV>,
+				 <&clks IMX6QDL_CLK_PLL5_VIDEO_DIV>;
+};
+
+/* console */
+&uart2 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_uart2>;
+	status = "okay";
+};
+
+/* spi */
+&ecspi1 {
+	fsl,spi-num-chipselects = <1>;
+	cs-gpios = <&gpio3 24 0>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_ecspi1>;
+	status = "okay";
+
+	norflash: m25p80 at 0 {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		compatible = "jedec,spi-nor";
+		spi-max-frequency = <54000000>;
+		reg = <0>;
+	};
+};
+
+/* eth0 */
+&fec {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_enet>;
+	phy-mode = "rmii";
+	status = "okay";
+	phy-reset-gpios = <&gpio1 5 0>;
+	phy-reset-duration = <100>;
+	#address-cells = <0>;
+	#size-cells = <1>;
+	fixed-link {
+		speed = <100>;
+		full-duplex;
+	};
+};
+
+&ldb {
+	status = "disabled";
+
+	lvds0: lvds-channel at 0 {
+		status = "disabled";
+		fsl,data-width = <24>;
+		fsl,data-mapping = "spwg";
+
+		port at 4 {
+			reg = <4>;
+			lvds0_out: endpoint {
+				remote-endpoint = <&panel_in>;
+			};
+		};
+	};
+};
+
+&wdog1 {
+	status = "okay";
+};
+
+&usdhc3 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_usdhc3>;
+	wp-gpios = <&gpio7 1 0>;
+	cd-gpios = <&gpio7 0 0>;
+	status = "okay";
+	fsl,delay-line;
+};
+
+&gpmi {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_gpmi_nand>;
+	nand-on-flash-bbt;
+	status = "okay";
+
+	partitions {
+		compatible = "fixed-partitions";
+		#address-cells = <1>;
+		#size-cells = <1>;
+
+		partition at 0 {
+			label = "firmware";
+			reg = <0x00000000 0x000000000>; /* keep zero sized to enable autodetection */
+		};
+	};
+};
+
+/* define the SPI based 8 MiB NOR flash layout */
+&norflash {
+	partitions {
+		compatible = "fixed-partitions";
+		#address-cells = <1>;
+		#size-cells = <1>;
+
+		partition at 0 {
+			label = "barebox";
+			reg = <0x0 0x100000>;
+		};
+
+		/* space left to let barebox grow */
+
+		/* placed near the end of the NOR memory */
+		barebox_env: partition at 780000 {
+			label = "barebox-environment";
+			reg = <0x780000 0x40000>;
+		};
+
+		/* placed at the end of the NOR memory */
+		state_storage: partition at 7C0000 {
+			label = "barebox-state";
+			reg = <0x7C0000 0x40000>; /* four times mirrored */
+		};
+	};
+};
+
+&ocotp {
+	barebox,provide-mac-address = <&fec 0x620>;
+};
diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
index 3f8012c732..daa8ba66d9 100644
--- a/arch/arm/mach-imx/Kconfig
+++ b/arch/arm/mach-imx/Kconfig
@@ -592,6 +592,12 @@ config MACH_MNT_REFORM
 	select MCI_IMX_ESDHC_PBL
 	select I2C_IMX_EARLY
 
+config MACH_SKOV_IMX6
+	bool "Skov IMX6"
+	select ARCH_IMX6
+	select ARM_USE_COMPRESSED_DTB
+	select MCI_IMX_ESDHC_PBL
+
 endif
 
 # ----------------------------------------------------------
diff --git a/images/Makefile.imx b/images/Makefile.imx
index b892ec0196..12cc478674 100644
--- a/images/Makefile.imx
+++ b/images/Makefile.imx
@@ -331,6 +331,8 @@ $(call build_imx_habv4img, CONFIG_MACH_KONTRON_SAMX6I, start_imx6q_samx6i, kontr
 
 $(call build_imx_habv4img, CONFIG_MACH_KONTRON_SAMX6I, start_imx6dl_samx6i, kontron-samx6i/flash-header-samx6i-duallite, imx6dl-samx6i)
 
+$(call build_imx_habv4img, CONFIG_MACH_SKOV_IMX6, start_imx6_skov_imx6, skov-imx6/flash-header-mx6-skov-imx6, skov-imx6)
+
 $(call build_imx_habv4img, CONFIG_MACH_GW_VENTANA, start_imx6q_gw54xx_1gx64, gateworks-ventana/flash-header-ventana-quad-1gx64, gateworks-imx6q-ventana-1gx64)
 
 $(call build_imx_habv4img, CONFIG_MACH_ELTEC_HIPERCAM, start_imx6dl_eltec_hipercam, eltec-hipercam/flash-header-eltec-hipercam, eltec-hipercam)
-- 
2.29.2




More information about the barebox mailing list