forked from OERV-BSP/u-boot
Merge tag 'u-boot-imx-master-20250120' of https://gitlab.denx.de/u-boot/custodians/u-boot-imx
CI: https://source.denx.de/u-boot/custodians/u-boot-imx/-/pipelines/24263 - Add i.MX95 EMDIO support - Guard binman nodes with CONFIG_OPTEE on imx8m - Enable CAAM in phycore-imx8mp SPL. - Fix Fix NULL dereference in imx_pinctrl_probe().
This commit is contained in:
@@ -164,6 +164,7 @@
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_OPTEE
|
||||
tee: tee {
|
||||
description = "OP-TEE";
|
||||
type = "tee";
|
||||
@@ -178,6 +179,7 @@
|
||||
optional;
|
||||
};
|
||||
};
|
||||
#endif
|
||||
|
||||
binman_fip: fip {
|
||||
arch = "arm64";
|
||||
@@ -207,7 +209,11 @@
|
||||
fdt = "fdt-SEQ";
|
||||
firmware = "uboot";
|
||||
#ifndef CONFIG_ARMV8_PSCI
|
||||
#ifdef CONFIG_OPTEE
|
||||
loadables = "atf", "tee";
|
||||
#else
|
||||
loadables = "atf";
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
};
|
||||
|
||||
@@ -240,6 +240,7 @@
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_OPTEE
|
||||
tee: tee {
|
||||
description = "OP-TEE";
|
||||
type = "tee";
|
||||
@@ -254,6 +255,7 @@
|
||||
optional;
|
||||
};
|
||||
};
|
||||
#endif
|
||||
|
||||
binman_fip: fip {
|
||||
arch = "arm64";
|
||||
@@ -283,7 +285,11 @@
|
||||
fdt = "fdt-SEQ";
|
||||
firmware = "uboot";
|
||||
#ifndef CONFIG_ARMV8_PSCI
|
||||
#ifdef CONFIG_OPTEE
|
||||
loadables = "atf", "tee";
|
||||
#else
|
||||
loadables = "atf";
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
};
|
||||
|
||||
@@ -185,6 +185,7 @@
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_OPTEE
|
||||
tee: tee {
|
||||
description = "OP-TEE";
|
||||
type = "tee";
|
||||
@@ -199,6 +200,7 @@
|
||||
optional;
|
||||
};
|
||||
};
|
||||
#endif
|
||||
|
||||
@fdt-SEQ {
|
||||
description = "NAME";
|
||||
@@ -219,7 +221,11 @@
|
||||
fdt = "fdt-SEQ";
|
||||
firmware = "uboot";
|
||||
#ifndef CONFIG_ARMV8_PSCI
|
||||
#ifdef CONFIG_OPTEE
|
||||
loadables = "atf", "tee";
|
||||
#else
|
||||
loadables = "atf";
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
};
|
||||
|
||||
@@ -144,6 +144,7 @@
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_OPTEE
|
||||
tee: tee {
|
||||
description = "OP-TEE";
|
||||
type = "tee";
|
||||
@@ -158,6 +159,7 @@
|
||||
optional;
|
||||
};
|
||||
};
|
||||
#endif
|
||||
|
||||
fdt {
|
||||
compression = "none";
|
||||
@@ -179,7 +181,11 @@
|
||||
fdt = "fdt";
|
||||
firmware = "uboot";
|
||||
#ifndef CONFIG_ARMV8_PSCI
|
||||
#ifdef CONFIG_OPTEE
|
||||
loadables = "atf", "tee";
|
||||
#else
|
||||
loadables = "atf";
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
};
|
||||
|
||||
@@ -165,6 +165,8 @@ int power_init_board(void)
|
||||
|
||||
void spl_board_init(void)
|
||||
{
|
||||
arch_misc_init();
|
||||
|
||||
/* Set GIC clock to 500Mhz for OD VDD_SOC. */
|
||||
clock_enable(CCGR_GIC, 0);
|
||||
clock_set_target_val(GIC_CLK_ROOT, CLK_ROOT_ON | CLK_ROOT_SOURCE_SEL(5));
|
||||
|
||||
@@ -52,7 +52,6 @@ CONFIG_SPL_CUSTOM_SYS_MALLOC_ADDR=0x42200000
|
||||
CONFIG_SPL_SYS_MALLOC_SIZE=0x80000
|
||||
CONFIG_SPL_SYS_MMCSD_RAW_MODE=y
|
||||
CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR=0x300
|
||||
# CONFIG_SPL_CRYPTO is not set
|
||||
CONFIG_SPL_I2C=y
|
||||
CONFIG_SPL_NOR_SUPPORT=y
|
||||
CONFIG_SPL_POWER=y
|
||||
@@ -180,4 +179,3 @@ CONFIG_USB_GADGET_MANUFACTURER="PHYTEC"
|
||||
CONFIG_USB_GADGET_VENDOR_NUM=0x0525
|
||||
CONFIG_USB_GADGET_PRODUCT_NUM=0xa4a5
|
||||
CONFIG_IMX_WATCHDOG=y
|
||||
# CONFIG_SPL_SHA_HW_ACCEL is not set
|
||||
|
||||
@@ -1019,6 +1019,20 @@ config FSL_ENETC
|
||||
This driver supports the NXP ENETC Ethernet controller found on some
|
||||
of the NXP SoCs.
|
||||
|
||||
config FSL_ENETC_NETC_BLK_CTRL
|
||||
bool "NXP ENETC NETC blocks control driver"
|
||||
depends on FSL_ENETC && IMX95
|
||||
default y if IMX95
|
||||
help
|
||||
This driver configures Integrated Endpoint Register Block (IERB) and
|
||||
Privileged Register Block (PRB) of NETC. For i.MX platforms, it also
|
||||
includes the configuration of NETCMIX block.
|
||||
The IERB contains registers that are used for pre-boot initialization,
|
||||
debug, and non-customer configuration. The PRB controls global reset
|
||||
and global error handling for NETC. The NETCMIX block is mainly used
|
||||
to set MII protocol and PCS protocol of the links, it also contains
|
||||
settings for some other functions.
|
||||
|
||||
config MDIO_GPIO_BITBANG
|
||||
bool "GPIO bitbanging MDIO driver"
|
||||
depends on DM_MDIO && DM_GPIO
|
||||
|
||||
@@ -44,6 +44,7 @@ obj-$(CONFIG_FEC_MXC) += fec_mxc.o
|
||||
obj-$(CONFIG_FMAN_ENET) += fm/
|
||||
obj-$(CONFIG_FMAN_ENET) += fsl_mdio.o
|
||||
obj-$(CONFIG_FSL_ENETC) += fsl_enetc.o fsl_enetc_mdio.o
|
||||
obj-$(CONFIG_FSL_ENETC_NETC_BLK_CTRL) += fsl_enetc_netc_blk_ctrl.o
|
||||
obj-$(CONFIG_FSL_LS_MDIO) += fsl_ls_mdio.o
|
||||
obj-$(CONFIG_FSL_MC_ENET) += fsl-mc/
|
||||
obj-$(CONFIG_FSL_MC_ENET) += ldpaa_eth/
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
/*
|
||||
* ENETC ethernet controller driver
|
||||
* Copyright 2017-2021 NXP
|
||||
* Copyright 2023-2025 NXP
|
||||
*/
|
||||
|
||||
#include <dm.h>
|
||||
@@ -16,52 +17,267 @@
|
||||
#include <miiphy.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/build_bug.h>
|
||||
|
||||
#ifdef CONFIG_ARCH_IMX9
|
||||
#include <asm/mach-imx/sys_proto.h>
|
||||
#include <cpu_func.h>
|
||||
#endif
|
||||
|
||||
#include "fsl_enetc.h"
|
||||
|
||||
#define ENETC_DRIVER_NAME "enetc_eth"
|
||||
|
||||
/*
|
||||
* Calculate number of buffer descriptors per cacheline, and compile-time
|
||||
* validate that:
|
||||
* - the RX and TX descriptors are the same size
|
||||
* - the descriptors fit exactly into cachelines without overlap
|
||||
* - all descriptors fit exactly into cachelines
|
||||
*/
|
||||
#define ENETC_NUM_BD_IN_CL \
|
||||
((ARCH_DMA_MINALIGN / sizeof(struct enetc_tx_bd)) + \
|
||||
BUILD_BUG_ON_ZERO(sizeof(struct enetc_tx_bd) != \
|
||||
sizeof(union enetc_rx_bd)) + \
|
||||
BUILD_BUG_ON_ZERO(ARCH_DMA_MINALIGN % sizeof(struct enetc_tx_bd)) + \
|
||||
BUILD_BUG_ON_ZERO(ARCH_DMA_MINALIGN % sizeof(union enetc_rx_bd)) + \
|
||||
BUILD_BUG_ON_ZERO(ENETC_BD_CNT % \
|
||||
(ARCH_DMA_MINALIGN / sizeof(struct enetc_tx_bd))))
|
||||
|
||||
static int enetc_remove(struct udevice *dev);
|
||||
|
||||
static int enetc_is_imx95(struct udevice *dev)
|
||||
{
|
||||
struct pci_child_plat *pplat = dev_get_parent_plat(dev);
|
||||
|
||||
/* Test whether this is i.MX95 ENETCv4. This may be optimized out. */
|
||||
return IS_ENABLED(CONFIG_ARCH_IMX9) &&
|
||||
pplat->vendor == PCI_VENDOR_ID_PHILIPS;
|
||||
}
|
||||
|
||||
static int enetc_is_ls1028a(struct udevice *dev)
|
||||
{
|
||||
struct pci_child_plat *pplat = dev_get_parent_plat(dev);
|
||||
|
||||
/* Test whether this is LS1028A ENETC. This may be optimized out. */
|
||||
return IS_ENABLED(CONFIG_ARCH_LS1028A) &&
|
||||
pplat->vendor == PCI_VENDOR_ID_FREESCALE;
|
||||
}
|
||||
|
||||
static int enetc_dev_id(struct udevice *dev)
|
||||
{
|
||||
if (enetc_is_imx95(dev))
|
||||
return PCI_DEV(pci_get_devfn(dev)) >> 3;
|
||||
if (enetc_is_ls1028a(dev))
|
||||
return PCI_FUNC(pci_get_devfn(dev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void enetc_inval_rxbd(struct udevice *dev)
|
||||
{
|
||||
struct enetc_priv *priv = dev_get_priv(dev);
|
||||
union enetc_rx_bd *desc = &priv->enetc_rxbd[priv->rx_bdr.next_prod_idx];
|
||||
unsigned long start = rounddown((unsigned long)desc, ARCH_DMA_MINALIGN);
|
||||
unsigned long end = roundup((unsigned long)desc + sizeof(*desc),
|
||||
ARCH_DMA_MINALIGN);
|
||||
|
||||
if (enetc_is_imx95(dev))
|
||||
invalidate_dcache_range(start, end);
|
||||
}
|
||||
|
||||
static void enetc_flush_bd(struct udevice *dev, int pi, bool tx)
|
||||
{
|
||||
struct enetc_priv *priv = dev_get_priv(dev);
|
||||
union enetc_rx_bd *rxdesc = &priv->enetc_rxbd[pi];
|
||||
struct enetc_tx_bd *txdesc = &priv->enetc_txbd[pi];
|
||||
unsigned long desc = tx ? (unsigned long)txdesc : (unsigned long)rxdesc;
|
||||
unsigned long size = tx ? sizeof(*txdesc) : sizeof(*rxdesc);
|
||||
unsigned long start = rounddown(desc, ARCH_DMA_MINALIGN);
|
||||
unsigned long end = roundup(desc + size, ARCH_DMA_MINALIGN);
|
||||
|
||||
if (enetc_is_imx95(dev))
|
||||
flush_dcache_range(start, end);
|
||||
}
|
||||
|
||||
static void enetc_inval_buffer(struct udevice *dev, void *buf, size_t size)
|
||||
{
|
||||
unsigned long start = rounddown((unsigned long)buf, ARCH_DMA_MINALIGN);
|
||||
unsigned long end = roundup((unsigned long)buf + size,
|
||||
ARCH_DMA_MINALIGN);
|
||||
|
||||
if (enetc_is_imx95(dev))
|
||||
invalidate_dcache_range(start, end);
|
||||
}
|
||||
|
||||
static void enetc_flush_buffer(struct udevice *dev, void *buf, size_t size)
|
||||
{
|
||||
unsigned long start = rounddown((unsigned long)buf, ARCH_DMA_MINALIGN);
|
||||
unsigned long end = roundup((unsigned long)buf + size,
|
||||
ARCH_DMA_MINALIGN);
|
||||
|
||||
if (enetc_is_imx95(dev))
|
||||
flush_dcache_range(start, end);
|
||||
}
|
||||
|
||||
/* register accessors */
|
||||
static u32 enetc_read_reg(void __iomem *addr)
|
||||
{
|
||||
return readl(addr);
|
||||
}
|
||||
|
||||
static void enetc_write_reg(void __iomem *addr, u32 val)
|
||||
{
|
||||
writel(val, addr);
|
||||
}
|
||||
|
||||
static void enetc_write(struct enetc_priv *priv, u32 off, u32 val)
|
||||
{
|
||||
enetc_write_reg(priv->regs_base + off, val);
|
||||
}
|
||||
|
||||
/* base port register accessors */
|
||||
static void enetc_write_pmr(struct udevice *dev, u32 val)
|
||||
{
|
||||
struct enetc_data *data = (struct enetc_data *)dev_get_driver_data(dev);
|
||||
struct enetc_priv *priv = dev_get_priv(dev);
|
||||
const u32 off = ENETC_PMR + data->reg_offset_pmr;
|
||||
|
||||
enetc_write_reg(priv->port_regs + off, val);
|
||||
}
|
||||
|
||||
static void enetc_write_psipmar(struct udevice *dev, int n, u32 val)
|
||||
{
|
||||
struct enetc_data *data = (struct enetc_data *)dev_get_driver_data(dev);
|
||||
struct enetc_priv *priv = dev_get_priv(dev);
|
||||
const u32 off = (n ? ENETC_PSIPMAR1 : ENETC_PSIPMAR0) +
|
||||
data->reg_offset_psipmar;
|
||||
|
||||
enetc_write_reg(priv->port_regs + off, val);
|
||||
}
|
||||
|
||||
/* port station register accessors */
|
||||
static void enetc_write_psicfgr(struct udevice *dev, int port, u32 val)
|
||||
{
|
||||
struct enetc_data *data = (struct enetc_data *)dev_get_driver_data(dev);
|
||||
struct enetc_priv *priv = dev_get_priv(dev);
|
||||
const u32 off = ENETC_PSICFGR(port, ENETC_PSICFGR_SHIFT_LS) +
|
||||
data->reg_offset_psicfgr;
|
||||
|
||||
enetc_write_reg(priv->port_regs + off, val);
|
||||
}
|
||||
|
||||
/* port register accessors */
|
||||
static u32 enetc_read_pcapr_mdio(struct udevice *dev)
|
||||
{
|
||||
struct enetc_data *data = (struct enetc_data *)dev_get_driver_data(dev);
|
||||
struct enetc_priv *priv = dev_get_priv(dev);
|
||||
const u32 off = ENETC_PCAPR0 + data->reg_offset_pcapr;
|
||||
const u32 reg = enetc_read_reg(priv->port_regs + off);
|
||||
|
||||
if (enetc_is_imx95(dev))
|
||||
return reg & ENETC_PCS_PROT;
|
||||
else if (enetc_is_ls1028a(dev))
|
||||
return reg & ENETC_PCAPRO_MDIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void enetc_write_port(struct enetc_priv *priv, u32 off, u32 val)
|
||||
{
|
||||
enetc_write_reg(priv->port_regs + off, val);
|
||||
}
|
||||
|
||||
/* MAC port register accessors */
|
||||
static u32 enetc_read_mac_port(struct udevice *dev, u32 off)
|
||||
{
|
||||
struct enetc_data *data = (struct enetc_data *)dev_get_driver_data(dev);
|
||||
struct enetc_priv *priv = dev_get_priv(dev);
|
||||
|
||||
return enetc_read_reg(priv->port_regs + data->reg_offset_mac + off);
|
||||
}
|
||||
|
||||
static void enetc_write_mac_port(struct udevice *dev, u32 off, u32 val)
|
||||
{
|
||||
struct enetc_data *data = (struct enetc_data *)dev_get_driver_data(dev);
|
||||
struct enetc_priv *priv = dev_get_priv(dev);
|
||||
|
||||
enetc_write_reg(priv->port_regs + data->reg_offset_mac + off, val);
|
||||
}
|
||||
|
||||
/* BDR register accessor, see also ENETC_BDR() */
|
||||
static void enetc_bdr_write(struct enetc_priv *priv, int type, int n,
|
||||
u32 off, u32 val)
|
||||
{
|
||||
enetc_write(priv, ENETC_BDR(type, n, off), val);
|
||||
}
|
||||
|
||||
/*
|
||||
* sets the MAC address in IERB registers, this setting is persistent and
|
||||
* carried over to Linux.
|
||||
*/
|
||||
static void enetc_set_ierb_primary_mac(struct udevice *dev, int devfn,
|
||||
const u8 *enetaddr)
|
||||
{
|
||||
#ifdef CONFIG_ARCH_LS1028A
|
||||
/*
|
||||
* LS1028A is the only part with IERB at this time and there are plans to change
|
||||
* its structure, keep this LS1028A specific for now
|
||||
*/
|
||||
#define IERB_BASE 0x1f0800000ULL
|
||||
#define IERB_PFMAC(pf, vf, n) (IERB_BASE + 0x8000 + (pf) * 0x100 + (vf) * 8 \
|
||||
+ (n) * 4)
|
||||
|
||||
static int ierb_fn_to_pf[] = {0, 1, 2, -1, -1, -1, 3};
|
||||
|
||||
static void enetc_set_ierb_primary_mac(struct udevice *dev, void *blob)
|
||||
{
|
||||
static int ierb_fn_to_pf[] = { 0, 1, 2, -1, -1, -1, 3 };
|
||||
struct pci_child_plat *ppdata = dev_get_parent_plat(dev);
|
||||
struct eth_pdata *pdata = dev_get_plat(dev);
|
||||
struct enetc_priv *priv = dev_get_priv(dev);
|
||||
const u8 *enetaddr = pdata->enetaddr;
|
||||
u16 lower = *(const u16 *)(enetaddr + 4);
|
||||
u32 upper = *(const u32 *)enetaddr;
|
||||
int devfn, offset;
|
||||
char path[256];
|
||||
|
||||
if (ierb_fn_to_pf[devfn] < 0)
|
||||
if (enetc_is_imx95(dev)) {
|
||||
/*
|
||||
* Configure the ENETC primary MAC addresses - Set register
|
||||
* PMAR0/1 for SI 0 and PSIaPMAR0/1 for SI 1, 2 .. a
|
||||
* (optionally pre-configured in IERB).
|
||||
*/
|
||||
devfn = enetc_dev_id(dev);
|
||||
if (devfn > 2)
|
||||
return;
|
||||
|
||||
enetc_write(priv, IMX95_ENETC_SIPMAR0, upper);
|
||||
enetc_write(priv, IMX95_ENETC_SIPMAR1, lower);
|
||||
|
||||
snprintf(path, 256, "/soc/pcie@%x/ethernet@%x,%x",
|
||||
PCI_BUS(dm_pci_get_bdf(dev)), PCI_DEV(ppdata->devfn),
|
||||
PCI_FUNC(ppdata->devfn));
|
||||
} else if (enetc_is_ls1028a(dev)) {
|
||||
/*
|
||||
* LS1028A is the only part with IERB at this time and
|
||||
* there are plans to change its structure, keep this
|
||||
* LS1028A specific for now.
|
||||
*/
|
||||
devfn = PCI_FUNC(ppdata->devfn);
|
||||
|
||||
if (ierb_fn_to_pf[devfn] < 0)
|
||||
return;
|
||||
|
||||
out_le32(IERB_PFMAC(ierb_fn_to_pf[devfn], 0, 0), upper);
|
||||
out_le32(IERB_PFMAC(ierb_fn_to_pf[devfn], 0, 1), (u32)lower);
|
||||
|
||||
snprintf(path, 256, "/soc/pcie@1f0000000/ethernet@%x,%x",
|
||||
PCI_DEV(ppdata->devfn), PCI_FUNC(ppdata->devfn));
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
out_le32(IERB_PFMAC(ierb_fn_to_pf[devfn], 0, 0), upper);
|
||||
out_le32(IERB_PFMAC(ierb_fn_to_pf[devfn], 0, 1), (u32)lower);
|
||||
#endif
|
||||
offset = fdt_path_offset(blob, path);
|
||||
if (offset >= 0)
|
||||
fdt_setprop(blob, offset, "mac-address", pdata->enetaddr, 6);
|
||||
}
|
||||
|
||||
/* sets up primary MAC addresses in DT/IERB */
|
||||
void fdt_fixup_enetc_mac(void *blob)
|
||||
{
|
||||
struct pci_child_plat *ppdata;
|
||||
struct eth_pdata *pdata;
|
||||
struct udevice *dev;
|
||||
struct uclass *uc;
|
||||
char path[256];
|
||||
int offset;
|
||||
int devfn;
|
||||
|
||||
uclass_get(UCLASS_ETH, &uc);
|
||||
uclass_foreach_dev(dev, uc) {
|
||||
@@ -69,18 +285,7 @@ void fdt_fixup_enetc_mac(void *blob)
|
||||
strcmp(dev->driver->name, ENETC_DRIVER_NAME))
|
||||
continue;
|
||||
|
||||
pdata = dev_get_plat(dev);
|
||||
ppdata = dev_get_parent_plat(dev);
|
||||
devfn = PCI_FUNC(ppdata->devfn);
|
||||
|
||||
enetc_set_ierb_primary_mac(dev, devfn, pdata->enetaddr);
|
||||
|
||||
snprintf(path, 256, "/soc/pcie@1f0000000/ethernet@%x,%x",
|
||||
PCI_DEV(ppdata->devfn), PCI_FUNC(ppdata->devfn));
|
||||
offset = fdt_path_offset(blob, path);
|
||||
if (offset < 0)
|
||||
continue;
|
||||
fdt_setprop(blob, offset, "mac-address", pdata->enetaddr, 6);
|
||||
enetc_set_ierb_primary_mac(dev, blob);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,7 +306,7 @@ static int enetc_bind(struct udevice *dev)
|
||||
* PCI function # and enetc#N based on interface count
|
||||
*/
|
||||
if (ofnode_valid(dev_ofnode(dev)))
|
||||
sprintf(name, "enetc-%u", PCI_FUNC(pci_get_devfn(dev)));
|
||||
sprintf(name, "enetc-%u", enetc_dev_id(dev));
|
||||
else
|
||||
sprintf(name, "enetc#%u", eth_num_devices++);
|
||||
device_set_name(dev, name);
|
||||
@@ -181,10 +386,9 @@ static int enetc_init_sgmii(struct udevice *dev)
|
||||
/* set up MAC for RGMII */
|
||||
static void enetc_init_rgmii(struct udevice *dev, struct phy_device *phydev)
|
||||
{
|
||||
struct enetc_priv *priv = dev_get_priv(dev);
|
||||
u32 old_val, val;
|
||||
u32 old_val, val, dpx = 0;
|
||||
|
||||
old_val = val = enetc_read_port(priv, ENETC_PM_IF_MODE);
|
||||
old_val = val = enetc_read_mac_port(dev, ENETC_PM_IF_MODE);
|
||||
|
||||
/* disable unreliable RGMII in-band signaling and force the MAC into
|
||||
* the speed negotiated by the PHY.
|
||||
@@ -202,15 +406,20 @@ static void enetc_init_rgmii(struct udevice *dev, struct phy_device *phydev)
|
||||
val |= ENETC_PM_IFM_SSP_10;
|
||||
}
|
||||
|
||||
if (enetc_is_imx95(dev))
|
||||
dpx = ENETC_PM_IFM_FULL_DPX_IMX;
|
||||
else if (enetc_is_ls1028a(dev))
|
||||
dpx = ENETC_PM_IFM_FULL_DPX_LS;
|
||||
|
||||
if (phydev->duplex == DUPLEX_FULL)
|
||||
val |= ENETC_PM_IFM_FULL_DPX;
|
||||
val |= dpx;
|
||||
else
|
||||
val &= ~ENETC_PM_IFM_FULL_DPX;
|
||||
val &= ~dpx;
|
||||
|
||||
if (val == old_val)
|
||||
return;
|
||||
|
||||
enetc_write_port(priv, ENETC_PM_IF_MODE, val);
|
||||
enetc_write_mac_port(dev, ENETC_PM_IF_MODE, val);
|
||||
}
|
||||
|
||||
/* set up MAC configuration for the given interface type */
|
||||
@@ -230,9 +439,12 @@ static void enetc_setup_mac_iface(struct udevice *dev,
|
||||
case PHY_INTERFACE_MODE_USXGMII:
|
||||
case PHY_INTERFACE_MODE_10GBASER:
|
||||
/* set ifmode to (US)XGMII */
|
||||
if_mode = enetc_read_port(priv, ENETC_PM_IF_MODE);
|
||||
if_mode &= ~ENETC_PM_IF_IFMODE_MASK;
|
||||
enetc_write_port(priv, ENETC_PM_IF_MODE, if_mode);
|
||||
if_mode = enetc_read_mac_port(dev, ENETC_PM_IF_MODE);
|
||||
if (enetc_is_imx95(dev))
|
||||
if_mode &= ~ENETC_PM_IF_IFMODE_MASK_IMX;
|
||||
else if (enetc_is_ls1028a(dev))
|
||||
if_mode &= ~ENETC_PM_IF_IFMODE_MASK_LS;
|
||||
enetc_write_mac_port(dev, ENETC_PM_IF_MODE, if_mode);
|
||||
break;
|
||||
};
|
||||
}
|
||||
@@ -263,7 +475,7 @@ static void enetc_start_pcs(struct udevice *dev)
|
||||
struct enetc_priv *priv = dev_get_priv(dev);
|
||||
|
||||
/* register internal MDIO for debug purposes */
|
||||
if (enetc_read_port(priv, ENETC_PCAPR0) & ENETC_PCAPRO_MDIO) {
|
||||
if (enetc_read_pcapr_mdio(dev)) {
|
||||
priv->imdio.read = enetc_mdio_read;
|
||||
priv->imdio.write = enetc_mdio_write;
|
||||
priv->imdio.priv = priv->port_regs + ENETC_PM_IMDIO_BASE;
|
||||
@@ -375,6 +587,21 @@ static int enetc_remove(struct udevice *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int enetc_imx95_write_hwaddr(struct udevice *dev)
|
||||
{
|
||||
struct eth_pdata *plat = dev_get_plat(dev);
|
||||
struct enetc_priv *priv = dev_get_priv(dev);
|
||||
u8 *addr = plat->enetaddr;
|
||||
|
||||
u16 lower = *(const u16 *)(addr + 4);
|
||||
u32 upper = *(const u32 *)addr;
|
||||
|
||||
enetc_write_port(priv, IMX95_ENETC_PMAR0, upper);
|
||||
enetc_write_port(priv, IMX95_ENETC_PMAR1, lower);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* LS1028A is the only part with IERB at this time and there are plans to
|
||||
* change its structure, keep this LS1028A specific for now.
|
||||
@@ -413,39 +640,46 @@ static int enetc_ls1028a_write_hwaddr(struct udevice *dev)
|
||||
static int enetc_write_hwaddr(struct udevice *dev)
|
||||
{
|
||||
struct eth_pdata *plat = dev_get_plat(dev);
|
||||
struct enetc_priv *priv = dev_get_priv(dev);
|
||||
u8 *addr = plat->enetaddr;
|
||||
|
||||
if (IS_ENABLED(CONFIG_ARCH_LS1028A))
|
||||
if (enetc_is_imx95(dev))
|
||||
return enetc_imx95_write_hwaddr(dev);
|
||||
if (enetc_is_ls1028a(dev))
|
||||
return enetc_ls1028a_write_hwaddr(dev);
|
||||
|
||||
u16 lower = *(const u16 *)(addr + 4);
|
||||
u32 upper = *(const u32 *)addr;
|
||||
|
||||
enetc_write_port(priv, ENETC_PSIPMAR0, upper);
|
||||
enetc_write_port(priv, ENETC_PSIPMAR1, lower);
|
||||
enetc_write_psipmar(dev, 0, upper);
|
||||
enetc_write_psipmar(dev, 1, lower);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Configure port parameters (# of rings, frame size, enable port) */
|
||||
static void enetc_enable_si_port(struct enetc_priv *priv)
|
||||
static void enetc_enable_si_port(struct udevice *dev)
|
||||
{
|
||||
u32 val;
|
||||
struct enetc_priv *priv = dev_get_priv(dev);
|
||||
u32 val = ENETC_PM_CC_TXP_IMX | ENETC_PM_CC_TX | ENETC_PM_CC_RX;
|
||||
|
||||
/* set Rx/Tx BDR count */
|
||||
val = ENETC_PSICFGR_SET_TXBDR(ENETC_TX_BDR_CNT);
|
||||
val |= ENETC_PSICFGR_SET_RXBDR(ENETC_RX_BDR_CNT);
|
||||
enetc_write_port(priv, ENETC_PSICFGR(0), val);
|
||||
enetc_write_psicfgr(dev, 0, ENETC_PSICFGR_SET_BDR(ENETC_RX_BDR_CNT,
|
||||
ENETC_TX_BDR_CNT));
|
||||
/* set Rx max frame size */
|
||||
enetc_write_port(priv, ENETC_PM_MAXFRM, ENETC_RX_MAXFRM_SIZE);
|
||||
enetc_write_mac_port(dev, ENETC_PM_MAXFRM, ENETC_RX_MAXFRM_SIZE);
|
||||
/* enable MAC port */
|
||||
enetc_write_port(priv, ENETC_PM_CC, ENETC_PM_CC_RX_TX_EN);
|
||||
if (enetc_is_ls1028a(dev))
|
||||
val |= ENETC_PM_CC_TXP_LS | ENETC_PM_CC_PROMIS;
|
||||
enetc_write_mac_port(dev, ENETC_PM_CC, val);
|
||||
/* enable port */
|
||||
enetc_write_port(priv, ENETC_PMR, ENETC_PMR_SI0_EN);
|
||||
if (enetc_is_imx95(dev))
|
||||
enetc_write_port(priv, ENETC_POR, 0x0);
|
||||
enetc_write_pmr(dev, ENETC_PMR_SI0_EN);
|
||||
/* set SI cache policy */
|
||||
enetc_write(priv, ENETC_SICAR0,
|
||||
ENETC_SICAR_RD_CFG | ENETC_SICAR_WR_CFG);
|
||||
enetc_write(priv, ENETC_SICAR0, ENETC_SICAR_WR_CFG |
|
||||
(enetc_is_imx95(dev) ?
|
||||
ENETC_SICAR_RD_CFG_IMX :
|
||||
ENETC_SICAR_RD_CFG_LS));
|
||||
/* enable SI */
|
||||
enetc_write(priv, ENETC_SIMR, ENETC_SIMR_EN);
|
||||
}
|
||||
@@ -536,6 +770,8 @@ static void enetc_setup_rx_bdr(struct udevice *dev)
|
||||
priv->enetc_rxbd[i].w.addr = enetc_rxb_address(dev, i);
|
||||
/* each RX buffer must be aligned to 64B */
|
||||
WARN_ON(priv->enetc_rxbd[i].w.addr & (ARCH_DMA_MINALIGN - 1));
|
||||
|
||||
enetc_flush_bd(dev, i, false);
|
||||
}
|
||||
|
||||
/* reset producer (ENETC owned) and consumer (SW owned) index */
|
||||
@@ -556,6 +792,7 @@ static void enetc_setup_rx_bdr(struct udevice *dev)
|
||||
*/
|
||||
static int enetc_start(struct udevice *dev)
|
||||
{
|
||||
int ret;
|
||||
struct enetc_priv *priv = dev_get_priv(dev);
|
||||
|
||||
/* reset and enable the PCI device */
|
||||
@@ -563,15 +800,19 @@ static int enetc_start(struct udevice *dev)
|
||||
dm_pci_clrset_config16(dev, PCI_COMMAND, 0,
|
||||
PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
|
||||
|
||||
enetc_enable_si_port(priv);
|
||||
enetc_enable_si_port(dev);
|
||||
|
||||
/* setup Tx/Rx buffer descriptors */
|
||||
enetc_setup_tx_bdr(dev);
|
||||
enetc_setup_rx_bdr(dev);
|
||||
|
||||
ret = phy_startup(priv->phy);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
enetc_setup_mac_iface(dev, priv->phy);
|
||||
|
||||
return phy_startup(priv->phy);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -614,6 +855,8 @@ static int enetc_send(struct udevice *dev, void *packet, int length)
|
||||
enetc_dbg(dev, "TxBD[%d]send: pkt_len=%d, buff @0x%x%08x\n", pi, length,
|
||||
upper_32_bits((u64)nv_packet), lower_32_bits((u64)nv_packet));
|
||||
|
||||
enetc_flush_buffer(dev, packet, length);
|
||||
|
||||
/* prepare Tx BD */
|
||||
memset(&priv->enetc_txbd[pi], 0x0, sizeof(struct enetc_tx_bd));
|
||||
priv->enetc_txbd[pi].addr =
|
||||
@@ -621,7 +864,10 @@ static int enetc_send(struct udevice *dev, void *packet, int length)
|
||||
priv->enetc_txbd[pi].buf_len = cpu_to_le16(length);
|
||||
priv->enetc_txbd[pi].frm_len = cpu_to_le16(length);
|
||||
priv->enetc_txbd[pi].flags = cpu_to_le16(ENETC_TXBD_FLAGS_F);
|
||||
|
||||
dmb();
|
||||
enetc_flush_bd(dev, pi, true);
|
||||
|
||||
/* send frame: increment producer index */
|
||||
pi = (pi + 1) % txr->bd_count;
|
||||
txr->next_prod_idx = pi;
|
||||
@@ -643,15 +889,15 @@ static int enetc_recv(struct udevice *dev, int flags, uchar **packetp)
|
||||
{
|
||||
struct enetc_priv *priv = dev_get_priv(dev);
|
||||
struct bd_ring *rxr = &priv->rx_bdr;
|
||||
int tries = ENETC_POLL_TRIES;
|
||||
int pi = rxr->next_prod_idx;
|
||||
int ci = rxr->next_cons_idx;
|
||||
int tries = ENETC_POLL_TRIES;
|
||||
u32 status;
|
||||
int len;
|
||||
u8 rdy;
|
||||
|
||||
do {
|
||||
dmb();
|
||||
enetc_inval_rxbd(dev);
|
||||
status = le32_to_cpu(priv->enetc_rxbd[pi].r.lstatus);
|
||||
/* check if current BD is ready to be consumed */
|
||||
rdy = ENETC_RXBD_STATUS_R(status);
|
||||
@@ -663,45 +909,142 @@ static int enetc_recv(struct udevice *dev, int flags, uchar **packetp)
|
||||
dmb();
|
||||
len = le16_to_cpu(priv->enetc_rxbd[pi].r.buf_len);
|
||||
*packetp = (uchar *)enetc_rxb_address(dev, pi);
|
||||
enetc_inval_buffer(dev, *packetp, len);
|
||||
enetc_dbg(dev, "RxBD[%d]: len=%d err=%d pkt=0x%x%08x\n", pi, len,
|
||||
ENETC_RXBD_STATUS_ERRORS(status),
|
||||
upper_32_bits((u64)*packetp), lower_32_bits((u64)*packetp));
|
||||
|
||||
/* BD clean up and advance to next in ring */
|
||||
memset(&priv->enetc_rxbd[pi], 0, sizeof(union enetc_rx_bd));
|
||||
priv->enetc_rxbd[pi].w.addr = enetc_rxb_address(dev, pi);
|
||||
return len;
|
||||
}
|
||||
|
||||
static int enetc_free_pkt(struct udevice *dev, uchar *packet, int length)
|
||||
{
|
||||
const int bd_num_in_cl = enetc_is_imx95(dev) ? ENETC_NUM_BD_IN_CL : 1;
|
||||
struct enetc_priv *priv = dev_get_priv(dev);
|
||||
struct bd_ring *rxr = &priv->rx_bdr;
|
||||
int pi = rxr->next_prod_idx;
|
||||
int ci = rxr->next_cons_idx;
|
||||
uchar *packet_expected;
|
||||
int i;
|
||||
|
||||
packet_expected = (uchar *)enetc_rxb_address(dev, pi);
|
||||
if (packet != packet_expected) {
|
||||
printf("%s: Unexpected packet (expected %p)\n", __func__,
|
||||
packet_expected);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rxr->next_prod_idx = (pi + 1) % rxr->bd_count;
|
||||
ci = (ci + 1) % rxr->bd_count;
|
||||
rxr->next_cons_idx = ci;
|
||||
dmb();
|
||||
/* free up the slot in the ring for HW */
|
||||
enetc_write_reg(rxr->cons_idx, ci);
|
||||
|
||||
return len;
|
||||
if ((pi + 1) % bd_num_in_cl == 0) {
|
||||
/* BD clean up and advance to next in ring */
|
||||
for (i = 0; i < bd_num_in_cl; i++) {
|
||||
memset(&priv->enetc_rxbd[pi - i], 0, sizeof(union enetc_rx_bd));
|
||||
priv->enetc_rxbd[pi - i].w.addr = enetc_rxb_address(dev, pi - i);
|
||||
}
|
||||
|
||||
/* Will flush all bds in one cacheline */
|
||||
enetc_flush_bd(dev, pi - bd_num_in_cl + 1, false);
|
||||
|
||||
/* free up the slot in the ring for HW */
|
||||
enetc_write_reg(rxr->cons_idx, ci);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct eth_ops enetc_ops = {
|
||||
#if IS_ENABLED(CONFIG_ARCH_IMX9)
|
||||
static int enetc_read_rom_hwaddr(struct udevice *dev)
|
||||
{
|
||||
struct eth_pdata *pdata = dev_get_plat(dev);
|
||||
unsigned int dev_id = enetc_dev_id(dev);
|
||||
unsigned char *mac = pdata->enetaddr;
|
||||
|
||||
if (dev_id > 2)
|
||||
return -EINVAL;
|
||||
|
||||
imx_get_mac_from_fuse(dev_id, mac);
|
||||
|
||||
return !is_valid_ethaddr(mac);
|
||||
}
|
||||
|
||||
static const struct eth_ops enetc_ops_imx = {
|
||||
.start = enetc_start,
|
||||
.send = enetc_send,
|
||||
.recv = enetc_recv,
|
||||
.stop = enetc_stop,
|
||||
.free_pkt = enetc_free_pkt,
|
||||
.write_hwaddr = enetc_write_hwaddr,
|
||||
.read_rom_hwaddr = enetc_read_rom_hwaddr,
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(eth_enetc_imx) = {
|
||||
.name = ENETC_DRIVER_NAME,
|
||||
.id = UCLASS_ETH,
|
||||
.bind = enetc_bind,
|
||||
.probe = enetc_probe,
|
||||
.remove = enetc_remove,
|
||||
.ops = &enetc_ops_imx,
|
||||
.priv_auto = sizeof(struct enetc_priv),
|
||||
.plat_auto = sizeof(struct eth_pdata),
|
||||
};
|
||||
|
||||
static const struct enetc_data enetc_data_imx = {
|
||||
.reg_offset_pmr = ENETC_PMR_OFFSET_IMX,
|
||||
.reg_offset_psipmar = ENETC_PSIPMARn_OFFSET_IMX,
|
||||
.reg_offset_pcapr = ENETC_PCAPR_OFFSET_IMX,
|
||||
.reg_offset_psicfgr = ENETC_PSICFGR_OFFSET_IMX,
|
||||
.reg_offset_mac = ENETC_PM_OFFSET_IMX,
|
||||
};
|
||||
|
||||
static struct pci_device_id enetc_ids_imx[] = {
|
||||
{
|
||||
PCI_DEVICE(PCI_VENDOR_ID_PHILIPS, PCI_DEVICE_ID_ENETC4_ETH),
|
||||
.driver_data = (ulong)&enetc_data_imx,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
U_BOOT_PCI_DEVICE(eth_enetc_imx, enetc_ids_imx);
|
||||
#endif
|
||||
|
||||
static const struct eth_ops enetc_ops_ls = {
|
||||
.start = enetc_start,
|
||||
.send = enetc_send,
|
||||
.recv = enetc_recv,
|
||||
.stop = enetc_stop,
|
||||
.free_pkt = enetc_free_pkt,
|
||||
.write_hwaddr = enetc_write_hwaddr,
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(eth_enetc) = {
|
||||
U_BOOT_DRIVER(eth_enetc_ls) = {
|
||||
.name = ENETC_DRIVER_NAME,
|
||||
.id = UCLASS_ETH,
|
||||
.bind = enetc_bind,
|
||||
.probe = enetc_probe,
|
||||
.remove = enetc_remove,
|
||||
.ops = &enetc_ops,
|
||||
.ops = &enetc_ops_ls,
|
||||
.priv_auto = sizeof(struct enetc_priv),
|
||||
.plat_auto = sizeof(struct eth_pdata),
|
||||
};
|
||||
|
||||
static struct pci_device_id enetc_ids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_ENETC_ETH) },
|
||||
static const struct enetc_data enetc_data_ls = {
|
||||
.reg_offset_pmr = ENETC_PMR_OFFSET_LS,
|
||||
.reg_offset_psipmar = ENETC_PSIPMARn_OFFSET_LS,
|
||||
.reg_offset_pcapr = ENETC_PCAPR_OFFSET_LS,
|
||||
.reg_offset_psicfgr = ENETC_PSICFGR_OFFSET_LS,
|
||||
.reg_offset_mac = ENETC_PM_OFFSET_LS,
|
||||
};
|
||||
|
||||
static struct pci_device_id enetc_ids_ls[] = {
|
||||
{
|
||||
PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_ENETC_ETH),
|
||||
.driver_data = (ulong)&enetc_data_ls,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
U_BOOT_PCI_DEVICE(eth_enetc, enetc_ids);
|
||||
U_BOOT_PCI_DEVICE(eth_enetc_ls, enetc_ids_ls);
|
||||
|
||||
@@ -12,7 +12,9 @@
|
||||
|
||||
/* PCI function IDs */
|
||||
#define PCI_DEVICE_ID_ENETC_ETH 0xE100
|
||||
#define PCI_DEVICE_ID_ENETC4_ETH 0xE101
|
||||
#define PCI_DEVICE_ID_ENETC_MDIO 0xEE01
|
||||
#define PCI_DEVICE_ID_ENETC4_EMDIO 0xEE00
|
||||
|
||||
/* ENETC Ethernet controller registers */
|
||||
/* Station interface register offsets */
|
||||
@@ -22,7 +24,8 @@
|
||||
/* write cache cfg: snoop, no allocate, data & BD coherent */
|
||||
#define ENETC_SICAR_WR_CFG 0x6767
|
||||
/* read cache cfg: coherent copy, look up, don't alloc in cache */
|
||||
#define ENETC_SICAR_RD_CFG 0x27270000
|
||||
#define ENETC_SICAR_RD_CFG_LS 0x27270000
|
||||
#define ENETC_SICAR_RD_CFG_IMX 0x2b2b0000
|
||||
#define ENETC_SIROCT 0x300
|
||||
#define ENETC_SIRFRM 0x308
|
||||
#define ENETC_SITOCT 0x320
|
||||
@@ -57,32 +60,60 @@ enum enetc_bdr_type {TX, RX};
|
||||
#define ENETC_PORT_REGS_OFF 0x10000
|
||||
|
||||
/* Port registers */
|
||||
#define ENETC_PMR_OFFSET_IMX 0x0010
|
||||
#define ENETC_PMR_OFFSET_LS 0x0000
|
||||
#define ENETC_PMR 0x0000
|
||||
#define ENETC_PMR_SI0_EN BIT(16)
|
||||
#define ENETC_PSIPMMR 0x0018
|
||||
#define ENETC_PSIPMAR0 0x0100
|
||||
#define ENETC_PSIPMAR1 0x0104
|
||||
#define ENETC_PCAPR0 0x0900
|
||||
#define ENETC_PCAPRO_MDIO BIT(11)
|
||||
#define ENETC_PSICFGR(n) (0x0940 + (n) * 0x10)
|
||||
#define ENETC_PSICFGR_SET_TXBDR(val) ((val) & 0xff)
|
||||
#define ENETC_PSICFGR_SET_RXBDR(val) (((val) & 0xff) << 16)
|
||||
#define ENETC_PSIPMARn_OFFSET_IMX 0x0000
|
||||
#define ENETC_PSIPMARn_OFFSET_LS 0x0080
|
||||
#define ENETC_PSIPMAR0 0x0080
|
||||
#define ENETC_PSIPMAR1 0x0084
|
||||
#define ENETC_PCAPR_OFFSET_IMX 0x4008
|
||||
#define ENETC_PCAPR_OFFSET_LS 0x0900
|
||||
#define ENETC_PCAPR0 0x0000
|
||||
#define ENETC_PCAPRO_MDIO BIT(11) /* LS only */
|
||||
#define ENETC_PCS_PROT GENMASK(15, 0) /* IMX only */
|
||||
/* ENETC base registers */
|
||||
#define ENETC_PSICFGR_OFFSET_LS 0x0940
|
||||
#define ENETC_PSICFGR_SHIFT_LS 0x10
|
||||
#define ENETC_PSICFGR_OFFSET_IMX 0x2010
|
||||
#define ENETC_PSICFGR_SHIFT_IMX 0x80
|
||||
#define ENETC_PSICFGR(n, s) ((n) * (s))
|
||||
#define ENETC_PSICFGR_SET_BDR(rx, tx) (((rx) << 16) | (tx))
|
||||
/* MAC configuration */
|
||||
#define ENETC_PM_CC 0x8008
|
||||
#define ENETC_PM_OFFSET_IMX 0x5000
|
||||
#define ENETC_PM_OFFSET_LS 0x8000
|
||||
#define ENETC_PM_CC 0x0008
|
||||
#define ENETC_PM_CC_DEFAULT 0x0810
|
||||
#define ENETC_PM_CC_RX_TX_EN 0x8813
|
||||
#define ENETC_PM_MAXFRM 0x8014
|
||||
#define ENETC_PM_CC_TXP_IMX BIT(15)
|
||||
#define ENETC_PM_CC_TXP_LS BIT(11)
|
||||
#define ENETC_PM_CC_PROMIS BIT(4)
|
||||
#define ENETC_PM_CC_TX BIT(1)
|
||||
#define ENETC_PM_CC_RX BIT(0)
|
||||
#define ENETC_PM_MAXFRM 0x0014
|
||||
#define ENETC_RX_MAXFRM_SIZE PKTSIZE_ALIGN
|
||||
#define ENETC_PM_IMDIO_BASE 0x8030
|
||||
#define ENETC_PM_IF_MODE 0x8300
|
||||
#define ENETC_PM_IMDIO_BASE 0x0030
|
||||
#define ENETC_PM_IF_MODE 0x0300
|
||||
#define ENETC_PM_IF_MODE_RG BIT(2)
|
||||
#define ENETC_PM_IF_MODE_AN_ENA BIT(15)
|
||||
#define ENETC_PM_IFM_SSP_MASK GENMASK(14, 13)
|
||||
#define ENETC_PM_IFM_SSP_1000 (2 << 13)
|
||||
#define ENETC_PM_IFM_SSP_100 (0 << 13)
|
||||
#define ENETC_PM_IFM_SSP_10 (1 << 13)
|
||||
#define ENETC_PM_IFM_FULL_DPX BIT(12)
|
||||
#define ENETC_PM_IF_IFMODE_MASK GENMASK(1, 0)
|
||||
#define ENETC_PM_IFM_FULL_DPX_IMX BIT(6)
|
||||
#define ENETC_PM_IFM_FULL_DPX_LS BIT(12)
|
||||
#define ENETC_PM_IF_IFMODE_MASK_IMX GENMASK(2, 0)
|
||||
#define ENETC_PM_IF_IFMODE_MASK_LS GENMASK(1, 0)
|
||||
|
||||
/* i.MX95 specific registers */
|
||||
#define IMX95_ENETC_SIPMAR0 0x80
|
||||
#define IMX95_ENETC_SIPMAR1 0x84
|
||||
|
||||
/* Port registers */
|
||||
#define IMX95_ENETC_PMAR0 0x4020
|
||||
#define IMX95_ENETC_PMAR1 0x4024
|
||||
#define ENETC_POR 0x4100
|
||||
|
||||
/* buffer descriptors count must be multiple of 8 and aligned to 128 bytes */
|
||||
#define ENETC_BD_CNT CONFIG_SYS_RX_ETH_BUFFER
|
||||
@@ -163,25 +194,14 @@ struct enetc_priv {
|
||||
struct phy_device *phy;
|
||||
};
|
||||
|
||||
/* register accessors */
|
||||
#define enetc_read_reg(x) readl((x))
|
||||
#define enetc_write_reg(x, val) writel((val), (x))
|
||||
#define enetc_read(priv, off) enetc_read_reg((priv)->regs_base + (off))
|
||||
#define enetc_write(priv, off, v) \
|
||||
enetc_write_reg((priv)->regs_base + (off), v)
|
||||
|
||||
/* port register accessors */
|
||||
#define enetc_port_regs(priv, off) ((priv)->port_regs + (off))
|
||||
#define enetc_read_port(priv, off) \
|
||||
enetc_read_reg(enetc_port_regs((priv), (off)))
|
||||
#define enetc_write_port(priv, off, v) \
|
||||
enetc_write_reg(enetc_port_regs((priv), (off)), v)
|
||||
|
||||
/* BDR register accessors, see ENETC_BDR() */
|
||||
#define enetc_bdr_read(priv, t, n, off) \
|
||||
enetc_read(priv, ENETC_BDR(t, n, off))
|
||||
#define enetc_bdr_write(priv, t, n, off, val) \
|
||||
enetc_write(priv, ENETC_BDR(t, n, off), val)
|
||||
struct enetc_data {
|
||||
/* Register layout offsets */
|
||||
u16 reg_offset_pmr;
|
||||
u16 reg_offset_psipmar;
|
||||
u16 reg_offset_pcapr;
|
||||
u16 reg_offset_psicfgr;
|
||||
u16 reg_offset_mac;
|
||||
};
|
||||
|
||||
/* PCS / internal SoC PHY ID, it defaults to 0 on all interfaces */
|
||||
#define ENETC_PCS_PHY_ADDR 0
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* ENETC ethernet controller driver
|
||||
* Copyright 2019 NXP
|
||||
* Copyright 2019-2025 NXP
|
||||
*/
|
||||
|
||||
#include <dm.h>
|
||||
@@ -14,6 +14,16 @@
|
||||
|
||||
#include "fsl_enetc.h"
|
||||
|
||||
static u32 enetc_read(struct enetc_mdio_priv *priv, u32 off)
|
||||
{
|
||||
return readl(priv->regs_base + off);
|
||||
}
|
||||
|
||||
static void enetc_write(struct enetc_mdio_priv *priv, u32 off, u32 val)
|
||||
{
|
||||
writel(val, priv->regs_base + off);
|
||||
}
|
||||
|
||||
static void enetc_mdio_wait_bsy(struct enetc_mdio_priv *priv)
|
||||
{
|
||||
int to = 10000;
|
||||
@@ -122,7 +132,9 @@ static int enetc_mdio_bind(struct udevice *dev)
|
||||
|
||||
static int enetc_mdio_probe(struct udevice *dev)
|
||||
{
|
||||
struct pci_child_plat *pplat = dev_get_parent_plat(dev);
|
||||
struct enetc_mdio_priv *priv = dev_get_priv(dev);
|
||||
u16 cmd = PCI_COMMAND_MEMORY;
|
||||
|
||||
priv->regs_base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, 0, 0, PCI_REGION_TYPE, 0);
|
||||
if (!priv->regs_base) {
|
||||
@@ -132,7 +144,10 @@ static int enetc_mdio_probe(struct udevice *dev)
|
||||
|
||||
priv->regs_base += ENETC_MDIO_BASE;
|
||||
|
||||
dm_pci_clrset_config16(dev, PCI_COMMAND, 0, PCI_COMMAND_MEMORY);
|
||||
if (pplat->vendor == PCI_VENDOR_ID_PHILIPS) /* i.MX95 */
|
||||
cmd |= PCI_COMMAND_MASTER;
|
||||
|
||||
dm_pci_clrset_config16(dev, PCI_COMMAND, 0, cmd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -148,6 +163,7 @@ U_BOOT_DRIVER(enetc_mdio) = {
|
||||
|
||||
static struct pci_device_id enetc_mdio_ids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_ENETC_MDIO) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_PHILIPS, PCI_DEVICE_ID_ENETC4_EMDIO) },
|
||||
{ }
|
||||
};
|
||||
|
||||
|
||||
346
drivers/net/fsl_enetc_netc_blk_ctrl.c
Normal file
346
drivers/net/fsl_enetc_netc_blk_ctrl.c
Normal file
@@ -0,0 +1,346 @@
|
||||
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
|
||||
/*
|
||||
* NXP NETC Blocks Control Driver
|
||||
*
|
||||
* Copyright 2024 NXP
|
||||
*
|
||||
* This driver is used for pre-initialization of NETC, such as PCS and MII
|
||||
* protocols, LDID, warm reset, etc. Therefore, all NETC device drivers can
|
||||
* only be probed after the netc-blk-crtl driver has completed initialization.
|
||||
* In addition, when the system enters suspend mode, IERB, PRB, and NETCMIX
|
||||
* will be powered off, except for WOL. Therefore, when the system resumes,
|
||||
* these blocks need to be reinitialized.
|
||||
*/
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <clk.h>
|
||||
#include <dm.h>
|
||||
#include <dm/device_compat.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <phy_interface.h>
|
||||
|
||||
/* NETCMIX registers */
|
||||
#define IMX95_CFG_LINK_IO_VAR 0x0
|
||||
#define IO_VAR_16FF_16G_SERDES 0x1
|
||||
#define IO_VAR(port, var) (((var) & 0xf) << ((port) << 2))
|
||||
|
||||
#define IMX95_CFG_LINK_MII_PROT 0x4
|
||||
#define CFG_LINK_MII_PORT_0 GENMASK(3, 0)
|
||||
#define CFG_LINK_MII_PORT_1 GENMASK(7, 4)
|
||||
#define MII_PROT_MII 0x0
|
||||
#define MII_PROT_RMII 0x1
|
||||
#define MII_PROT_RGMII 0x2
|
||||
#define MII_PROT_SERIAL 0x3
|
||||
#define MII_PROT(port, prot) (((prot) & 0xf) << ((port) << 2))
|
||||
|
||||
#define IMX95_CFG_LINK_PCS_PROT(a) (0x8 + (a) * 4)
|
||||
#define PCS_PROT_1G_SGMII BIT(0)
|
||||
#define PCS_PROT_2500M_SGMII BIT(1)
|
||||
#define PCS_PROT_XFI BIT(3)
|
||||
#define PCS_PROT_SFI BIT(4)
|
||||
#define PCS_PROT_10G_SXGMII BIT(6)
|
||||
|
||||
/* NETC privileged register block register */
|
||||
#define PRB_NETCRR 0x100
|
||||
#define NETCRR_SR BIT(0)
|
||||
#define NETCRR_LOCK BIT(1)
|
||||
|
||||
#define PRB_NETCSR 0x104
|
||||
#define NETCSR_ERROR BIT(0)
|
||||
#define NETCSR_STATE BIT(1)
|
||||
|
||||
/* NETC integrated endpoint register block register */
|
||||
#define IERB_EMDIOFAUXR 0x344
|
||||
#define IERB_T0FAUXR 0x444
|
||||
#define IERB_EFAUXR(a) (0x3044 + 0x100 * (a))
|
||||
#define IERB_VFAUXR(a) (0x4004 + 0x40 * (a))
|
||||
#define FAUXR_LDID GENMASK(3, 0)
|
||||
|
||||
/* Platform information */
|
||||
#define IMX95_ENETC0_BUS_DEVFN 0x0
|
||||
#define IMX95_ENETC1_BUS_DEVFN 0x40
|
||||
#define IMX95_ENETC2_BUS_DEVFN 0x80
|
||||
|
||||
/* Flags for different platforms */
|
||||
#define NETC_HAS_NETCMIX BIT(0)
|
||||
|
||||
struct netc_blk_ctrl {
|
||||
void __iomem *prb;
|
||||
void __iomem *ierb;
|
||||
void __iomem *netcmix;
|
||||
};
|
||||
|
||||
static void netc_reg_write(void __iomem *base, u32 offset, u32 val)
|
||||
{
|
||||
writel(val, base + offset);
|
||||
}
|
||||
|
||||
static u32 netc_reg_read(void __iomem *base, u32 offset)
|
||||
{
|
||||
return readl(base + offset);
|
||||
}
|
||||
|
||||
static int netc_of_pci_get_bus_devfn(ofnode node)
|
||||
{
|
||||
u32 reg[5];
|
||||
int error;
|
||||
|
||||
error = ofnode_read_u32_array(node, "reg", reg, ARRAY_SIZE(reg));
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
return (reg[0] >> 8) & 0xffff;
|
||||
}
|
||||
|
||||
static int netc_get_link_mii_protocol(phy_interface_t interface)
|
||||
{
|
||||
switch (interface) {
|
||||
case PHY_INTERFACE_MODE_MII:
|
||||
return MII_PROT_MII;
|
||||
case PHY_INTERFACE_MODE_RMII:
|
||||
return MII_PROT_RMII;
|
||||
case PHY_INTERFACE_MODE_RGMII:
|
||||
case PHY_INTERFACE_MODE_RGMII_ID:
|
||||
case PHY_INTERFACE_MODE_RGMII_RXID:
|
||||
case PHY_INTERFACE_MODE_RGMII_TXID:
|
||||
return MII_PROT_RGMII;
|
||||
case PHY_INTERFACE_MODE_SGMII:
|
||||
case PHY_INTERFACE_MODE_2500BASEX:
|
||||
case PHY_INTERFACE_MODE_10GBASER:
|
||||
case PHY_INTERFACE_MODE_XGMII:
|
||||
case PHY_INTERFACE_MODE_USXGMII:
|
||||
return MII_PROT_SERIAL;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int imx95_netcmix_init(struct udevice *dev)
|
||||
{
|
||||
struct netc_blk_ctrl *priv = dev_get_priv(dev);
|
||||
ofnode child, gchild;
|
||||
phy_interface_t interface;
|
||||
int bus_devfn, mii_proto;
|
||||
u32 val;
|
||||
|
||||
/* Default setting of MII protocol */
|
||||
val = MII_PROT(0, MII_PROT_RGMII) | MII_PROT(1, MII_PROT_RGMII) |
|
||||
MII_PROT(2, MII_PROT_SERIAL);
|
||||
|
||||
/* Update the link MII protocol through parsing phy-mode */
|
||||
dev_for_each_subnode(child, dev) {
|
||||
if (!ofnode_is_enabled(child))
|
||||
continue;
|
||||
|
||||
ofnode_for_each_subnode(gchild, child) {
|
||||
if (!ofnode_is_enabled(gchild))
|
||||
continue;
|
||||
|
||||
if (!ofnode_device_is_compatible(gchild, "pci1131,e101"))
|
||||
continue;
|
||||
|
||||
bus_devfn = netc_of_pci_get_bus_devfn(gchild);
|
||||
if (bus_devfn < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (bus_devfn == IMX95_ENETC2_BUS_DEVFN)
|
||||
continue;
|
||||
|
||||
interface = ofnode_read_phy_mode(gchild);
|
||||
if (interface == -1)
|
||||
continue;
|
||||
|
||||
mii_proto = netc_get_link_mii_protocol(interface);
|
||||
if (mii_proto < 0)
|
||||
return -EINVAL;
|
||||
|
||||
switch (bus_devfn) {
|
||||
case IMX95_ENETC0_BUS_DEVFN:
|
||||
val &= ~CFG_LINK_MII_PORT_0;
|
||||
val |= FIELD_PREP(CFG_LINK_MII_PORT_0, mii_proto);
|
||||
break;
|
||||
case IMX95_ENETC1_BUS_DEVFN:
|
||||
val &= ~CFG_LINK_MII_PORT_1;
|
||||
val |= FIELD_PREP(CFG_LINK_MII_PORT_1, mii_proto);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Configure Link I/O variant */
|
||||
netc_reg_write(priv->netcmix, IMX95_CFG_LINK_IO_VAR,
|
||||
IO_VAR(2, IO_VAR_16FF_16G_SERDES));
|
||||
/* Configure Link 2 PCS protocol */
|
||||
netc_reg_write(priv->netcmix, IMX95_CFG_LINK_PCS_PROT(2),
|
||||
PCS_PROT_10G_SXGMII);
|
||||
netc_reg_write(priv->netcmix, IMX95_CFG_LINK_MII_PROT, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool netc_ierb_is_locked(struct netc_blk_ctrl *priv)
|
||||
{
|
||||
return !!(netc_reg_read(priv->prb, PRB_NETCRR) & NETCRR_LOCK);
|
||||
}
|
||||
|
||||
static int netc_lock_ierb(struct netc_blk_ctrl *priv)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
netc_reg_write(priv->prb, PRB_NETCRR, NETCRR_LOCK);
|
||||
|
||||
return readl_poll_timeout(priv->prb + PRB_NETCSR, val,
|
||||
!(val & NETCSR_STATE), 2000);
|
||||
}
|
||||
|
||||
static int netc_unlock_ierb_with_warm_reset(struct netc_blk_ctrl *priv)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
netc_reg_write(priv->prb, PRB_NETCRR, 0);
|
||||
|
||||
return readl_poll_timeout(priv->prb + PRB_NETCRR, val,
|
||||
!(val & NETCRR_LOCK), 100000);
|
||||
}
|
||||
|
||||
static int imx95_ierb_init(struct udevice *dev)
|
||||
{
|
||||
struct netc_blk_ctrl *priv = dev_get_priv(dev);
|
||||
|
||||
/* EMDIO : No MSI-X intterupt */
|
||||
netc_reg_write(priv->ierb, IERB_EMDIOFAUXR, 0);
|
||||
/* ENETC0 PF */
|
||||
netc_reg_write(priv->ierb, IERB_EFAUXR(0), 0);
|
||||
/* ENETC0 VF0 */
|
||||
netc_reg_write(priv->ierb, IERB_VFAUXR(0), 1);
|
||||
/* ENETC0 VF1 */
|
||||
netc_reg_write(priv->ierb, IERB_VFAUXR(1), 2);
|
||||
/* ENETC1 PF */
|
||||
netc_reg_write(priv->ierb, IERB_EFAUXR(1), 3);
|
||||
/* ENETC1 VF0 */
|
||||
netc_reg_write(priv->ierb, IERB_VFAUXR(2), 5);
|
||||
/* ENETC1 VF1 */
|
||||
netc_reg_write(priv->ierb, IERB_VFAUXR(3), 6);
|
||||
/* ENETC2 PF */
|
||||
netc_reg_write(priv->ierb, IERB_EFAUXR(2), 4);
|
||||
/* ENETC2 VF0 */
|
||||
netc_reg_write(priv->ierb, IERB_VFAUXR(4), 5);
|
||||
/* ENETC2 VF1 */
|
||||
netc_reg_write(priv->ierb, IERB_VFAUXR(5), 6);
|
||||
/* NETC TIMER */
|
||||
netc_reg_write(priv->ierb, IERB_T0FAUXR, 7);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int netc_ierb_init(struct udevice *dev)
|
||||
{
|
||||
struct netc_blk_ctrl *priv = dev_get_priv(dev);
|
||||
int err;
|
||||
|
||||
if (netc_ierb_is_locked(priv)) {
|
||||
err = netc_unlock_ierb_with_warm_reset(priv);
|
||||
if (err) {
|
||||
dev_err(dev, "Unlock IERB failed.\n");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
err = imx95_ierb_init(dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = netc_lock_ierb(priv);
|
||||
if (err) {
|
||||
dev_err(dev, "Lock IERB failed.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int netc_prb_check_error(struct netc_blk_ctrl *priv)
|
||||
{
|
||||
if (netc_reg_read(priv->prb, PRB_NETCSR) & NETCSR_ERROR)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id netc_blk_ctrl_match[] = {
|
||||
{ .compatible = "nxp,imx95-netc-blk-ctrl" },
|
||||
{},
|
||||
};
|
||||
|
||||
static int netc_blk_ctrl_probe(struct udevice *dev)
|
||||
{
|
||||
struct netc_blk_ctrl *priv = dev_get_priv(dev);
|
||||
struct clk *ipg_clk;
|
||||
fdt_addr_t regs;
|
||||
int err;
|
||||
|
||||
ipg_clk = devm_clk_get_optional(dev, "ipg");
|
||||
if (IS_ERR(ipg_clk)) {
|
||||
dev_err(dev, "Set ipg clock failed\n");
|
||||
return PTR_ERR(ipg_clk);
|
||||
}
|
||||
|
||||
err = clk_prepare_enable(ipg_clk);
|
||||
if (err) {
|
||||
dev_err(dev, "Enable ipg clock failed\n");
|
||||
return PTR_ERR(ipg_clk);
|
||||
}
|
||||
|
||||
regs = dev_read_addr_name(dev, "ierb");
|
||||
if (regs == FDT_ADDR_T_NONE) {
|
||||
dev_err(dev, "Missing IERB resource\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
priv->ierb = (void __iomem *)regs;
|
||||
regs = dev_read_addr_name(dev, "prb");
|
||||
if (regs == FDT_ADDR_T_NONE) {
|
||||
dev_err(dev, "Missing PRB resource\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
priv->prb = (void __iomem *)regs;
|
||||
regs = dev_read_addr_name(dev, "netcmix");
|
||||
if (regs == FDT_ADDR_T_NONE) {
|
||||
dev_err(dev, "Missing NETCMIX resource\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
priv->netcmix = (void __iomem *)regs;
|
||||
|
||||
err = imx95_netcmix_init(dev);
|
||||
if (err) {
|
||||
dev_err(dev, "Initializing NETCMIX failed\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = netc_ierb_init(dev);
|
||||
if (err) {
|
||||
dev_err(dev, "Initializing IERB failed\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
if (netc_prb_check_error(priv) < 0)
|
||||
dev_warn(dev, "The current IERB configuration is invalid\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
U_BOOT_DRIVER(netc_blk_ctrl_drv) = {
|
||||
.name = "netc_blk_ctrl",
|
||||
.id = UCLASS_SIMPLE_BUS,
|
||||
.of_match = netc_blk_ctrl_match,
|
||||
.probe = netc_blk_ctrl_probe,
|
||||
.priv_auto = sizeof(struct netc_blk_ctrl),
|
||||
.flags = DM_FLAG_PRE_RELOC,
|
||||
};
|
||||
@@ -219,7 +219,7 @@ int imx_pinctrl_probe(struct udevice *dev,
|
||||
if (info->flags & IMX8_USE_SCU)
|
||||
return 0;
|
||||
|
||||
addr = ofnode_get_addr_size_index(dev_ofnode(dev), 0, &size);
|
||||
addr = ofnode_get_addr_size_index(node, 0, &size);
|
||||
if (addr == FDT_ADDR_T_NONE)
|
||||
return -EINVAL;
|
||||
|
||||
@@ -228,7 +228,7 @@ int imx_pinctrl_probe(struct udevice *dev,
|
||||
return -ENOMEM;
|
||||
priv->info = info;
|
||||
|
||||
info->mux_mask = ofnode_read_u32(node, "fsl,mux_mask", 0);
|
||||
info->mux_mask = ofnode_read_u32_default(node, "fsl,mux_mask", 0);
|
||||
/*
|
||||
* Refer to linux documentation for details:
|
||||
* Documentation/devicetree/bindings/pinctrl/fsl,imx7d-pinctrl.txt
|
||||
|
||||
Reference in New Issue
Block a user