diff options
Diffstat (limited to 'arch/arm/boards/skov-imx6/board.c')
-rw-r--r-- | arch/arm/boards/skov-imx6/board.c | 328 |
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; } |