2021-11-23 16:33:30 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0+
|
|
|
|
/*
|
|
|
|
* Copyright 2021 Collabora Ltd.
|
2023-05-25 21:02:25 +00:00
|
|
|
* Copyright 2018-2020 Variscite Ltd.
|
|
|
|
* Copyright 2023 DimOnOff Inc.
|
2021-11-23 16:33:30 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <common.h>
|
2023-05-25 21:02:25 +00:00
|
|
|
#include <dm.h>
|
2021-11-23 16:33:30 +00:00
|
|
|
#include <env.h>
|
2023-05-25 21:02:25 +00:00
|
|
|
#include <fdtdec.h>
|
|
|
|
#include <fdt_support.h>
|
|
|
|
#include <i2c_eeprom.h>
|
|
|
|
#include <malloc.h>
|
2021-11-23 16:33:30 +00:00
|
|
|
#include <asm/io.h>
|
2023-05-25 21:02:25 +00:00
|
|
|
#include <asm/global_data.h>
|
2023-05-25 21:02:29 +00:00
|
|
|
#include <dt-bindings/gpio/gpio.h>
|
2023-05-25 21:02:25 +00:00
|
|
|
#include <linux/libfdt.h>
|
|
|
|
|
|
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
|
|
|
|
|
|
/* Optional SOM features flags. */
|
|
|
|
#define VAR_EEPROM_F_WIFI BIT(0)
|
|
|
|
#define VAR_EEPROM_F_ETH BIT(1) /* Ethernet PHY on SOM. */
|
|
|
|
#define VAR_EEPROM_F_AUDIO BIT(2)
|
|
|
|
#define VAR_EEPROM_F_MX8M_LVDS BIT(3) /* i.MX8MM, i.MX8MN, i.MX8MQ only */
|
|
|
|
#define VAR_EEPROM_F_MX8Q_SOC_ID BIT(3) /* 0 = i.MX8QM, 1 = i.MX8QP */
|
|
|
|
#define VAR_EEPROM_F_NAND BIT(4)
|
|
|
|
|
|
|
|
#define VAR_IMX8_EEPROM_MAGIC 0x384D /* "8M" */
|
|
|
|
|
|
|
|
/* Number of DRAM adjustment tables. */
|
|
|
|
#define DRAM_TABLES_NUM 7
|
|
|
|
|
|
|
|
struct var_imx8_eeprom_info {
|
|
|
|
u16 magic;
|
|
|
|
u8 partnumber[3]; /* Part number */
|
|
|
|
u8 assembly[10]; /* Assembly number */
|
|
|
|
u8 date[9]; /* Build date */
|
|
|
|
u8 mac[6]; /* MAC address */
|
|
|
|
u8 somrev;
|
|
|
|
u8 eeprom_version;
|
|
|
|
u8 features; /* SOM features */
|
|
|
|
u8 dramsize; /* DRAM size */
|
|
|
|
u8 off[DRAM_TABLES_NUM + 1]; /* DRAM table offsets */
|
|
|
|
u8 partnumber2[5]; /* Part number 2 */
|
|
|
|
} __packed;
|
2021-11-23 16:33:30 +00:00
|
|
|
|
|
|
|
static void setup_fec(void)
|
|
|
|
{
|
|
|
|
struct iomuxc_gpr_base_regs *gpr =
|
|
|
|
(struct iomuxc_gpr_base_regs *)IOMUXC_GPR_BASE_ADDR;
|
|
|
|
|
|
|
|
/* Use 125M anatop REF_CLK1 for ENET1, not from external */
|
|
|
|
clrsetbits_le32(&gpr->gpr[1], 0x2000, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int board_init(void)
|
|
|
|
{
|
2023-02-06 00:54:16 +00:00
|
|
|
if (IS_ENABLED(CONFIG_FEC_MXC))
|
2021-11-23 16:33:30 +00:00
|
|
|
setup_fec();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int board_mmc_get_env_dev(int devno)
|
|
|
|
{
|
|
|
|
return devno;
|
|
|
|
}
|
2023-05-25 21:02:25 +00:00
|
|
|
|
|
|
|
#if !defined(CONFIG_SPL_BUILD)
|
|
|
|
|
|
|
|
#if defined(CONFIG_DISPLAY_BOARDINFO)
|
|
|
|
|
|
|
|
static void display_som_infos(struct var_imx8_eeprom_info *info)
|
|
|
|
{
|
|
|
|
char partnumber[sizeof(info->partnumber) +
|
|
|
|
sizeof(info->partnumber2) + 1];
|
|
|
|
char assembly[sizeof(info->assembly) + 1];
|
|
|
|
char date[sizeof(info->date) + 1];
|
|
|
|
|
|
|
|
/* Read first part of P/N. */
|
|
|
|
memcpy(partnumber, info->partnumber, sizeof(info->partnumber));
|
|
|
|
|
|
|
|
/* Read second part of P/N. */
|
|
|
|
if (info->eeprom_version >= 3)
|
|
|
|
memcpy(partnumber + sizeof(info->partnumber), info->partnumber2,
|
|
|
|
sizeof(info->partnumber2));
|
|
|
|
|
|
|
|
memcpy(assembly, info->assembly, sizeof(info->assembly));
|
|
|
|
memcpy(date, info->date, sizeof(info->date));
|
|
|
|
|
|
|
|
/* Make sure strings are null terminated. */
|
|
|
|
partnumber[sizeof(partnumber) - 1] = '\0';
|
|
|
|
assembly[sizeof(assembly) - 1] = '\0';
|
|
|
|
date[sizeof(date) - 1] = '\0';
|
|
|
|
|
|
|
|
printf("SOM board: P/N: %s, Assy: %s, Date: %s\n"
|
|
|
|
" Wifi: %s, EthPhy: %s, Rev: %d\n",
|
|
|
|
partnumber, assembly, date,
|
|
|
|
info->features & VAR_EEPROM_F_WIFI ? "yes" : "no",
|
|
|
|
info->features & VAR_EEPROM_F_ETH ? "yes" : "no",
|
|
|
|
info->somrev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int var_read_som_eeprom(struct var_imx8_eeprom_info *info)
|
|
|
|
{
|
|
|
|
const char *path = "eeprom-som";
|
|
|
|
struct udevice *dev;
|
|
|
|
int ret, off;
|
|
|
|
|
|
|
|
off = fdt_path_offset(gd->fdt_blob, path);
|
|
|
|
if (off < 0) {
|
|
|
|
pr_err("%s: fdt_path_offset() failed: %d\n", __func__, off);
|
|
|
|
return off;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = uclass_get_device_by_of_offset(UCLASS_I2C_EEPROM, off, &dev);
|
|
|
|
if (ret) {
|
|
|
|
pr_err("%s: uclass_get_device_by_of_offset() failed: %d\n",
|
|
|
|
__func__, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = i2c_eeprom_read(dev, 0, (uint8_t *)info,
|
|
|
|
sizeof(struct var_imx8_eeprom_info));
|
|
|
|
if (ret) {
|
|
|
|
pr_err("%s: i2c_eeprom_read() failed: %d\n", __func__, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (htons(info->magic) != VAR_IMX8_EEPROM_MAGIC) {
|
|
|
|
/* Do not fail if the content is invalid */
|
|
|
|
pr_err("Board: Invalid board info magic: 0x%08x, expected 0x%08x\n",
|
|
|
|
htons(info->magic), VAR_IMX8_EEPROM_MAGIC);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int checkboard(void)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
struct var_imx8_eeprom_info *info;
|
|
|
|
|
|
|
|
info = malloc(sizeof(struct var_imx8_eeprom_info));
|
|
|
|
if (!info)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
rc = var_read_som_eeprom(info);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
display_som_infos(info);
|
|
|
|
|
|
|
|
#if defined(CONFIG_BOARD_TYPES)
|
|
|
|
gd->board_type = info->features;
|
|
|
|
#endif /* CONFIG_BOARD_TYPES */
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* CONFIG_DISPLAY_BOARDINFO */
|
|
|
|
|
2023-05-25 21:02:29 +00:00
|
|
|
static int insert_gpios_prop(void *blob, int node, const char *prop,
|
|
|
|
unsigned int phandle, u32 gpio, u32 flags)
|
|
|
|
{
|
|
|
|
fdt32_t val[3] = { cpu_to_fdt32(phandle), cpu_to_fdt32(gpio),
|
|
|
|
cpu_to_fdt32(flags) };
|
|
|
|
return fdt_setprop(blob, node, prop, &val, sizeof(val));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int configure_phy_reset_gpios(void *blob)
|
|
|
|
{
|
|
|
|
int node;
|
|
|
|
int phynode;
|
|
|
|
int ret;
|
|
|
|
u32 handle;
|
|
|
|
u32 gpio;
|
|
|
|
u32 flags;
|
|
|
|
char path[1024];
|
|
|
|
const char *eth_alias = "ethernet0";
|
|
|
|
|
|
|
|
snprintf(path, sizeof(path), "%s/mdio/ethernet-phy@4",
|
|
|
|
fdt_get_alias(blob, eth_alias));
|
|
|
|
|
|
|
|
phynode = fdt_path_offset(blob, path);
|
|
|
|
if (phynode < 0) {
|
|
|
|
pr_err("%s(): unable to locate PHY node: %s\n", __func__, path);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gd_board_type() & VAR_EEPROM_F_ETH) {
|
|
|
|
snprintf(path, sizeof(path), "%s",
|
|
|
|
fdt_get_alias(blob, "gpio0")); /* Alias to gpio1 */
|
|
|
|
gpio = 9;
|
|
|
|
flags = GPIO_ACTIVE_LOW;
|
|
|
|
} else {
|
|
|
|
snprintf(path, sizeof(path), "%s/gpio@20",
|
|
|
|
fdt_get_alias(blob, "i2c1")); /* Alias to i2c2 */
|
|
|
|
gpio = 5;
|
|
|
|
flags = GPIO_ACTIVE_HIGH;
|
|
|
|
}
|
|
|
|
|
|
|
|
node = fdt_path_offset(blob, path);
|
|
|
|
if (node < 0) {
|
|
|
|
pr_err("%s(): unable to locate GPIO node: %s\n", __func__,
|
|
|
|
path);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
handle = fdt_get_phandle(blob, node);
|
|
|
|
if (handle < 0) {
|
|
|
|
pr_err("%s(): unable to locate GPIO controller handle: %s\n",
|
|
|
|
__func__, path);
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = insert_gpios_prop(blob, phynode, "reset-gpios",
|
|
|
|
handle, gpio, flags);
|
|
|
|
if (ret < 0) {
|
|
|
|
pr_err("%s(): failed to set reset-gpios property\n", __func__);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(CONFIG_OF_BOARD_FIXUP)
|
|
|
|
int board_fix_fdt(void *blob)
|
|
|
|
{
|
|
|
|
/* Fix U-Boot device tree: */
|
|
|
|
return configure_phy_reset_gpios(blob);
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_OF_BOARD_FIXUP */
|
|
|
|
|
|
|
|
#if defined(CONFIG_OF_BOARD_SETUP)
|
|
|
|
int ft_board_setup(void *blob, struct bd_info *bd)
|
|
|
|
{
|
|
|
|
/* Fix kernel device tree: */
|
|
|
|
return configure_phy_reset_gpios(blob);
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_OF_BOARD_SETUP */
|
|
|
|
|
2023-05-25 21:02:25 +00:00
|
|
|
#endif /* CONFIG_SPL_BUILD */
|