u-boot/board/dhelectronics/dh_imx8mp/imx8mp_dhcom_pdk2.c
Marek Vasut ef7ceb3ec7 arm64: imx8mp: Auto-detect PHY on i.MX8MP DHCOM
The i.MX8MP DHCOM SoM may be populated with either KSZ9131RNXI RGMII PHY
or LAN8740Ai RMII PHY attached to EQoS MAC, and either external RGMII PHY
or LAN8740Ai RMII PHY attached to FEC MAC. The SoM configuration can be
detected for each MAC by reading RX_CTL pull resistor state early on boot.
Make use of this, detect the exact PHY configuration, and patch control DT
accordingly so that the ethernet is configured correctly in U-Boot.

Signed-off-by: Marek Vasut <marex@denx.de>
2023-03-30 08:40:27 +02:00

372 lines
9.7 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2022 Marek Vasut <marex@denx.de>
*/
#include <common.h>
#include <asm/arch/clock.h>
#include <asm/mach-imx/iomux-v3.h>
#include <asm/arch/imx8mp_pins.h>
#include <asm/arch/sys_proto.h>
#include <asm/io.h>
#include <dm.h>
#include <dt-bindings/clock/imx8mp-clock.h>
#include <env.h>
#include <env_internal.h>
#include <i2c_eeprom.h>
#include <linux/bitfield.h>
#include <malloc.h>
#include <net.h>
#include <miiphy.h>
#include "lpddr4_timing.h"
#include "../common/dh_common.h"
#include "../common/dh_imx.h"
DECLARE_GLOBAL_DATA_PTR;
int mach_cpu_init(void)
{
icache_enable();
return 0;
}
int board_phys_sdram_size(phys_size_t *size)
{
const u16 memsz[] = { 512, 1024, 1536, 2048, 3072, 4096, 6144, 8192 };
u8 memcfg = dh_get_memcfg();
*size = (u64)memsz[memcfg] << 20ULL;
return 0;
}
static void setup_eqos(void)
{
struct iomuxc_gpr_base_regs *gpr =
(struct iomuxc_gpr_base_regs *)IOMUXC_GPR_BASE_ADDR;
/* Set INTF as RGMII, enable RGMII TXC clock. */
clrsetbits_le32(&gpr->gpr[1],
IOMUXC_GPR_GPR1_GPR_ENET_QOS_INTF_SEL_MASK, BIT(16));
setbits_le32(&gpr->gpr[1], BIT(19) | BIT(21));
set_clk_eqos(ENET_125MHZ);
}
static void setup_fec(void)
{
struct iomuxc_gpr_base_regs *gpr =
(struct iomuxc_gpr_base_regs *)IOMUXC_GPR_BASE_ADDR;
/* Enable RGMII TX clk output. */
setbits_le32(&gpr->gpr[1], BIT(22));
set_clk_enet(ENET_125MHZ);
}
static int dh_imx8_setup_ethaddr(void)
{
unsigned char enetaddr[6];
if (dh_mac_is_in_env("ethaddr"))
return 0;
if (!dh_imx_get_mac_from_fuse(enetaddr))
goto out;
if (!dh_get_mac_from_eeprom(enetaddr, "eeprom0"))
goto out;
return -ENXIO;
out:
return eth_env_set_enetaddr("ethaddr", enetaddr);
}
static int dh_imx8_setup_eth1addr(void)
{
unsigned char enetaddr[6];
if (dh_mac_is_in_env("eth1addr"))
return 0;
if (!dh_imx_get_mac_from_fuse(enetaddr))
goto increment_out;
if (!dh_get_mac_from_eeprom(enetaddr, "eeprom1"))
goto out;
/*
* Populate second ethernet MAC from first ethernet EEPROM with MAC
* address LSByte incremented by 1. This is only used on SoMs without
* second ethernet EEPROM, i.e. early prototypes.
*/
if (!dh_get_mac_from_eeprom(enetaddr, "eeprom0"))
goto increment_out;
return -ENXIO;
increment_out:
enetaddr[5]++;
out:
return eth_env_set_enetaddr("eth1addr", enetaddr);
}
int dh_setup_mac_address(void)
{
int ret;
ret = dh_imx8_setup_ethaddr();
if (ret)
printf("%s: Unable to setup ethaddr! ret = %d\n", __func__, ret);
ret = dh_imx8_setup_eth1addr();
if (ret)
printf("%s: Unable to setup eth1addr! ret = %d\n", __func__, ret);
return ret;
}
int board_init(void)
{
setup_eqos();
setup_fec();
return 0;
}
int board_late_init(void)
{
dh_setup_mac_address();
return 0;
}
enum env_location env_get_location(enum env_operation op, int prio)
{
return prio ? ENVL_UNKNOWN : ENVL_SPI_FLASH;
}
static const char *iomuxc_compat = "fsl,imx8mp-iomuxc";
static const char *lan_compat = "ethernet-phy-id0007.c110";
static const char *ksz_compat = "ethernet-phy-id0022.1642";
static int dh_dt_patch_som_eqos(const void *fdt_blob)
{
const void __iomem *mux = (void __iomem *)IOMUXC_BASE_ADDR +
FIELD_GET(MUX_CTRL_OFS_MASK, MX8MP_PAD_ENET_RX_CTL__GPIO1_IO24);
int mac_node, mdio_node, iomuxc_node, ksz_node, lan_node, subnode;
const char *mac_compat = "nxp,imx8mp-dwmac-eqos";
void *blob = (void *)fdt_blob;
const fdt32_t *clk_prop;
bool is_gigabit;
u32 handle;
u32 clk[6];
setbits_le32(mux, IOMUX_CONFIG_SION);
is_gigabit = !(readl(GPIO1_BASE_ADDR) & BIT(24));
clrbits_le32(mux, IOMUX_CONFIG_SION);
/* Adjust EQoS node for Gigabit KSZ9131RNXI or Fast LAN8740Ai PHY */
mac_node = fdt_node_offset_by_compatible(blob, -1, mac_compat);
if (mac_node < 0)
return 0;
mdio_node = fdt_first_subnode(blob, mac_node);
if (mdio_node < 0)
return 0;
/* KSZ9131RNXI */
ksz_node = fdt_node_offset_by_compatible(blob, mdio_node, ksz_compat);
if (ksz_node < 0)
return 0;
/* LAN8740Ai */
lan_node = fdt_node_offset_by_compatible(blob, mdio_node, lan_compat);
if (lan_node < 0)
return 0;
iomuxc_node = fdt_node_offset_by_compatible(blob, -1, iomuxc_compat);
if (iomuxc_node < 0)
return 0;
/*
* The code below adjusts the following DT properties:
* - assigned-clock-parents .. 125 MHz RGMII / 50 MHz RMII ref clock
* - assigned-clock-rates .... 125 MHz RGMII / 50 MHz RMII ref clock
* - phy-handle .............. KSZ9131RNXI RGMII / LAN8740Ai RMII
* - phy-mode ................ RGMII / RMII
* - pinctrl-0 ............... RGMII / RMII
* - PHY subnode status ...... "disabled"/"okay" per RGMII / RMII
*/
/* Perform all inplace changes first, string changes last. */
clk_prop = fdt_getprop(blob, mac_node, "assigned-clock-parents", NULL);
if (!clk_prop)
return 0;
clk[0] = clk_prop[0];
clk[1] = cpu_to_fdt32(IMX8MP_SYS_PLL1_266M);
clk[2] = clk_prop[2];
clk[3] = cpu_to_fdt32(IMX8MP_SYS_PLL2_100M);
clk[4] = clk_prop[4];
clk[5] = is_gigabit ? cpu_to_fdt32(IMX8MP_SYS_PLL2_125M) :
cpu_to_fdt32(IMX8MP_SYS_PLL2_50M);
fdt_setprop_inplace(blob, mac_node, "assigned-clock-parents",
clk, 6 * sizeof(u32));
clk[0] = cpu_to_fdt32(0);
clk[1] = cpu_to_fdt32(100000000);
clk[2] = is_gigabit ? cpu_to_fdt32(125000000) :
cpu_to_fdt32(50000000);
fdt_setprop_inplace(blob, mac_node, "assigned-clock-rates",
clk, 3 * sizeof(u32));
handle = fdt_get_phandle(blob, is_gigabit ? ksz_node : lan_node);
fdt_setprop_inplace_u32(blob, mac_node, "phy-handle", handle);
fdt_for_each_subnode(subnode, blob, iomuxc_node) {
if (!strstr(fdt_get_name(blob, subnode, NULL),
is_gigabit ? "eqos-rgmii" : "eqos-rmii"))
continue;
handle = fdt_get_phandle(blob, subnode);
fdt_setprop_inplace_u32(blob, mac_node, "pinctrl-0", handle);
break;
}
fdt_setprop_string(blob, mac_node, "phy-mode",
is_gigabit ? "rgmii-id" : "rmii");
mac_node = fdt_node_offset_by_compatible(blob, -1, mac_compat);
mdio_node = fdt_first_subnode(blob, mac_node);
ksz_node = fdt_node_offset_by_compatible(blob, mdio_node, ksz_compat);
fdt_setprop_string(blob, ksz_node, "status",
is_gigabit ? "okay" : "disabled");
mac_node = fdt_node_offset_by_compatible(blob, -1, mac_compat);
mdio_node = fdt_first_subnode(blob, mac_node);
lan_node = fdt_node_offset_by_compatible(blob, mdio_node, lan_compat);
fdt_setprop_string(blob, lan_node, "status",
is_gigabit ? "disabled" : "okay");
return 0;
}
static int dh_dt_patch_som_fec(const void *fdt_blob)
{
const void __iomem *mux = (void __iomem *)IOMUXC_BASE_ADDR +
FIELD_GET(MUX_CTRL_OFS_MASK, MX8MP_PAD_SAI1_TXFS__GPIO4_IO10);
int mac_node, mdio_node, iomuxc_node, lan_node, phy_node, subnode;
const char *mac_compat = "fsl,imx8mp-fec";
void *blob = (void *)fdt_blob;
const fdt32_t *clk_prop;
bool is_gigabit;
u32 handle;
u32 clk[8];
setbits_le32(mux, IOMUX_CONFIG_SION);
is_gigabit = !(readl(GPIO4_BASE_ADDR) & BIT(10));
clrbits_le32(mux, IOMUX_CONFIG_SION);
/* Test for non-default SoM with 100/Full PHY attached to FEC */
if (is_gigabit)
return 0;
/* Adjust FEC node for Fast LAN8740Ai PHY */
mac_node = fdt_node_offset_by_compatible(blob, -1, mac_compat);
if (mac_node < 0)
return 0;
/* Optional PHY pointed to by phy-handle, possibly on carrier board */
phy_node = fdtdec_lookup_phandle(blob, mac_node, "phy-handle");
if (phy_node > 0) {
fdt_setprop_string(blob, phy_node, "status", "disabled");
mac_node = fdt_node_offset_by_compatible(blob, -1, mac_compat);
}
mdio_node = fdt_first_subnode(blob, mac_node);
if (mdio_node < 0)
return 0;
/* LAN8740Ai */
lan_node = fdt_node_offset_by_compatible(blob, mdio_node, lan_compat);
if (lan_node < 0)
return 0;
iomuxc_node = fdt_node_offset_by_compatible(blob, -1, iomuxc_compat);
if (iomuxc_node < 0)
return 0;
/*
* The code below adjusts the following DT properties:
* - assigned-clock-parents .. 50 MHz RMII ref clock
* - assigned-clock-rates .... 50 MHz RMII ref clock
* - phy-handle .............. LAN8740Ai RMII
* - phy-mode ................ RMII
* - pinctrl-0 ............... RMII
* - PHY subnode status ...... "okay" for RMII PHY
*/
/* Perform all inplace changes first, string changes last. */
clk_prop = fdt_getprop(blob, mac_node, "assigned-clock-parents", NULL);
if (!clk_prop)
return 0;
clk[0] = clk_prop[0];
clk[1] = cpu_to_fdt32(IMX8MP_SYS_PLL1_266M);
clk[2] = clk_prop[2];
clk[3] = cpu_to_fdt32(IMX8MP_SYS_PLL2_100M);
clk[4] = clk_prop[4];
clk[5] = cpu_to_fdt32(IMX8MP_SYS_PLL2_50M);
clk[6] = clk_prop[6];
clk[7] = cpu_to_fdt32(IMX8MP_SYS_PLL2_50M);
fdt_setprop_inplace(blob, mac_node, "assigned-clock-parents",
clk, 8 * sizeof(u32));
clk[0] = cpu_to_fdt32(0);
clk[1] = cpu_to_fdt32(100000000);
clk[2] = cpu_to_fdt32(50000000);
clk[3] = cpu_to_fdt32(0);
fdt_setprop_inplace(blob, mac_node, "assigned-clock-rates",
clk, 4 * sizeof(u32));
handle = fdt_get_phandle(blob, lan_node);
fdt_setprop_inplace_u32(blob, mac_node, "phy-handle", handle);
fdt_for_each_subnode(subnode, blob, iomuxc_node) {
if (!strstr(fdt_get_name(blob, subnode, NULL), "fec-rmii"))
continue;
handle = fdt_get_phandle(blob, subnode);
fdt_setprop_inplace_u32(blob, mac_node, "pinctrl-0", handle);
break;
}
fdt_setprop_string(blob, mac_node, "phy-mode", "rmii");
mac_node = fdt_node_offset_by_compatible(blob, -1, mac_compat);
mdio_node = fdt_first_subnode(blob, mac_node);
lan_node = fdt_node_offset_by_compatible(blob, mdio_node, lan_compat);
fdt_setprop_string(blob, lan_node, "status", "okay");
return 0;
}
static int dh_dt_patch_som(const void *fdt_blob)
{
int ret;
/* Do nothing if not i.MX8MP DHCOM SoM */
ret = fdt_node_check_compatible(fdt_blob, 0, "dh,imx8mp-dhcom-som");
if (ret)
return 0;
ret = dh_dt_patch_som_eqos(fdt_blob);
if (ret)
return ret;
return dh_dt_patch_som_fec(fdt_blob);
}
int fdtdec_board_setup(const void *fdt_blob)
{
return dh_dt_patch_som(fdt_blob);
}