summaryrefslogtreecommitdiffstats
path: root/arch/arm/boards/skov-imx6/board.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/boards/skov-imx6/board.c')
-rw-r--r--arch/arm/boards/skov-imx6/board.c328
1 files changed, 254 insertions, 74 deletions
diff --git a/arch/arm/boards/skov-imx6/board.c b/arch/arm/boards/skov-imx6/board.c
index a58172b2b1..8ebb4a6e58 100644
--- a/arch/arm/boards/skov-imx6/board.c
+++ b/arch/arm/boards/skov-imx6/board.c
@@ -2,18 +2,27 @@
#define pr_fmt(fmt) "skov-imx6: " fmt
+#include <bootsource.h>
#include <common.h>
-#include <init.h>
-#include <mach/bbu.h>
+#include <deep-probe.h>
+#include <envfs.h>
#include <environment.h>
-#include <bootsource.h>
#include <globalvar.h>
+#include <gpio.h>
+#include <init.h>
+#include <linux/micrel_phy.h>
+#include <mach/imx/bbu.h>
#include <net.h>
#include <of_gpio.h>
-#include <gpio.h>
#include "version.h"
+struct skov_imx6_priv {
+ struct device *dev;
+};
+
+static struct skov_imx6_priv *skov_priv;
+
static int eth_of_fixup_node(struct device_node *root, const char *node_path,
const u8 *ethaddr)
{
@@ -21,24 +30,21 @@ static int eth_of_fixup_node(struct device_node *root, const char *node_path,
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);
+ dev_err(skov_priv->dev, "The mac-address %pM is invalid.\n", ethaddr);
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);
+ dev_err(skov_priv->dev, "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));
+ dev_err(skov_priv->dev, "Setting mac-address property of %pOF failed with: %s.\n",
+ node, strerror(-ret));
return ret;
}
@@ -51,7 +57,7 @@ static int eth_of_fixup_node_from_eth_device(struct device_node *root,
edev = eth_get_byname(ethname);
if (!edev) {
- pr_err("Did not find eth device \"%s\" to copy mac-address from.\n", ethname);
+ dev_err(skov_priv->dev, "Did not find eth device \"%s\" to copy mac-address from.\n", ethname);
return -ENOENT;
}
@@ -65,14 +71,14 @@ static int get_mac_address_from_env_variable(const char *env, u8 ethaddr[ETH_ALE
ethaddr_str = getenv(env);
if (!ethaddr_str) {
- pr_err("State variable %s storing the mac-address not found.\n", env);
+ dev_err(skov_priv->dev, "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);
+ dev_err(skov_priv->dev, "Could not convert \"%s\" in state variable %s into mac-address.\n",
+ ethaddr_str, env);
return -EINVAL;
}
@@ -87,13 +93,13 @@ static int get_default_mac_address_from_state_node(const char *state_node_path,
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);
+ dev_err(skov_priv->dev, "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);
+ dev_err(skov_priv->dev, "Node %s has no property \"default\" of proper type.\n", state_node_path);
return -ENOENT;
}
@@ -303,20 +309,114 @@ static const struct board_description imx6_variants[] = {
};
static int skov_board_no = -1;
+static bool skov_have_switch = true;
+static const char *no_switch_suffix = "-noswitch";
-static int skov_imx6_fixup(struct device_node *root, void *unused)
+static void fixup_noswitch_machine_compatible(struct device_node *root)
{
- int ret;
- const char *val;
- uint32_t brightness;
+ const char *compat = imx6_variants[skov_board_no].dts_compatible;
+ const char *generic = "skov,imx6";
+ char *buf;
+
+ /* add generic compatible, so systemd&co can make right decisions */
+ buf = xasprintf("%s%s", generic, no_switch_suffix);
+ of_prepend_machine_compatible(root, buf);
+
+ /* add specific compatible as fallback, in case this board has new
+ * challenges.
+ */
+ buf = xasprintf("%s%s", compat, no_switch_suffix);
+ of_prepend_machine_compatible(root, buf);
+
+ free(buf);
+}
+
+static void skov_imx6_no_switch(struct device_node *root)
+{
+ const char *fec_alias = "ethernet0";
struct device_node *node;
+ int ret;
+
+ fixup_noswitch_machine_compatible(root);
+
+ node = of_find_node_by_alias(root, fec_alias);
+ if (node) {
+ ret = of_device_disable(node);
+ if (ret)
+ dev_warn(skov_priv->dev, "Can't disable %s\n", fec_alias);
+ } else {
+ dev_warn(skov_priv->dev, "Can't find node by alias: %s\n", fec_alias);
+ }
+
+ node = of_find_node_by_alias(root, "mdio-gpio0");
+ if (node) {
+ ret = of_device_disable(node);
+ if (ret)
+ dev_warn(skov_priv->dev, "Can't disable mdio-gpio0 node\n");
+ } else {
+ dev_warn(skov_priv->dev, "Can't find mdio-gpio0 node\n");
+ }
+}
+
+static int skov_imx6_switch_port(struct device_node *root, const char *path)
+{
+ size_t size;
+ char *buf;
+ int ret;
+
+ /* size is, string + '\0' + port number */
+ size = strlen(path) + 2;
+ buf = xzalloc(size);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = snprintf(buf, size, "%s0", path);
+ if (ret < 0)
+ return ret;
+
+ ret = eth_of_fixup_node_from_eth_device(root, buf, "eth0");
+ if (ret)
+ return ret;
+
+ ret = snprintf(buf, size, "%s1", path);
+ if (ret < 0)
+ return ret;
+
+ ret = eth2_of_fixup_node_individually(root, buf, "eth0",
+ "state.ethaddr.eth2",
+ "/state/ethaddr/eth2");
+ return ret;
+}
+
+static void skov_imx6_switch(struct device_node *root)
+{
+ const char *old = "/mdio-gpio/ksz8873@3/ports/ports@";
+ const char *new = "/mdio/switch@0/ports/ports@";
+ int ret;
+
+ /* Old DTS variants (pre kernel mainline) use different path. Try first
+ * the new variant, then fall back to the old one.
+ */
+ ret = skov_imx6_switch_port(root, new);
+ if (ret) {
+ ret = skov_imx6_switch_port(root, old);
+ if (ret)
+ dev_err(skov_priv->dev, "Filed to set mac address\n");
+ }
+}
+
+static int skov_imx6_fixup(struct device_node *root, void *unused)
+{
struct device_node *chosen = of_create_node(root, "/chosen");
+ struct device_node *node;
+ uint32_t brightness;
+ const char *val;
+ int ret;
- eth_of_fixup_node_from_eth_device(root,
- "/mdio-gpio/ksz8873@3/ports/ports@0", "eth0");
- eth2_of_fixup_node_individually(root,
- "/mdio-gpio/ksz8873@3/ports/ports@1", "eth0",
- "state.ethaddr.eth2", "/state/ethaddr/eth2");
+ if (skov_have_switch)
+ skov_imx6_switch(root);
+ else
+ skov_imx6_no_switch(root);
switch (bootsource_get()) {
case BOOTSOURCE_MMC:
@@ -326,7 +426,7 @@ static int skov_imx6_fixup(struct device_node *root, void *unused)
default:
val = getenv("state.display.brightness");
if (!val) {
- pr_err("could not get default display brightness\n");
+ dev_err(skov_priv->dev, "could not get default display brightness\n");
return 0;
}
@@ -337,7 +437,7 @@ static int skov_imx6_fixup(struct device_node *root, void *unused)
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",
+ dev_err(skov_priv->dev, "error %d while setting default-brightness-level property on node %s to %d\n",
ret, node->name, brightness);
}
@@ -348,6 +448,40 @@ static int skov_imx6_fixup(struct device_node *root, void *unused)
return 0;
}
+static void skov_init_parallel_lcd(void)
+{
+ struct device_node *lcd;
+
+ lcd = of_find_compatible_node(NULL, NULL, "fsl,imx-parallel-display");
+ if (!lcd) {
+ dev_err(skov_priv->dev, "Cannot find \"fsl,imx-parallel-display\" node\n");
+ return;
+ }
+
+ of_device_enable_and_register(lcd);
+}
+
+static void skov_init_ldb(void)
+{
+ struct device_node *ldb, *chan;
+
+ ldb = of_find_compatible_node(NULL, NULL, "fsl,imx6q-ldb");
+ if (!ldb) {
+ dev_err(skov_priv->dev, "Cannot find \"fsl,imx6q-ldb\" node\n");
+ return;
+ }
+
+ /* First enable channel 0, prior to enabling parent */
+ chan = of_find_node_by_name_address(ldb, "lvds-channel@0");
+ if (chan)
+ of_device_enable(chan);
+ else
+ dev_err(skov_priv->dev, "Cannot find \"lvds-channel@0\" node\n");
+
+ /* Now probe will see the expected device tree */
+ of_device_enable_and_register(ldb);
+}
+
/*
* Some variants need tweaks to make them work
*
@@ -357,10 +491,19 @@ static int skov_imx6_fixup(struct device_node *root, void *unused)
*/
static void skov_init_board(const struct board_description *variant)
{
- struct device_node *np;
+ struct device_node *gpio_np = NULL;
char *environment_path, *envdev;
int ret;
+ gpio_np = of_find_node_by_name_address(NULL, "gpio@20b4000");
+ if (gpio_np) {
+ ret = of_device_ensure_probed(gpio_np);
+ if (ret)
+ dev_warn(skov_priv->dev, "Can't probe GPIO node\n");
+ } else {
+ dev_warn(skov_priv->dev, "Can't get GPIO node\n");
+ }
+
imx6_bbu_internal_spi_i2c_register_handler("spiflash", "/dev/m25p0.barebox",
BBU_HANDLER_FLAG_DEFAULT);
@@ -377,12 +520,12 @@ static void skov_init_board(const struct board_description *variant)
break;
}
- pr_notice("Using environment in %s\n", envdev);
+ dev_notice(skov_priv->dev, "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);
+ dev_warn(skov_priv->dev, "Failed to enable environment partition '%s' (%d)\n",
+ environment_path, ret);
if (variant->flags & SKOV_NEED_ENABLE_RMII) {
/*
@@ -391,11 +534,13 @@ static void skov_init_board(const struct board_description *variant)
*/
gpio_request(24, "must_be_low");
gpio_direction_output(24, 0);
+ gpio_free(24);
}
/* SD card handling */
gpio_request(205, "mmc io supply");
gpio_direction_output(205, 0); /* select 3.3 V IO voltage */
+ gpio_free(205);
if (variant->flags & SKOV_ENABLE_MMC_POWER) {
/*
@@ -406,61 +551,89 @@ static void skov_init_board(const struct board_description *variant)
gpio_direction_output(200, 0); /* switch on */
mdelay(1);
gpio_direction_output(200, 1); /* switch on */
+ gpio_free(200);
}
- 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_PARALLEL)
+ skov_init_parallel_lcd();
+
+ if (variant->flags & SKOV_DISPLAY_LVDS)
+ skov_init_ldb();
+}
+
+static int skov_set_switch_lan2_mac(struct skov_imx6_priv *priv)
+{
+ const char *state = "/state/ethaddr/eth2";
+ struct device_node *lan2_np;
+ u8 ethaddr[ETH_ALEN];
+ int ret;
+
+ ret = get_mac_address_from_env_variable("state.ethaddr.eth2", ethaddr);
+ if (ret || !is_valid_ether_addr(ethaddr)) {
+ ret = get_default_mac_address_from_state_node(state, ethaddr);
+ if (ret || !is_valid_ether_addr(ethaddr)) {
+ dev_err(priv->dev, "can't get MAC for LAN2\n");
+ return -ENODEV;
+ }
}
- 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@0");
- if (np)
- of_device_enable(np);
- else
- pr_err("Cannot find \"lvds-channel@0\" node\n");
+ lan2_np = of_find_node_by_path("/mdio/switch@0/ports/ports@1");
+ if (!lan2_np) {
+ dev_err(priv->dev, "LAN2 node not found\n");
+ return -ENODEV;
}
+
+ of_eth_register_ethaddr(lan2_np, ethaddr);
+
+ return 0;
}
-static void fixup_machine_compatible(const char *compat)
+static int skov_switch_test(void)
{
- const char *curcompat;
- struct device_node *root;
- int cclen = 0, clen = strlen(compat) + 1;
- void *buf;
+ struct device *sw_dev;
+ struct device *eth0;
+ int ret;
- root = of_get_root_node();
- if (!root)
- return;
+ if (skov_board_no < 0)
+ return 0;
- curcompat = of_get_property(root, "compatible", &cclen);
+ /* Driver should be able to detect if device do actually
+ * exist. So, we need only to detect if driver is actually
+ * probed.
+ */
+ sw_dev = of_find_device_by_node_path("/mdio/switch@0");
+ if (!sw_dev) {
+ dev_err(skov_priv->dev, "switch@0 device was not created!\n");
+ goto no_switch;
+ }
- buf = xzalloc(cclen + clen);
+ if (dev_is_probed(sw_dev)) {
+ skov_set_switch_lan2_mac(skov_priv);
+ /* even if we fail, continue to boot as good as possible */
+ return 0;
+ }
- memcpy(buf, compat, clen);
- memcpy(buf + clen, curcompat, cclen);
+no_switch:
+ skov_have_switch = false;
- /*
- * 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);
+ dev_notice(skov_priv->dev, "No-switch variant is detected\n");
- free(buf);
+ eth0 = get_device_by_name("eth0");
+ if (eth0) {
+ ret = dev_set_param(eth0, "mode", "disabled");
+ if (ret)
+ dev_warn(skov_priv->dev, "Can't set eth0 mode\n");
+ } else {
+ dev_warn(skov_priv->dev, "Can't disable eth0\n");
+ }
+
+ return 0;
}
+late_initcall(skov_switch_test);
-static int skov_imx6_probe(struct device_d *dev)
+static int skov_imx6_probe(struct device *dev)
{
+ struct skov_imx6_priv *priv;
unsigned v = 0;
const struct board_description *variant;
@@ -480,6 +653,10 @@ static int skov_imx6_probe(struct device_d *dev)
skov_board_no = v;
+ priv = xzalloc(sizeof(*priv));
+ priv->dev = dev;
+ skov_priv = priv;
+
globalvar_add_simple_int("board.no", &skov_board_no, "%u");
globalvar_add_simple("board.variant", variant->variant);
globalvar_add_simple("board.revision",variant->revision);
@@ -487,10 +664,12 @@ static int skov_imx6_probe(struct device_d *dev)
globalvar_add_simple("board.dts", variant->dts_compatible);
globalvar_add_simple("board.display", variant->display ?: NULL);
- fixup_machine_compatible(variant->dts_compatible);
+ of_prepend_machine_compatible(NULL, variant->dts_compatible);
skov_init_board(variant);
+ defaultenv_append_directory(defaultenv_skov_imx6);
+
return 0;
}
@@ -501,8 +680,9 @@ static __maybe_unused struct of_device_id skov_version_ids[] = {
/* sentinel */
}
};
+BAREBOX_DEEP_PROBE_ENABLE(skov_version_ids);
-static struct driver_d skov_version_driver = {
+static struct driver skov_version_driver = {
.name = "skov-imx6",
.probe = skov_imx6_probe,
.of_compatible = DRV_OF_COMPAT(skov_version_ids),
@@ -518,7 +698,7 @@ static void skov_imx6_devices_shutdown(void)
external = getenv("state.display.external");
if (!external) {
- pr_err("could not get state variable display.external\n");
+ dev_err(skov_priv->dev, "could not get state variable display.external\n");
return;
}