// SPDX-License-Identifier: GPL-2.0+ /* * (C) Copyright 2019 * Kontron Asia Pacific Design * (C) Copyright 2020 * Thomas Schaefer, Kontron Europe GmbH * thomas.schaefer@kontron.com */ #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 #include #include "pitx-imx8m.h" #include "../common/emb_eep.h" extern int EMB_EEP_I2C_EEPROM_BUS_NUM_1; DECLARE_GLOBAL_DATA_PTR; static const char *hw_variants[8] = { "unknown", "unknown", "unknown", "L130", "L140", "L150", "L160", "L120" }; #define QSPI_PAD_CTRL (PAD_CTL_DSE2 | PAD_CTL_HYS) #define UART_PAD_CTRL (PAD_CTL_DSE6 | PAD_CTL_FSEL1) #define WDOG_PAD_CTRL (PAD_CTL_DSE6 | PAD_CTL_HYS | PAD_CTL_PUE) static iomux_v3_cfg_t const wdog_pads[] = { IMX8MQ_PAD_GPIO1_IO02__WDOG1_WDOG_B | MUX_PAD_CTRL(WDOG_PAD_CTRL), }; #ifdef CONFIG_FSL_QSPI static iomux_v3_cfg_t const qspi_pads[] = { IMX8MQ_PAD_NAND_ALE__QSPI_A_SCLK | MUX_PAD_CTRL(QSPI_PAD_CTRL), IMX8MQ_PAD_NAND_CE0_B__QSPI_A_SS0_B | MUX_PAD_CTRL(QSPI_PAD_CTRL), IMX8MQ_PAD_NAND_DATA00__QSPI_A_DATA0 | MUX_PAD_CTRL(QSPI_PAD_CTRL), IMX8MQ_PAD_NAND_DATA01__QSPI_A_DATA1 | MUX_PAD_CTRL(QSPI_PAD_CTRL), IMX8MQ_PAD_NAND_DATA02__QSPI_A_DATA2 | MUX_PAD_CTRL(QSPI_PAD_CTRL), IMX8MQ_PAD_NAND_DATA03__QSPI_A_DATA3 | MUX_PAD_CTRL(QSPI_PAD_CTRL), }; int board_qspi_init(void) { imx_iomux_v3_setup_multiple_pads(qspi_pads, ARRAY_SIZE(qspi_pads)); set_clk_qspi(); return 0; } #endif static iomux_v3_cfg_t const uart_pads[] = { IMX8MQ_PAD_UART3_RXD__UART3_RX | MUX_PAD_CTRL(UART_PAD_CTRL), IMX8MQ_PAD_UART3_TXD__UART3_TX | MUX_PAD_CTRL(UART_PAD_CTRL), IMX8MQ_PAD_ECSPI1_SS0__UART3_RTS_B | MUX_PAD_CTRL(UART_PAD_CTRL), IMX8MQ_PAD_ECSPI1_MISO__UART3_CTS_B | MUX_PAD_CTRL(UART_PAD_CTRL), }; int board_early_init_f(void) { struct wdog_regs *wdog = (struct wdog_regs *)WDOG1_BASE_ADDR; imx_iomux_v3_setup_multiple_pads(wdog_pads, ARRAY_SIZE(wdog_pads)); set_wdog_reset(wdog); imx_iomux_v3_setup_multiple_pads(uart_pads, ARRAY_SIZE(uart_pads)); return 0; } #ifdef CONFIG_BOARD_POSTCLK_INIT int board_postclk_init(void) { /* TODO */ return 0; } #endif #ifdef CONFIG_OF_BOARD_SETUP int ft_board_setup(void *blob, bd_t *bd) { return 0; } #endif /* Get the top of usable RAM */ ulong board_get_usable_ram_top(ulong total_size) { /*printf("board_get_usable_ram_top total_size is 0x%lx \n", total_size);*/ if(gd->ram_top > 0x100000000) gd->ram_top = 0x100000000; return gd->ram_top; } #ifdef CONFIG_FEC_MXC #define FEC_RST_PAD IMX_GPIO_NR(1, 11) static iomux_v3_cfg_t const fec1_rst_pads[] = { IMX8MQ_PAD_GPIO1_IO11__GPIO1_IO11 | MUX_PAD_CTRL(NO_PAD_CTRL), }; static void setup_iomux_fec(void) { imx_iomux_v3_setup_multiple_pads(fec1_rst_pads, ARRAY_SIZE(fec1_rst_pads)); // gpio_request(IMX_GPIO_NR(1, 11), "fec1_rst"); // gpio_direction_output(IMX_GPIO_NR(1, 11), 0); // udelay(500); // gpio_direction_output(IMX_GPIO_NR(1, 11), 1); } static int setup_fec(void) { struct iomuxc_gpr_base_regs *gpr = (struct iomuxc_gpr_base_regs *) IOMUXC_GPR_BASE_ADDR; setup_iomux_fec(); /* Use 125M anatop REF_CLK1 for ENET1, not from external */ clrsetbits_le32(&gpr->gpr[1], IOMUXC_GPR_GPR1_GPR_ENET1_TX_CLK_SEL_MASK, 0); return set_clk_enet(ENET_125MHZ); } int board_phy_config(struct phy_device *phydev) { unsigned int val; /* enable rgmii rxc skew and phy mode select to RGMII copper */ phy_write(phydev, MDIO_DEVAD_NONE, 0x1d, 0x1f); phy_write(phydev, MDIO_DEVAD_NONE, 0x1e, 0x8); phy_write(phydev, MDIO_DEVAD_NONE, 0x1d, 0x05); phy_write(phydev, MDIO_DEVAD_NONE, 0x1e, 0x100); /* * Set LED configuration register 1: * LED2_SEL: 0b1011 (link established, blink on activity) */ val = phy_read(phydev, MDIO_DEVAD_NONE, 0x18); val &= 0xf0ff; phy_write(phydev, MDIO_DEVAD_NONE, 0x18, val | (0xb<<8)); if (phydev->drv->config) phydev->drv->config(phydev); return 0; } #endif #ifdef CONFIG_USB_DWC3 #define USB_PHY_CTRL0 0xF0040 #define USB_PHY_CTRL0_REF_SSP_EN BIT(2) #define USB_PHY_CTRL1 0xF0044 #define USB_PHY_CTRL1_RESET BIT(0) #define USB_PHY_CTRL1_COMMONONN BIT(1) #define USB_PHY_CTRL1_ATERESET BIT(3) #define USB_PHY_CTRL1_VDATSRCENB0 BIT(19) #define USB_PHY_CTRL1_VDATDETENB0 BIT(20) #define USB_PHY_CTRL2 0xF0048 #define USB_PHY_CTRL2_TXENABLEN0 BIT(8) static struct dwc3_device dwc3_device_data = { #ifdef CONFIG_SPL_BUILD .maximum_speed = USB_SPEED_HIGH, #else .maximum_speed = USB_SPEED_SUPER, #endif .base = USB1_BASE_ADDR, .dr_mode = USB_DR_MODE_PERIPHERAL, .index = 0, .power_down_scale = 2, }; int usb_gadget_handle_interrupts(void) { dwc3_uboot_handle_interrupt(0); return 0; } static void dwc3_nxp_usb_phy_init(struct dwc3_device *dwc3) { u32 RegData; RegData = readl(dwc3->base + USB_PHY_CTRL1); RegData &= ~(USB_PHY_CTRL1_VDATSRCENB0 | USB_PHY_CTRL1_VDATDETENB0 | USB_PHY_CTRL1_COMMONONN); RegData |= USB_PHY_CTRL1_RESET | USB_PHY_CTRL1_ATERESET; writel(RegData, dwc3->base + USB_PHY_CTRL1); RegData = readl(dwc3->base + USB_PHY_CTRL0); RegData |= USB_PHY_CTRL0_REF_SSP_EN; writel(RegData, dwc3->base + USB_PHY_CTRL0); RegData = readl(dwc3->base + USB_PHY_CTRL2); RegData |= USB_PHY_CTRL2_TXENABLEN0; writel(RegData, dwc3->base + USB_PHY_CTRL2); RegData = readl(dwc3->base + USB_PHY_CTRL1); RegData &= ~(USB_PHY_CTRL1_RESET | USB_PHY_CTRL1_ATERESET); writel(RegData, dwc3->base + USB_PHY_CTRL1); } #endif #ifdef CONFIG_USB_TCPC struct tcpc_port port; struct tcpc_port_config port_config = { .i2c_bus = 0, .addr = 0x50, .port_type = TYPEC_PORT_UFP, .max_snk_mv = 20000, .max_snk_ma = 3000, .max_snk_mw = 15000, .op_snk_mv = 9000, }; #define USB_TYPEC_SEL IMX_GPIO_NR(3, 15) static iomux_v3_cfg_t ss_mux_gpio[] = { IMX8MQ_PAD_NAND_RE_B__GPIO3_IO15 | MUX_PAD_CTRL(NO_PAD_CTRL), }; void ss_mux_select(enum typec_cc_polarity pol) { if (pol == TYPEC_POLARITY_CC1) gpio_direction_output(USB_TYPEC_SEL, 1); else gpio_direction_output(USB_TYPEC_SEL, 0); } static int setup_typec(void) { int ret; imx_iomux_v3_setup_multiple_pads(ss_mux_gpio, ARRAY_SIZE(ss_mux_gpio)); gpio_request(USB_TYPEC_SEL, "typec_sel"); ret = tcpc_init(&port, port_config, &ss_mux_select); if (ret) { printf("%s: tcpc init failed, err=%d\n", __func__, ret); } return ret; } #endif #if defined(CONFIG_USB_DWC3) || defined(CONFIG_USB_XHCI_IMX8M) static iomux_v3_cfg_t usbhub_rst[] = { IMX8MQ_PAD_NAND_CE3_B__GPIO3_IO4 | MUX_PAD_CTRL(NO_PAD_CTRL), }; int board_usb_init(int index, enum usb_init_type init) { int ret = 0; imx_iomux_v3_setup_multiple_pads(usbhub_rst, ARRAY_SIZE(usbhub_rst)); imx8m_usb_power(index, true); if (index == 0 && init == USB_INIT_DEVICE) { #ifdef CONFIG_USB_TCPC ret = tcpc_setup_ufp_mode(&port); #endif dwc3_nxp_usb_phy_init(&dwc3_device_data); return dwc3_uboot_init(&dwc3_device_data); } else if (index == 0 && init == USB_INIT_HOST) { #ifdef CONFIG_USB_TCPC ret = tcpc_setup_dfp_mode(&port); #endif return ret; } return 0; } int board_usb_cleanup(int index, enum usb_init_type init) { int ret = 0; if (index == 0 && init == USB_INIT_DEVICE) { dwc3_uboot_exit(index); } else if (index == 0 && init == USB_INIT_HOST) { #ifdef CONFIG_USB_TCPC ret = tcpc_disable_src_vbus(&port); #endif } imx8m_usb_power(index, false); return ret; } #endif #ifdef CONFIG_CMD_KBOARDINFO char *getSerNo (void) { struct env_entry e; static struct env_entry *ep; e.key = "board_serial"; e.data = NULL; hsearch_r (e, ENV_FIND, &ep, &env_htab, 0); if (ep == NULL) return "na"; else return ep->data; } /* try to fetch identnumber */ char *getSapId (int eeprom_num) { return (emb_eep_find_string_in_dmi(eeprom_num, 2, 5)); } char *getManufacturer (int eeprom_num) { return (emb_eep_find_string_in_dmi(eeprom_num, 2, 1)); } char *getProductName (int eeprom_num) { return (emb_eep_find_string_in_dmi(eeprom_num, 2, 2)); } char *getManufacturerDate (int eeprom_num) { return (emb_eep_find_string_in_dmi(eeprom_num, 160, 2)); } char *getRevision (int eeprom_num) { return (emb_eep_find_string_in_dmi(eeprom_num, 2, 3)); } char *getMacAddress (int eeprom_num, int eth_num) { char *macaddress; macaddress = emb_eep_find_mac_in_dmi(eeprom_num, eth_num); if (macaddress != NULL) return (macaddress); else return "na"; } uint64_t getBootCounter (int eeprom_num) { uint64_t bc; char *tmp; tmp = emb_eep_find_string_in_dmi(eeprom_num, 161, 1); if (tmp != NULL) { memcpy(&bc, tmp, sizeof(uint64_t)); return (be64_to_cpu(bc)); } else return (-1ULL); } #endif // CONFIG_CMD_KBOARDINFO static int get_hw_rev(void) { return ((gpio_get_value(IMX_GPIO_NR(5,0)) | gpio_get_value(IMX_GPIO_NR(5,1)) << 1 | gpio_get_value(IMX_GPIO_NR(5,2)) << 2 ) & 0x7 ); } #define BRD_REV_PAD_CTRL (PAD_CTL_PUE | PAD_CTL_FSEL2 | PAD_CTL_DSE7) static iomux_v3_cfg_t const gpio_pads[] = { IMX8MQ_PAD_NAND_CLE__GPIO3_IO5 | MUX_PAD_CTRL(NO_PAD_CTRL), IMX8MQ_PAD_NAND_RE_B__GPIO3_IO15 | MUX_PAD_CTRL(NO_PAD_CTRL), IMX8MQ_PAD_NAND_WE_B__GPIO3_IO17 | MUX_PAD_CTRL(NO_PAD_CTRL), IMX8MQ_PAD_NAND_WP_B__GPIO3_IO18 | MUX_PAD_CTRL(NO_PAD_CTRL), IMX8MQ_PAD_NAND_READY_B__GPIO3_IO16 | MUX_PAD_CTRL(NO_PAD_CTRL), IMX8MQ_PAD_NAND_DATA04__GPIO3_IO10 | MUX_PAD_CTRL(NO_PAD_CTRL), IMX8MQ_PAD_NAND_DATA05__GPIO3_IO11 | MUX_PAD_CTRL(NO_PAD_CTRL), IMX8MQ_PAD_NAND_DATA06__GPIO3_IO12 | MUX_PAD_CTRL(NO_PAD_CTRL), IMX8MQ_PAD_ECSPI1_SCLK__GPIO5_IO6 | MUX_PAD_CTRL(NO_PAD_CTRL), IMX8MQ_PAD_SAI5_RXC__GPIO3_IO20 | MUX_PAD_CTRL(NO_PAD_CTRL), IMX8MQ_PAD_GPIO1_IO05__GPIO1_IO5 | MUX_PAD_CTRL(NO_PAD_CTRL), IMX8MQ_PAD_GPIO1_IO10__GPIO1_IO10 | MUX_PAD_CTRL(NO_PAD_CTRL), IMX8MQ_PAD_SAI3_TXC__GPIO5_IO0 | MUX_PAD_CTRL(BRD_REV_PAD_CTRL), IMX8MQ_PAD_SAI3_TXD__GPIO5_IO1 | MUX_PAD_CTRL(BRD_REV_PAD_CTRL), IMX8MQ_PAD_SAI3_MCLK__GPIO5_IO2 | MUX_PAD_CTRL(BRD_REV_PAD_CTRL), }; int board_gpio_init(void) { imx_iomux_v3_setup_multiple_pads(gpio_pads, ARRAY_SIZE(gpio_pads)); gpio_request(IMX_GPIO_NR(3, 5), "GPIO0"); gpio_direction_output(IMX_GPIO_NR(3, 5), 0); /*GPIO0*/ gpio_request(IMX_GPIO_NR(3, 15), "GPIO1"); gpio_direction_output(IMX_GPIO_NR(3, 15), 0); /*GPIO1*/ gpio_request(IMX_GPIO_NR(3, 17), "GPIO2"); gpio_direction_output(IMX_GPIO_NR(3, 17), 0); /*GPIO2*/ gpio_request(IMX_GPIO_NR(3, 18), "GPIO3"); gpio_direction_output(IMX_GPIO_NR(3, 18), 0); /*GPIO3*/ gpio_request(IMX_GPIO_NR(3, 16), "GPIO4"); gpio_direction_output(IMX_GPIO_NR(3, 16), 0); /*GPIO4*/ gpio_request(IMX_GPIO_NR(3, 10), "GPIO5"); gpio_direction_output(IMX_GPIO_NR(3, 10), 0); /*GPIO5*/ gpio_request(IMX_GPIO_NR(3, 11), "GPIO6"); gpio_direction_output(IMX_GPIO_NR(3, 11), 0); /*GPIO6*/ gpio_request(IMX_GPIO_NR(3, 12), "GPIO7"); gpio_direction_output(IMX_GPIO_NR(3, 12), 0); /*GPIO7*/ gpio_request(IMX_GPIO_NR(5, 6), "LVDS_VDD_EN"); gpio_direction_output(IMX_GPIO_NR(5, 6), 1); /*LVDS_VDD_EN*/ gpio_request(IMX_GPIO_NR(3, 20), "DSI_RST#"); gpio_direction_output(IMX_GPIO_NR(3, 20), 0); /*DSI_RST#*/ gpio_request(IMX_GPIO_NR(1, 5), "CAM_RST#"); gpio_direction_output(IMX_GPIO_NR(1, 5), 1); /*CAM0_RST#*/ gpio_request(IMX_GPIO_NR(1, 10), "GBE0_PWDN#"); gpio_direction_output(IMX_GPIO_NR(1, 10), 1); /*GBE0_PWDN*/ gpio_request(IMX_GPIO_NR(5, 0), "BRD_REV0"); gpio_direction_input(IMX_GPIO_NR(5, 0)); /* BRD_REV[0] */ gpio_request(IMX_GPIO_NR(5, 1), "BRD_REV1"); gpio_direction_input(IMX_GPIO_NR(5, 1)); /* BRD_REV[1] */ gpio_request(IMX_GPIO_NR(5, 2), "BRD_REV2"); gpio_direction_input(IMX_GPIO_NR(5, 2)); /* BRD_REV[2] */ return 0; } int board_init(void) { board_qspi_init(); #ifdef CONFIG_FEC_MXC setup_fec(); #endif #if defined(CONFIG_USB_DWC3) || defined(CONFIG_USB_XHCI_IMX8M) init_usb_clk(); #endif #ifdef CONFIG_USB_TCPC setup_typec(); #endif return 0; } int board_mmc_get_env_dev(int devno) { return devno; } #define TPM_RESET IMX_GPIO_NR(3, 2) #define USBHUB_RESET IMX_GPIO_NR(3, 4) int misc_init_r(void) { int hw_rev; #ifdef CONFIG_EMB_EEP_SPI emb_vpd_init_r(); #endif #ifdef CONFIG_EMB_EEP_I2C_EEPROM EMB_EEP_I2C_EEPROM_BUS_NUM_1 = CONFIG_EMB_EEP_I2C_EEPROM_BUS_NUM_EE1; emb_eep_init_r (1, 1, 1); /* import 1 MAC address */ /*emb_eep_init_r (1, 1, 2); */ /* import 2 MAC address, will try it once the ETH1 UP */ #endif board_gpio_init(); /* * reset TPM chip (Infineon SLB9670) as required by datasheet * (60ms minimum Reset Inactive Time, 70ms implemented) */ gpio_request(TPM_RESET, "tpm_reset"); gpio_direction_output(TPM_RESET, 0); udelay(70000); gpio_direction_output(TPM_RESET, 1); /* * reset USB hub as required by datasheet * (3ms minimum reset duration, 10ms implemented) */ gpio_request(USBHUB_RESET, "usbhub_reset"); gpio_direction_output(USBHUB_RESET, 0); udelay(10000); gpio_direction_output(USBHUB_RESET, 1); hw_rev = get_hw_rev(); /* check if hw variant is L120 (7) */ if (hw_rev == 7) { int nodeoffset; void *fdt = map_sysmem((ulong)gd->fdt_blob, 0); imx_iomux_v3_setup_pad(IMX8MQ_PAD_ECSPI1_MISO__GPIO5_IO8 | MUX_PAD_CTRL(UART_PAD_CTRL)); imx_iomux_v3_setup_pad(IMX8MQ_PAD_ECSPI1_SS0__GPIO5_IO9 | MUX_PAD_CTRL(UART_PAD_CTRL | PAD_CTL_PUE)); gpio_request(IMX_GPIO_NR(5, 8), "UART3_CTS#"); gpio_direction_output(IMX_GPIO_NR(5, 8), 0); gpio_request(IMX_GPIO_NR(5, 9), "UART3_RTS#"); gpio_direction_input(IMX_GPIO_NR(5, 9)); /* * Set SION for GPIO1_IO12 pad. This ensures that the pad * cannot be put into GPIO output mode by the CLI 'gpio' * command causing TPS2062 OUT2 to drive against * V_5V0_USB1_OTG_VBUS voltage line from external host. * * In addition, usb0 port is removed from the u-boot device * tree. This ensures that SION setting is not overwritten * when 'usb start' is invoked. */ imx_iomux_v3_setup_pad(IMX8MQ_PAD_GPIO1_IO12__GPIO1_IO12 | MUX_PAD_CTRL(NO_PAD_CTRL) | MUX_MODE_SION); /* * Remove USB OTG port (usb0) from the device tree as we cannot * use it in host mode on rev2 modules. However, using the ums * command is still possible. */ nodeoffset = fdt_path_offset (fdt, "usb0"); if (nodeoffset < 0) { printf("libfdt path 'usb0' not found," " cannot remove usb0 node\n"); return 0; /* No need to return an error code here */ } if (!fdt_del_node (fdt, nodeoffset)) printf("Rev2 module: removing USB OTG port from DT\n"); } return 0; } static enum boot_source pitx_imx8m_boot_source(void) { enum boot_device bdev = get_boot_device(); switch(bdev) { case MMC1_BOOT: case MMC2_BOOT: case MMC3_BOOT: case MMC4_BOOT: /* * USDHC port 1-4 can be used to * attach an eMMC device. */ return BOOT_SOURCE_MMC; case SD1_BOOT: case SD2_BOOT: case SD3_BOOT: case SD4_BOOT: /* * USDHC port 1-4 can be used to * attach an SD card device. */ return BOOT_SOURCE_SDHC; default: printf("Unknown bootsource %d\n", bdev); return BOOT_SOURCE_UNKNOWN; }; } int board_late_init(void) { int hw_rev = get_hw_rev(); env_set("hw_rev", hw_variants[hw_rev]); env_set_ulong("bootsource", pitx_imx8m_boot_source()); #ifdef CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG env_set("board_name", "PITX-iMX8M"); env_set("board_rev", "iMX8MQ"); #endif #ifdef CONFIG_ENV_IS_IN_MMC board_late_mmc_env_init(); #endif #ifndef CONFIG_SPL_BUILD #if defined(CONFIG_KEX_EEP_BOOTCOUNTER) emb_eep_update_bootcounter(1); #endif #endif return 0; } #ifdef CONFIG_FSL_FASTBOOT #ifdef CONFIG_ANDROID_RECOVERY int is_recovery_key_pressing(void) { return 0; /*TODO*/ } #endif /*CONFIG_ANDROID_RECOVERY*/ #endif /*CONFIG_FSL_FASTBOOT*/ #if defined(CONFIG_VIDEO_IMXDCSS) struct display_info_t const displays[] = {{ .bus = 0, /* Unused */ .addr = 0, /* Unused */ .pixfmt = GDF_32BIT_X888RGB, .detect = NULL, .enable = NULL, #ifndef CONFIG_VIDEO_IMXDCSS_1080P .mode = { .name = "HDMI", /* 720P60 */ .refresh = 60, .xres = 1280, .yres = 720, .pixclock = 13468, /* 74250 kHz */ .left_margin = 110, .right_margin = 220, .upper_margin = 5, .lower_margin = 20, .hsync_len = 40, .vsync_len = 5, .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, .vmode = FB_VMODE_NONINTERLACED } #else .mode = { .name = "HDMI", /* 1080P60 */ .refresh = 60, .xres = 1920, .yres = 1080, .pixclock = 6734, /* 148500 kHz */ .left_margin = 148, .right_margin = 88, .upper_margin = 36, .lower_margin = 4, .hsync_len = 44, .vsync_len = 5, .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, .vmode = FB_VMODE_NONINTERLACED } #endif } }; size_t display_count = ARRAY_SIZE(displays); #endif /* CONFIG_VIDEO_IMXDCSS */ /* return hard code board id for imx8m_ref */ #if defined(CONFIG_ANDROID_THINGS_SUPPORT) && defined(CONFIG_ARCH_IMX8M) int get_imx8m_baseboard_id(void) { return IMX8M_REF_3G; } #endif