// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-FileCopyrightText: 2009 Carlo Caione #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lowlevel.h" //https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#BOOT_ORDER static const char * const boot_mode_names[] = { [0x0] = "unknown", [0x1] = "sd", [0x2] = "net", [0x3] = "rpiboot", [0x4] = "usbmsd", [0x5] = "usbc", [0x6] = "nvme", [0x7] = "http", }; struct rpi_priv; struct rpi_machine_data { int (*init)(struct rpi_priv *priv); u8 hw_id; #define RPI_OLD_SCHEMA BIT(0) u8 flags; }; struct rpi_priv { struct device_d *dev; const struct rpi_machine_data *dcfg; unsigned int hw_id; const char *name; }; static void rpi_set_usbethaddr(void) { u8 mac[ETH_ALEN]; if (rpi_get_usbethaddr(mac)) return; /* Ignore error; not critical */ eth_register_ethaddr(0, mac); } static void rpi_set_usbotg(const char *alias) { struct device_node *usb; usb = of_find_node_by_alias(NULL, alias); if (usb) of_property_write_string(usb, "dr_mode", "otg"); } static struct gpio_led rpi_leds[] = { { .gpio = -EINVAL, .led = { .name = "ACT", }, }, { .gpio = -EINVAL, .led = { .name = "PWR", }, }, }; static void rpi_add_led(void) { int i; struct gpio_led *l; for (i = 0; i < ARRAY_SIZE(rpi_leds); i++) { l = &rpi_leds[i]; if (gpio_is_valid(l->gpio)) led_gpio_register(l); } l = &rpi_leds[0]; if (gpio_is_valid(l->gpio)) led_set_trigger(LED_TRIGGER_HEARTBEAT, &l->led); } static int rpi_b_init(struct rpi_priv *priv) { rpi_leds[0].gpio = 16; rpi_leds[0].active_low = 1; rpi_set_usbethaddr(); return 0; } static int rpi_b_plus_init(struct rpi_priv *priv) { rpi_leds[0].gpio = 47; rpi_leds[1].gpio = 35; rpi_set_usbethaddr(); return 0; } static int rpi_0_init(struct rpi_priv *priv) { rpi_leds[0].gpio = 47; rpi_set_usbotg("usb0"); return 0; } static int rpi_0_w_init(struct rpi_priv *priv) { struct device_node *np; int ret; rpi_0_init(priv); np = of_find_node_by_path("/chosen"); if (!np) return -ENODEV; if (!of_device_enable_and_register_by_alias("serial1")) return -ENODEV; ret = of_property_write_string(np, "stdout-path", "serial1:115200n8"); if (ret) return ret; return of_device_disable_by_alias("serial0"); } static int rpi_mem_init(void) { ssize_t size; size = rpi_get_arm_mem(); if (size < 0) { printf("could not query ARM memory size\n"); size = get_ram_size((ulong *) BCM2835_SDRAM_BASE, SZ_128M); } bcm2835_add_device_sdram(size); return 0; } mem_initcall(rpi_mem_init); static int rpi_env_init(void) { struct stat s; const char *diskdev = "/dev/disk0.0"; int ret; device_detect_by_name("mci0"); ret = stat(diskdev, &s); if (ret) { printf("no %s. using default env\n", diskdev); return 0; } mkdir("/boot", 0666); ret = mount(diskdev, "fat", "/boot", NULL); if (ret) { printf("failed to mount %s\n", diskdev); return 0; } defaultenv_append_directory(defaultenv_rpi); default_environment_path_set("/boot/barebox.env"); return 0; } /* Some string properties in fdt passed to us from vc may be * malformed by not being null terminated, so just create and * return a fixed copy. */ static char *of_read_vc_string(struct device_node *node, const char *prop_name) { int len; const char *str; str = of_get_property(node, prop_name, &len); if (!str) { pr_warn("no property '%s' found in vc fdt's '%s' node\n", prop_name, node->full_name); return NULL; } return xstrndup(str, len); } static enum reset_src_type rpi_decode_pm_rsts(struct device_node *chosen, struct device_node *bootloader) { u32 pm_rsts; int ret; ret = of_property_read_u32(chosen, "pm_rsts", &pm_rsts); if (ret && bootloader) ret = of_property_read_u32(bootloader, "rsts", &pm_rsts); if (ret) { pr_warn("'pm_rsts' value not found in vc fdt\n"); return RESET_UKWN; } /* * https://github.com/raspberrypi/linux/issues/932#issuecomment-93989581 */ if (pm_rsts & PM_RSTS_HADPOR_SET) return RESET_POR; if (pm_rsts & PM_RSTS_HADDR_SET) return RESET_JTAG; if (pm_rsts & PM_RSTS_HADWR_SET) return RESET_WDG; if (pm_rsts & PM_RSTS_HADSR_SET) return RESET_RST; return RESET_UKWN; } static u32 rpi_boot_mode, rpi_boot_part; /* Extract useful information from the VideoCore FDT we got. * Some parameters are defined here: * https://www.raspberrypi.com/documentation/computers/configuration.html#part4 */ static void rpi_vc_fdt_parse(void *fdt) { int ret; struct device_node *root, *chosen, *bootloader; char *str; root = of_unflatten_dtb(fdt, INT_MAX); if (IS_ERR(root)) return; str = of_read_vc_string(root, "serial-number"); if (str) { barebox_set_serial_number(str); free(str); } str = of_read_vc_string(root, "model"); if (str) { barebox_set_model(str); free(str); } chosen = of_find_node_by_path_from(root, "/chosen"); if (!chosen) { pr_err("no '/chosen' node found in vc fdt\n"); goto out; } bootloader = of_find_node_by_name(chosen, "bootloader"); str = of_read_vc_string(chosen, "bootargs"); if (str) { globalvar_add_simple("vc.bootargs", str); free(str); } str = of_read_vc_string(chosen, "overlay_prefix"); if (str) { globalvar_add_simple("vc.overlay_prefix", str); free(str); } str = of_read_vc_string(chosen, "os_prefix"); if (str) { globalvar_add_simple("vc.os_prefix", str); free(str); } ret = of_property_read_u32(chosen, "boot-mode", &rpi_boot_mode); if (ret && bootloader) ret = of_property_read_u32(bootloader, "boot-mode", &rpi_boot_mode); if (ret) pr_debug("'boot-mode' property not found in vc fdt\n"); else globalvar_add_simple_enum("vc.boot_mode", &rpi_boot_mode, boot_mode_names, ARRAY_SIZE(boot_mode_names)); ret = of_property_read_u32(chosen, "partition", &rpi_boot_part); if (ret && bootloader) ret = of_property_read_u32(bootloader, "partition", &rpi_boot_part); if (ret) pr_debug("'partition' property not found in vc fdt\n"); else globalvar_add_simple_int("vc.boot_partition", &rpi_boot_part, "%u"); if (IS_ENABLED(CONFIG_RESET_SOURCE)) reset_source_set(rpi_decode_pm_rsts(chosen, bootloader)); out: if (root) of_delete_node(root); return; } static void rpi_vc_fdt(void) { void *saved_vc_fdt; struct fdt_header *oftree; unsigned long magic, size; /* VideoCore FDT was copied in PBL just above Barebox memory */ saved_vc_fdt = (void *)(arm_mem_endmem_get()); oftree = saved_vc_fdt; magic = be32_to_cpu(oftree->magic); if (magic == VIDEOCORE_FDT_ERROR) { if (oftree->totalsize) pr_err("there was an error copying fdt in pbl: %d\n", be32_to_cpu(oftree->totalsize)); return; } if (magic != FDT_MAGIC) return; size = be32_to_cpu(oftree->totalsize); if (write_file("/vc.dtb", saved_vc_fdt, size)) pr_err("failed to save videocore fdt to a file\n"); rpi_vc_fdt_parse(saved_vc_fdt); } static void rpi_set_kernel_name(void) { switch(cpu_architecture()) { case CPU_ARCH_ARMv6: globalvar_add_simple("vc.kernel", "kernel.img"); break; case CPU_ARCH_ARMv7: globalvar_add_simple("vc.kernel", "kernel7.img"); break; case CPU_ARCH_ARMv8: globalvar_add_simple("vc.kernel", "kernel8.img"); break; } } static const struct rpi_machine_data *rpi_get_dcfg(struct rpi_priv *priv) { const struct rpi_machine_data *dcfg; dcfg = of_device_get_match_data(priv->dev); if (!dcfg) { dev_err(priv->dev, "Unknown board. Not applying fixups\n"); return NULL; } /* Comments from u-boot: * For details of old-vs-new scheme, see: * https://github.com/pimoroni/RPi.version/blob/master/RPi/version.py * http://www.raspberrypi.org/forums/viewtopic.php?f=63&t=99293&p=690282 * (a few posts down) * * For the RPi 1, bit 24 is the "warranty bit", so we mask off just the * lower byte to use as the board rev: * http://www.raspberrypi.org/forums/viewtopic.php?f=63&t=98367&start=250 * http://www.raspberrypi.org/forums/viewtopic.php?f=31&t=20594 */ for (; dcfg->hw_id != U8_MAX; dcfg++) { if (priv->hw_id & 0x800000) { if (dcfg->hw_id != ((priv->hw_id >> 4) & 0xff)) continue; } else { if (!(dcfg->flags & RPI_OLD_SCHEMA)) continue; if (dcfg->hw_id != (priv->hw_id & 0xff)) continue; } return dcfg; } dev_err(priv->dev, "dcfg 0x%x for board_id doesn't match DT compatible\n", priv->hw_id); return ERR_PTR(-ENODEV); } static int rpi_devices_probe(struct device_d *dev) { const struct rpi_machine_data *dcfg; struct regulator *reg; struct rpi_priv *priv; const char *name, *ptr; char *hostname; int ret; priv = xzalloc(sizeof(*priv)); priv->dev = dev; ret = rpi_get_board_rev(); if (ret < 0) goto free_priv; priv->hw_id = ret; dcfg = rpi_get_dcfg(priv); if (IS_ERR(dcfg)) goto free_priv; /* construct short recognizable host name */ name = of_device_get_match_compatible(priv->dev); ptr = strchr(name, ','); hostname = basprintf("rpi-%s", ptr ? ptr + 1 : name); barebox_set_hostname(hostname); free(hostname); rpi_add_led(); bcm2835_register_fb(); armlinux_set_architecture(MACH_TYPE_BCM2708); rpi_env_init(); rpi_vc_fdt(); rpi_set_kernel_name(); if (dcfg && dcfg->init) dcfg->init(priv); reg = regulator_get_name("bcm2835_usb"); if (IS_ERR(reg)) return PTR_ERR(reg); regulator_enable(reg); return 0; free_priv: kfree(priv); return ret; } static const struct rpi_machine_data rpi_1_ids[] = { { .hw_id = BCM2835_BOARD_REV_A_7, .flags = RPI_OLD_SCHEMA, }, { .hw_id = BCM2835_BOARD_REV_A_8, .flags = RPI_OLD_SCHEMA, }, { .hw_id = BCM2835_BOARD_REV_A_9, .flags = RPI_OLD_SCHEMA, }, { .hw_id = BCM2835_BOARD_REV_A, }, { .hw_id = BCM2835_BOARD_REV_A_PLUS_12, .flags = RPI_OLD_SCHEMA, }, { .hw_id = BCM2835_BOARD_REV_A_PLUS_15, .flags = RPI_OLD_SCHEMA, }, { .hw_id = BCM2835_BOARD_REV_A_PLUS, }, { .hw_id = BCM2835_BOARD_REV_B_I2C1_4, .flags = RPI_OLD_SCHEMA, }, { .hw_id = BCM2835_BOARD_REV_B_I2C1_5, .flags = RPI_OLD_SCHEMA, }, { .hw_id = BCM2835_BOARD_REV_B_I2C1_6, .flags = RPI_OLD_SCHEMA, }, { .hw_id = BCM2835_BOARD_REV_B, }, { .hw_id = BCM2835_BOARD_REV_B_I2C0_2, .flags = RPI_OLD_SCHEMA, }, { .hw_id = BCM2835_BOARD_REV_B_I2C0_3, .flags = RPI_OLD_SCHEMA, }, { .hw_id = BCM2835_BOARD_REV_B_REV2_d, .flags = RPI_OLD_SCHEMA, .init = rpi_b_init, }, { .hw_id = BCM2835_BOARD_REV_B_REV2_e, .flags = RPI_OLD_SCHEMA, .init = rpi_b_init, }, { .hw_id = BCM2835_BOARD_REV_B_REV2_f, .flags = RPI_OLD_SCHEMA, .init = rpi_b_init, }, { .hw_id = BCM2835_BOARD_REV_B_PLUS_10, .flags = RPI_OLD_SCHEMA, .init = rpi_b_plus_init, }, { .hw_id = BCM2835_BOARD_REV_B_PLUS_13, .flags = RPI_OLD_SCHEMA, .init = rpi_b_plus_init, }, { .hw_id = BCM2835_BOARD_REV_B_PLUS, .init = rpi_b_plus_init, }, { .hw_id = BCM2835_BOARD_REV_CM_11, .flags = RPI_OLD_SCHEMA, }, { .hw_id = BCM2835_BOARD_REV_CM_14, .flags = RPI_OLD_SCHEMA, }, { .hw_id = BCM2835_BOARD_REV_CM1, }, { .hw_id = BCM2835_BOARD_REV_ZERO, .init = rpi_0_init, }, { .hw_id = BCM2835_BOARD_REV_ZERO_W, .init = rpi_0_w_init, }, { .hw_id = U8_MAX }, }; static const struct rpi_machine_data rpi_2_ids[] = { { .hw_id = BCM2836_BOARD_REV_2_B, .init = rpi_b_plus_init, }, { .hw_id = U8_MAX }, }; static const struct rpi_machine_data rpi_3_ids[] = { { .hw_id = BCM2837B0_BOARD_REV_3A_PLUS, .init = rpi_b_plus_init, }, { .hw_id = BCM2837_BOARD_REV_3_B, .init = rpi_b_init, }, { .hw_id = BCM2837B0_BOARD_REV_3B_PLUS, .init = rpi_b_plus_init, }, { .hw_id = BCM2837_BOARD_REV_CM3, }, { .hw_id = BCM2837B0_BOARD_REV_CM3_PLUS, }, { .hw_id = BCM2837B0_BOARD_REV_ZERO_2, }, { .hw_id = U8_MAX }, }; static const struct rpi_machine_data rpi_4_ids[] = { { .hw_id = BCM2711_BOARD_REV_4_B, }, { .hw_id = BCM2711_BOARD_REV_400, }, { .hw_id = BCM2711_BOARD_REV_CM4, }, { .hw_id = U8_MAX }, }; static const struct of_device_id rpi_of_match[] = { /* BCM2835 based Boards */ { .compatible = "raspberrypi,model-a", .data = rpi_1_ids }, { .compatible = "raspberrypi,model-a-plus", .data = rpi_1_ids }, { .compatible = "raspberrypi,model-b", .data = rpi_1_ids }, /* Raspberry Pi Model B (no P5) */ { .compatible = "raspberrypi,model-b-i2c0", .data = rpi_1_ids }, { .compatible = "raspberrypi,model-b-rev2", .data = rpi_1_ids }, { .compatible = "raspberrypi,model-b-plus", .data = rpi_1_ids }, { .compatible = "raspberrypi,compute-module", .data = rpi_1_ids }, { .compatible = "raspberrypi,model-zero", .data = rpi_1_ids }, { .compatible = "raspberrypi,model-zero-w", .data = rpi_1_ids }, /* BCM2836 based Boards */ { .compatible = "raspberrypi,2-model-b", .data = rpi_2_ids }, /* BCM2837 based Boards */ { .compatible = "raspberrypi,3-model-a-plus", .data = rpi_3_ids }, { .compatible = "raspberrypi,3-model-b", .data = rpi_3_ids }, { .compatible = "raspberrypi,3-model-b-plus", .data = rpi_3_ids }, { .compatible = "raspberrypi,model-zero-2-w", .data = rpi_3_ids }, { .compatible = "raspberrypi,3-compute-module", .data = rpi_3_ids }, { .compatible = "raspberrypi,3-compute-module-lite", .data = rpi_3_ids }, /* BCM2711 based Boards */ { .compatible = "raspberrypi,4-model-b", .data = rpi_4_ids }, { .compatible = "raspberrypi,4-compute-module", .data = rpi_4_ids }, { .compatible = "raspberrypi,400", .data = rpi_4_ids }, { /* sentinel */ }, }; BAREBOX_DEEP_PROBE_ENABLE(rpi_of_match); static struct driver_d rpi_board_driver = { .name = "board-rpi", .probe = rpi_devices_probe, .of_compatible = DRV_OF_COMPAT(rpi_of_match), }; late_platform_driver(rpi_board_driver);