[U-Boot] [PATCH v3 0/6] ls1028a: networking support

This patch set introduces ENETC ethernet and MDIO drivers, integrated in LS1028A NXP device and enables networking on the two existing boards. The SGMII interface labeled MAC0 is now functional on the reference design board (RDB). The RDB also features four switch ports, not enabled yet. On the development system (QDS) the RGMII port is now functional. Add-on cards are not yet enabled.
Depends on: - DM MDIO: https://patchwork.ozlabs.org/patch/1109368/ - PCI EA and FLR: https://patchwork.ozlabs.org/patch/1111608/
Changes in v3: - Added cover letter - Added serdes (SoC internal PHYs) configuration code - Use a single config option for ethernet and MDIO drivers as they are cross dependent - Use net_rx_packets for Rx and set SYS_RX_ETH_BUFFER to 8, minimum BD ring size supported by ENETC - MDIO driver now resides in its own file, read/write functions are reused by ethernet driver to access serdes - PCI IDs for our functions are no longer defined in pci_ids.h - several cosmetic updates, static functions, comments
Alex Marginean (6): include: configs: ls1028a: set SYS_RX_ETH_BUFFER to 8 drivers: net: add NXP ENETC ethernet driver drivers: net: add NXP ENETC MDIO driver drivers: net: apply serdes configuration for ENETC Ethernet interfaces arm: dts: ls1028a updates for network interfaces configs: ls1028a: enable networking options in rds, qds defconfig
arch/arm/dts/fsl-ls1028a-qds.dts | 13 + arch/arm/dts/fsl-ls1028a-rdb.dts | 13 + arch/arm/dts/fsl-ls1028a.dtsi | 24 ++ configs/ls1028aqds_tfa_defconfig | 5 +- configs/ls1028ardb_tfa_defconfig | 2 + drivers/net/Kconfig | 7 + drivers/net/Makefile | 1 + drivers/net/fsl_enetc.c | 583 +++++++++++++++++++++++++++++++ drivers/net/fsl_enetc.h | 227 ++++++++++++ drivers/net/fsl_enetc_mdio.c | 149 ++++++++ include/configs/ls1028a_common.h | 4 + 11 files changed, 1027 insertions(+), 1 deletion(-) create mode 100644 drivers/net/fsl_enetc.c create mode 100644 drivers/net/fsl_enetc.h create mode 100644 drivers/net/fsl_enetc_mdio.c

LS1028A ethernet interfaces work with at least 8 BDs, set number of buffers to match that.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com --- include/configs/ls1028a_common.h | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/include/configs/ls1028a_common.h b/include/configs/ls1028a_common.h index 0db86396e9..f9d2602afd 100644 --- a/include/configs/ls1028a_common.h +++ b/include/configs/ls1028a_common.h @@ -197,4 +197,8 @@ #define CONFIG_SYS_EEPROM_PAGE_WRITE_BITS 3 #define CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS 5
+/* Ethernet */ +/* smallest ENETC BD ring has 8 entries */ +#define CONFIG_SYS_RX_ETH_BUFFER 8 + #endif /* __L1028A_COMMON_H */

Hi Alex,
On Thu, Jun 20, 2019 at 10:48 PM Alex Marginean alexandru.marginean@nxp.com wrote:
LS1028A ethernet interfaces work with at least 8 BDs, set number of buffers to match that.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com
include/configs/ls1028a_common.h | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/include/configs/ls1028a_common.h b/include/configs/ls1028a_common.h index 0db86396e9..f9d2602afd 100644 --- a/include/configs/ls1028a_common.h +++ b/include/configs/ls1028a_common.h @@ -197,4 +197,8 @@ #define CONFIG_SYS_EEPROM_PAGE_WRITE_BITS 3 #define CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS 5
+/* Ethernet */ +/* smallest ENETC BD ring has 8 entries */ +#define CONFIG_SYS_RX_ETH_BUFFER 8
We should avoid adding more ad-hoc CONFIG_XXX macros. We should either make this a Kconfig option, or move this macro to the driver header file (eg: fsl_enetc.h) and do not prefix it with CONFIG_. If we move to driver header, this patch can be simply dropped as I see there are already macro defined in patch [2/6].
#endif /* __L1028A_COMMON_H */
Regards, Bin

Hi Bin,
On 7/1/2019 11:08 AM, Bin Meng wrote:
Hi Alex,
On Thu, Jun 20, 2019 at 10:48 PM Alex Marginean alexandru.marginean@nxp.com wrote:
LS1028A ethernet interfaces work with at least 8 BDs, set number of buffers to match that.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com
include/configs/ls1028a_common.h | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/include/configs/ls1028a_common.h b/include/configs/ls1028a_common.h index 0db86396e9..f9d2602afd 100644 --- a/include/configs/ls1028a_common.h +++ b/include/configs/ls1028a_common.h @@ -197,4 +197,8 @@ #define CONFIG_SYS_EEPROM_PAGE_WRITE_BITS 3 #define CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS 5
+/* Ethernet */ +/* smallest ENETC BD ring has 8 entries */ +#define CONFIG_SYS_RX_ETH_BUFFER 8
We should avoid adding more ad-hoc CONFIG_XXX macros. We should either make this a Kconfig option, or move this macro to the driver header file (eg: fsl_enetc.h) and do not prefix it with CONFIG_. If we move to driver header, this patch can be simply dropped as I see there are already macro defined in patch [2/6].
This is used in net.h to define PKTBUFSRX which is further used in net.c to define net_pkt_buf. I was actually planning to use net_pkt_buf instead of allocation memory in the driver, that's why I used it. I can use an enetc specific define, but then I either have to allocate packet data in the driver or put more code in to only use up to 4 (default CONFIG_SYS_RX_ETH_BUFFER value) BDs in an 8 entry BDR.
Alex
#endif /* __L1028A_COMMON_H */
Regards, Bin _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot

Hi Alex,
On Wed, Jul 3, 2019 at 1:46 AM Alex Marginean alexm.osslist@gmail.com wrote:
Hi Bin,
On 7/1/2019 11:08 AM, Bin Meng wrote:
Hi Alex,
On Thu, Jun 20, 2019 at 10:48 PM Alex Marginean alexandru.marginean@nxp.com wrote:
LS1028A ethernet interfaces work with at least 8 BDs, set number of buffers to match that.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com
include/configs/ls1028a_common.h | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/include/configs/ls1028a_common.h b/include/configs/ls1028a_common.h index 0db86396e9..f9d2602afd 100644 --- a/include/configs/ls1028a_common.h +++ b/include/configs/ls1028a_common.h @@ -197,4 +197,8 @@ #define CONFIG_SYS_EEPROM_PAGE_WRITE_BITS 3 #define CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS 5
+/* Ethernet */ +/* smallest ENETC BD ring has 8 entries */ +#define CONFIG_SYS_RX_ETH_BUFFER 8
We should avoid adding more ad-hoc CONFIG_XXX macros. We should either make this a Kconfig option, or move this macro to the driver header file (eg: fsl_enetc.h) and do not prefix it with CONFIG_. If we move to driver header, this patch can be simply dropped as I see there are already macro defined in patch [2/6].
This is used in net.h to define PKTBUFSRX which is further used in net.c to define net_pkt_buf. I was actually planning to use net_pkt_buf instead of allocation memory in the driver, that's why I used it. I can use an enetc specific define, but then I either have to allocate packet data in the driver or put more code in to only use up to 4 (default CONFIG_SYS_RX_ETH_BUFFER value) BDs in an 8 entry BDR.
Ah, thanks for the clarification. I missed that! Then this looks good to me.
Reviewed-by: Bin Meng bmeng.cn@gmail.com
Regards, Bin

Adds a driver for NXP ENETC ethernet controller currently integrated in LS1028A. ENETC is a fairly straight-forward BD ring device and interfaces are presented as PCI EPs on the SoC ECAM.
Signed-off-by: Catalin Horghidan catalin.horghidan@nxp.com Signed-off-by: Alex Marginean alexm.osslist@gmail.com --- drivers/net/Kconfig | 7 + drivers/net/Makefile | 1 + drivers/net/fsl_enetc.c | 380 ++++++++++++++++++++++++++++++++++++++++ drivers/net/fsl_enetc.h | 168 ++++++++++++++++++ 4 files changed, 556 insertions(+) create mode 100644 drivers/net/fsl_enetc.c create mode 100644 drivers/net/fsl_enetc.h
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 403df5e960..802eadf99d 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -588,4 +588,11 @@ config HIGMACV300_ETH This driver supports HIGMACV300 Ethernet controller found on HiSilicon SoCs.
+config FSL_ENETC + bool "NXP ENETC Ethernet controller" + depends on DM_PCI && DM_ETH + help + This driver supports the NXP ENETC Ethernet controller found on some + of the NXP SoCs. + endif # NETDEVICES diff --git a/drivers/net/Makefile b/drivers/net/Makefile index a3706ca27b..b9a6bd6297 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -79,3 +79,4 @@ obj-y += mscc_eswitch/ obj-$(CONFIG_HIGMACV300_ETH) += higmacv300.o obj-$(CONFIG_MDIO_SANDBOX) += mdio_sandbox.o obj-$(CONFIG_MDIO_MUX_SANDBOX) += mdio_mux_sandbox.o +obj-$(CONFIG_FSL_ENETC) += fsl_enetc.o diff --git a/drivers/net/fsl_enetc.c b/drivers/net/fsl_enetc.c new file mode 100644 index 0000000000..58ba63ceb1 --- /dev/null +++ b/drivers/net/fsl_enetc.c @@ -0,0 +1,380 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * ENETC ethernet controller driver + * Copyright 2017-2019 NXP + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <memalign.h> +#include <asm/io.h> +#include <pci.h> + +#include "fsl_enetc.h" + +/* + * Bind the device: + * - set a more explicit name on the interface + */ +static int enetc_bind(struct udevice *dev) +{ + char name[16]; + static int eth_num_devices; + + /* + * prefer using PCI function numbers to number interfaces, but these + * are only available if dts nodes are present. For PCI they are + * optional, handle that case too. Just in case some nodes are present + * and some are not, use different naming scheme - enetc-N based on + * PCI function # and enetc#N based on interface count + */ + if (ofnode_valid(dev->node)) + sprintf(name, "enetc-%u", PCI_FUNC(pci_get_devfn(dev))); + else + sprintf(name, "enetc#%u", eth_num_devices++); + device_set_name(dev, name); + + return 0; +} + +/* + * Probe ENETC driver: + * - initialize port and station interface BARs + */ +static int enetc_probe(struct udevice *dev) +{ + struct enetc_priv *priv = dev_get_priv(dev); + + if (ofnode_valid(dev->node) && !ofnode_is_available(dev->node)) { + enetc_dbg(dev, "interface disabled\n"); + return -ENODEV; + } + + priv->enetc_txbd = memalign(ENETC_BD_ALIGN, + sizeof(struct enetc_tx_bd) * ENETC_BD_CNT); + priv->enetc_rxbd = memalign(ENETC_BD_ALIGN, + sizeof(union enetc_rx_bd) * ENETC_BD_CNT); + + if (!priv->enetc_txbd || !priv->enetc_rxbd) { + /* free should be able to handle NULL, just free all pointers */ + free(priv->enetc_txbd); + free(priv->enetc_rxbd); + + return -ENOMEM; + } + + /* initialize register */ + priv->regs_base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, 0); + if (!priv->regs_base) { + enetc_dbg(dev, "failed to map BAR0\n"); + return -EINVAL; + } + priv->port_regs = priv->regs_base + ENETC_PORT_REGS_OFF; + + dm_pci_clrset_config16(dev, PCI_COMMAND, 0, PCI_COMMAND_MEMORY); + + return 0; +} + +/* + * Remove the driver from an interface: + * - free up allocated memory + */ +static int enetc_remove(struct udevice *dev) +{ + struct enetc_priv *priv = dev_get_priv(dev); + + free(priv->enetc_txbd); + free(priv->enetc_rxbd); + + return 0; +} + +/* ENETC Port MAC address registers, accepts big-endian format */ +static void enetc_set_primary_mac_addr(struct enetc_priv *priv, const u8 *addr) +{ + 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); +} + +/* Configure port parameters (# of rings, frame size, enable port) */ +static void enetc_enable_si_port(struct enetc_priv *priv) +{ + u32 val; + + /* 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); + /* set Rx max frame size */ + enetc_write_port(priv, ENETC_PM_MAXFRM, ENETC_RX_MAXFRM_SIZE); + /* enable MAC port */ + enetc_write_port(priv, ENETC_PM_CC, ENETC_PM_CC_RX_TX_EN); + /* enable port */ + enetc_write_port(priv, ENETC_PMR, ENETC_PMR_SI0_EN); + /* set SI cache policy */ + enetc_write(priv, ENETC_SICAR0, + ENETC_SICAR_RD_CFG | ENETC_SICAR_WR_CFG); + /* enable SI */ + enetc_write(priv, ENETC_SIMR, ENETC_SIMR_EN); +} + +/* returns DMA address for a given buffer index */ +static inline u64 enetc_rxb_address(struct udevice *dev, int i) +{ + return cpu_to_le64(dm_pci_virt_to_mem(dev, net_rx_packets[i])); +} + +/* + * Setup a single Tx BD Ring (ID = 0): + * - set Tx buffer descriptor address + * - set the BD count + * - initialize the producer and consumer index + */ +static void enetc_setup_tx_bdr(struct udevice *dev) +{ + struct enetc_priv *priv = dev_get_priv(dev); + struct bd_ring *tx_bdr = &priv->tx_bdr; + u64 tx_bd_add = (u64)priv->enetc_txbd; + + /* used later to advance to the next Tx BD */ + tx_bdr->bd_count = ENETC_BD_CNT; + tx_bdr->next_prod_idx = 0; + tx_bdr->next_cons_idx = 0; + tx_bdr->cons_idx = priv->regs_base + + ENETC_BDR(TX, ENETC_TX_BDR_ID, ENETC_TBCIR); + tx_bdr->prod_idx = priv->regs_base + + ENETC_BDR(TX, ENETC_TX_BDR_ID, ENETC_TBPIR); + + /* set Tx BD address */ + enetc_bdr_write(priv, TX, ENETC_TX_BDR_ID, ENETC_TBBAR0, + lower_32_bits(tx_bd_add)); + enetc_bdr_write(priv, TX, ENETC_TX_BDR_ID, ENETC_TBBAR1, + upper_32_bits(tx_bd_add)); + /* set Tx 8 BD count */ + enetc_bdr_write(priv, TX, ENETC_TX_BDR_ID, ENETC_TBLENR, + tx_bdr->bd_count); + + /* reset both producer/consumer indexes */ + enetc_write_reg(tx_bdr->cons_idx, tx_bdr->next_cons_idx); + enetc_write_reg(tx_bdr->prod_idx, tx_bdr->next_prod_idx); + + /* enable TX ring */ + enetc_bdr_write(priv, TX, ENETC_TX_BDR_ID, ENETC_TBMR, ENETC_TBMR_EN); +} + +/* + * Setup a single Rx BD Ring (ID = 0): + * - set Rx buffer descriptors address (one descriptor per buffer) + * - set buffer size as max frame size + * - enable Rx ring + * - reset consumer and producer indexes + * - set buffer for each descriptor + */ +static void enetc_setup_rx_bdr(struct udevice *dev) +{ + struct enetc_priv *priv = dev_get_priv(dev); + struct bd_ring *rx_bdr = &priv->rx_bdr; + u64 rx_bd_add = (u64)priv->enetc_rxbd; + int i; + + /* used later to advance to the next BD produced by ENETC HW */ + rx_bdr->bd_count = ENETC_BD_CNT; + rx_bdr->next_prod_idx = 0; + rx_bdr->next_cons_idx = 0; + rx_bdr->cons_idx = priv->regs_base + + ENETC_BDR(RX, ENETC_RX_BDR_ID, ENETC_RBCIR); + rx_bdr->prod_idx = priv->regs_base + + ENETC_BDR(RX, ENETC_RX_BDR_ID, ENETC_RBPIR); + + /* set Rx BD address */ + enetc_bdr_write(priv, RX, ENETC_RX_BDR_ID, ENETC_RBBAR0, + lower_32_bits(rx_bd_add)); + enetc_bdr_write(priv, RX, ENETC_RX_BDR_ID, ENETC_RBBAR1, + upper_32_bits(rx_bd_add)); + /* set Rx BD count (multiple of 8) */ + enetc_bdr_write(priv, RX, ENETC_RX_BDR_ID, ENETC_RBLENR, + rx_bdr->bd_count); + /* set Rx buffer size */ + enetc_bdr_write(priv, RX, ENETC_RX_BDR_ID, ENETC_RBBSR, PKTSIZE_ALIGN); + + /* fill Rx BD */ + memset(priv->enetc_rxbd, 0, + rx_bdr->bd_count * sizeof(union enetc_rx_bd)); + for (i = 0; i < rx_bdr->bd_count; i++) { + 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)); + } + + /* reset producer (ENETC owned) and consumer (SW owned) index */ + enetc_write_reg(rx_bdr->cons_idx, rx_bdr->next_cons_idx); + enetc_write_reg(rx_bdr->prod_idx, rx_bdr->next_prod_idx); + + /* enable Rx ring */ + enetc_bdr_write(priv, RX, ENETC_RX_BDR_ID, ENETC_RBMR, ENETC_RBMR_EN); +} + +/* + * Start ENETC interface: + * - perform FLR + * - enable access to port and SI registers + * - set mac address + * - setup TX/RX buffer descriptors + * - enable Tx/Rx rings + */ +static int enetc_start(struct udevice *dev) +{ + struct eth_pdata *plat = dev_get_platdata(dev); + struct enetc_priv *priv = dev_get_priv(dev); + + /* reset and enable the PCI device */ + dm_pci_flr(dev); + dm_pci_clrset_config16(dev, PCI_COMMAND, 0, + PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + + if (!is_valid_ethaddr(plat->enetaddr)) { + enetc_dbg(dev, "invalid MAC address, generate random ...\n"); + net_random_ethaddr(plat->enetaddr); + } + enetc_set_primary_mac_addr(priv, plat->enetaddr); + + enetc_enable_si_port(priv); + + /* setup Tx/Rx buffer descriptors */ + enetc_setup_tx_bdr(dev); + enetc_setup_rx_bdr(dev); + + return 0; +} + +/* + * Stop the network interface: + * - just quiesce it, we can wipe all configuration as _start starts from + * scratch each time + */ +static void enetc_stop(struct udevice *dev) +{ + /* FLR is sufficient to quiesce the device */ + dm_pci_flr(dev); +} + +/* + * ENETC transmit packet: + * - check if Tx BD ring is full + * - set buffer/packet address (dma address) + * - set final fragment flag + * - try while producer index equals consumer index or timeout + */ +static int enetc_send(struct udevice *dev, void *packet, int length) +{ + struct enetc_priv *priv = dev_get_priv(dev); + struct bd_ring *txr = &priv->tx_bdr; + void *nv_packet = (void *)packet; + int tries = ENETC_POLL_TRIES; + u32 pi, ci; + + pi = txr->next_prod_idx; + ci = enetc_read_reg(txr->cons_idx) & ENETC_BDR_IDX_MASK; + /* Tx ring is full when */ + if (((pi + 1) % txr->bd_count) == ci) { + enetc_dbg(dev, "Tx BDR full\n"); + return -ETIMEDOUT; + } + 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)); + + /* prepare Tx BD */ + memset(&priv->enetc_txbd[pi], 0x0, sizeof(struct enetc_tx_bd)); + priv->enetc_txbd[pi].addr = + cpu_to_le64(dm_pci_virt_to_mem(dev, nv_packet)); + 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(); + /* send frame: increment producer index */ + pi = (pi + 1) % txr->bd_count; + txr->next_prod_idx = pi; + enetc_write_reg(txr->prod_idx, pi); + while ((--tries >= 0) && + (pi != (enetc_read_reg(txr->cons_idx) & ENETC_BDR_IDX_MASK))) + udelay(10); + + return tries > 0 ? 0 : -ETIMEDOUT; +} + +/* + * Receive frame: + * - wait for the next BD to get ready bit set + * - clean up the descriptor + * - move on and indicate to HW that the cleaned BD is available for Rx + */ +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; + u32 status; + int len; + u8 rdy; + + do { + dmb(); + 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); + } while (--tries >= 0 && !rdy); + + if (!rdy) + return -EAGAIN; + + dmb(); + len = le16_to_cpu(priv->enetc_rxbd[pi].r.buf_len); + *packetp = (uchar *)enetc_rxb_address(dev, pi); + 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); + 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; +} + +static const struct eth_ops enetc_ops = { + .start = enetc_start, + .send = enetc_send, + .recv = enetc_recv, + .stop = enetc_stop, +}; + +U_BOOT_DRIVER(eth_enetc) = { + .name = "enetc_eth", + .id = UCLASS_ETH, + .bind = enetc_bind, + .probe = enetc_probe, + .remove = enetc_remove, + .ops = &enetc_ops, + .priv_auto_alloc_size = sizeof(struct enetc_priv), + .platdata_auto_alloc_size = sizeof(struct eth_pdata), +}; + +static struct pci_device_id enetc_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_ENETC_ETH) }, + {} +}; + +U_BOOT_PCI_DEVICE(eth_enetc, enetc_ids); diff --git a/drivers/net/fsl_enetc.h b/drivers/net/fsl_enetc.h new file mode 100644 index 0000000000..155ecc895b --- /dev/null +++ b/drivers/net/fsl_enetc.h @@ -0,0 +1,168 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * ENETC ethernet controller driver + * Copyright 2017-2019 NXP + */ + +#ifndef _ENETC_H +#define _ENETC_H + +#define enetc_dbg(dev, fmt, args...) debug("%s:" fmt, dev->name, ##args) + +/* PCI function IDs */ +#define PCI_DEVICE_ID_ENETC_ETH 0xE100 + +/* ENETC Ethernet controller registers */ +/* Station interface register offsets */ +#define ENETC_SIMR 0x000 +#define ENETC_SIMR_EN BIT(31) +#define ENETC_SICAR0 0x040 +/* 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_SIROCT 0x300 +#define ENETC_SIRFRM 0x308 +#define ENETC_SITOCT 0x320 +#define ENETC_SITFRM 0x328 + +/* Rx/Tx Buffer Descriptor Ring registers */ +enum enetc_bdr_type {TX, RX}; +#define ENETC_BDR(type, n, off) (0x8000 + (type) * 0x100 + (n) * 0x200 + (off)) +#define ENETC_BDR_IDX_MASK 0xffff + +/* Rx BDR reg offsets */ +#define ENETC_RBMR 0x00 +#define ENETC_RBMR_EN BIT(31) +#define ENETC_RBBSR 0x08 +/* initial consumer index for Rx BDR */ +#define ENETC_RBCIR 0x0c +#define ENETC_RBBAR0 0x10 +#define ENETC_RBBAR1 0x14 +#define ENETC_RBPIR 0x18 +#define ENETC_RBLENR 0x20 + +/* Tx BDR reg offsets */ +#define ENETC_TBMR 0x00 +#define ENETC_TBMR_EN BIT(31) +#define ENETC_TBBAR0 0x10 +#define ENETC_TBBAR1 0x14 +#define ENETC_TBPIR 0x18 +#define ENETC_TBCIR 0x1c +#define ENETC_TBLENR 0x20 + +/* Port registers offset */ +#define ENETC_PORT_REGS_OFF 0x10000 + +/* Port registers */ +#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_PSICFGR(n) (0x0940 + (n) * 0x10) +#define ENETC_PSICFGR_SET_TXBDR(val) ((val) & 0xff) +#define ENETC_PSICFGR_SET_RXBDR(val) (((val) & 0xff) << 16) +/* MAC configuration */ +#define ENETC_PM_CC 0x8008 +#define ENETC_PM_CC_DEFAULT 0x0810 +#define ENETC_PM_CC_RX_TX_EN 0x8813 +#define ENETC_PM_MAXFRM 0x8014 +#define ENETC_RX_MAXFRM_SIZE PKTSIZE_ALIGN + +/* buffer descriptors count must be multiple of 8 and aligned to 128 bytes */ +#define ENETC_BD_CNT CONFIG_SYS_RX_ETH_BUFFER +#define ENETC_BD_ALIGN 128 + +/* single pair of Rx/Tx rings */ +#define ENETC_RX_BDR_CNT 1 +#define ENETC_TX_BDR_CNT 1 +#define ENETC_RX_BDR_ID 0 +#define ENETC_TX_BDR_ID 0 + +/* Tx buffer descriptor */ +struct enetc_tx_bd { + __le64 addr; + __le16 buf_len; + __le16 frm_len; + __le16 err_csum; + __le16 flags; +}; + +#define ENETC_TXBD_FLAGS_F BIT(15) +#define ENETC_POLL_TRIES 32000 + +/* Rx buffer descriptor */ +union enetc_rx_bd { + /* SW provided BD format */ + struct { + __le64 addr; + u8 reserved[8]; + } w; + + /* ENETC returned BD format */ + struct { + __le16 inet_csum; + __le16 parse_summary; + __le32 rss_hash; + __le16 buf_len; + __le16 vlan_opt; + union { + struct { + __le16 flags; + __le16 error; + }; + __le32 lstatus; + }; + } r; +}; + +#define ENETC_RXBD_STATUS_R(status) (((status) >> 30) & 0x1) +#define ENETC_RXBD_STATUS_F(status) (((status) >> 31) & 0x1) +#define ENETC_RXBD_STATUS_ERRORS(status) (((status) >> 16) & 0xff) +#define ENETC_RXBD_STATUS(flags) ((flags) << 16) + +/* Tx/Rx ring info */ +struct bd_ring { + void *cons_idx; + void *prod_idx; + /* next BD index to use */ + int next_prod_idx; + int next_cons_idx; + int bd_count; +}; + +/* ENETC private structure */ +struct enetc_priv { + struct enetc_tx_bd *enetc_txbd; + union enetc_rx_bd *enetc_rxbd; + + void *regs_base; /* base ENETC registers */ + void *port_regs; /* base ENETC port registers */ + + /* Rx/Tx buffer descriptor rings info */ + struct bd_ring tx_bdr; + struct bd_ring rx_bdr; +}; + +/* 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) + +#endif /* _ENETC_H */

On Thu, Jun 20, 2019 at 10:48 PM Alex Marginean alexandru.marginean@nxp.com wrote:
Adds a driver for NXP ENETC ethernet controller currently integrated in LS1028A. ENETC is a fairly straight-forward BD ring device and interfaces are presented as PCI EPs on the SoC ECAM.
Signed-off-by: Catalin Horghidan catalin.horghidan@nxp.com Signed-off-by: Alex Marginean alexm.osslist@gmail.com
drivers/net/Kconfig | 7 + drivers/net/Makefile | 1 + drivers/net/fsl_enetc.c | 380 ++++++++++++++++++++++++++++++++++++++++ drivers/net/fsl_enetc.h | 168 ++++++++++++++++++ 4 files changed, 556 insertions(+) create mode 100644 drivers/net/fsl_enetc.c create mode 100644 drivers/net/fsl_enetc.h
Reviewed-by: Bin Meng bmeng.cn@gmail.com

Adds a driver for the MDIO interface currently integrated in LS1028A SoC. This MDIO interface is shared by multiple ethernet interfaces and is presented as a stand-alone PCI function on the SoC ECAM. Ethernet has a functional dependency on MDIO, for simplicity there is a single config option for both.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com --- drivers/net/Kconfig | 2 +- drivers/net/Makefile | 2 +- drivers/net/fsl_enetc.c | 57 ++++++++++++++ drivers/net/fsl_enetc.h | 21 +++++ drivers/net/fsl_enetc_mdio.c | 149 +++++++++++++++++++++++++++++++++++ 5 files changed, 229 insertions(+), 2 deletions(-) create mode 100644 drivers/net/fsl_enetc_mdio.c
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 802eadf99d..4d85fb1716 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -590,7 +590,7 @@ config HIGMACV300_ETH
config FSL_ENETC bool "NXP ENETC Ethernet controller" - depends on DM_PCI && DM_ETH + depends on DM_PCI && DM_ETH && DM_MDIO help This driver supports the NXP ENETC Ethernet controller found on some of the NXP SoCs. diff --git a/drivers/net/Makefile b/drivers/net/Makefile index b9a6bd6297..282d6d220f 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -79,4 +79,4 @@ obj-y += mscc_eswitch/ obj-$(CONFIG_HIGMACV300_ETH) += higmacv300.o obj-$(CONFIG_MDIO_SANDBOX) += mdio_sandbox.o obj-$(CONFIG_MDIO_MUX_SANDBOX) += mdio_mux_sandbox.o -obj-$(CONFIG_FSL_ENETC) += fsl_enetc.o +obj-$(CONFIG_FSL_ENETC) += fsl_enetc.o fsl_enetc_mdio.o diff --git a/drivers/net/fsl_enetc.c b/drivers/net/fsl_enetc.c index 58ba63ceb1..f14b484b26 100644 --- a/drivers/net/fsl_enetc.c +++ b/drivers/net/fsl_enetc.c @@ -10,6 +10,7 @@ #include <memalign.h> #include <asm/io.h> #include <pci.h> +#include <miiphy.h>
#include "fsl_enetc.h"
@@ -38,6 +39,59 @@ static int enetc_bind(struct udevice *dev) return 0; }
+/* Configure the actual/external ethernet PHY, if one is found */ +static void enetc_start_phy(struct udevice *dev) +{ + struct enetc_priv *priv = dev_get_priv(dev); + struct udevice *miidev; + struct phy_device *phy; + u32 phandle, phy_id; + ofnode phy_node; + int supported; + + if (!ofnode_valid(dev->node)) { + enetc_dbg(dev, "no enetc ofnode found, skipping PHY set-up\n"); + return; + } + + if (ofnode_read_u32(dev->node, "phy-handle", &phandle)) { + enetc_dbg(dev, "phy-handle not found, skipping PHY set-up\n"); + return; + } + + phy_node = ofnode_get_by_phandle(phandle); + if (!ofnode_valid(phy_node)) { + enetc_dbg(dev, "invalid phy node, skipping PHY set-up\n"); + return; + } + enetc_dbg(dev, "phy node: %s\n", ofnode_get_name(phy_node)); + + if (ofnode_read_u32(phy_node, "reg", &phy_id)) { + enetc_dbg(dev, + "missing reg in PHY node, skipping PHY set-up\n"); + return; + } + + if (uclass_get_device_by_ofnode(UCLASS_MDIO, + ofnode_get_parent(phy_node), + &miidev)) { + enetc_dbg(dev, "can't find MDIO bus for node %s\n", + ofnode_get_name(ofnode_get_parent(phy_node))); + return; + } + + phy = dm_mdio_phy_connect(miidev, phy_id, dev, priv->if_type); + if (!phy) { + enetc_dbg(dev, "dm_mdio_phy_connect returned null\n"); + return; + } + + supported = GENMASK(6, 0); /* speeds up to 1G & AN */ + phy->advertising = phy->supported & supported; + phy_config(phy); + phy_startup(phy); +} + /* * Probe ENETC driver: * - initialize port and station interface BARs @@ -249,6 +303,9 @@ static int enetc_start(struct udevice *dev) enetc_setup_tx_bdr(dev); enetc_setup_rx_bdr(dev);
+ priv->if_type = PHY_INTERFACE_MODE_NONE; + enetc_start_phy(dev); + return 0; }
diff --git a/drivers/net/fsl_enetc.h b/drivers/net/fsl_enetc.h index 155ecc895b..fbb9dfa15e 100644 --- a/drivers/net/fsl_enetc.h +++ b/drivers/net/fsl_enetc.h @@ -11,6 +11,7 @@
/* PCI function IDs */ #define PCI_DEVICE_ID_ENETC_ETH 0xE100 +#define PCI_DEVICE_ID_ENETC_MDIO 0xEE01
/* ENETC Ethernet controller registers */ /* Station interface register offsets */ @@ -143,6 +144,8 @@ struct enetc_priv { /* Rx/Tx buffer descriptor rings info */ struct bd_ring tx_bdr; struct bd_ring rx_bdr; + + int if_type; };
/* register accessors */ @@ -165,4 +168,22 @@ struct enetc_priv { #define enetc_bdr_write(priv, t, n, off, val) \ enetc_write(priv, ENETC_BDR(t, n, off), val)
+/* ENETC external MDIO registers */ +#define ENETC_MDIO_BASE 0x1c00 +#define ENETC_MDIO_CFG 0x00 +#define ENETC_EMDIO_CFG_C22 0x00809508 +#define ENETC_EMDIO_CFG_C45 0x00809548 +#define ENETC_EMDIO_CFG_RD_ER BIT(1) +#define ENETC_EMDIO_CFG_BSY BIT(0) +#define ENETC_MDIO_CTL 0x04 +#define ENETC_MDIO_CTL_READ BIT(15) +#define ENETC_MDIO_DATA 0x08 +#define ENETC_MDIO_STAT 0x0c + +#define ENETC_MDIO_READ_ERR 0xffff + +struct enetc_mdio_priv { + void *regs_base; +}; + #endif /* _ENETC_H */ diff --git a/drivers/net/fsl_enetc_mdio.c b/drivers/net/fsl_enetc_mdio.c new file mode 100644 index 0000000000..46afac06d7 --- /dev/null +++ b/drivers/net/fsl_enetc_mdio.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * ENETC ethernet controller driver + * Copyright 2019 NXP + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <pci.h> +#include <miiphy.h> +#include <asm/io.h> +#include <asm/processor.h> +#include <miiphy.h> + +#include "fsl_enetc.h" + +static void enetc_mdio_wait_bsy(struct enetc_mdio_priv *priv) +{ + while (enetc_read(priv, ENETC_MDIO_CFG) & ENETC_EMDIO_CFG_BSY) + cpu_relax(); +} + +static int enetc_mdio_read_priv(struct enetc_mdio_priv *priv, int addr, + int devad, int reg) +{ + if (devad == MDIO_DEVAD_NONE) + enetc_write(priv, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C22); + else + enetc_write(priv, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C45); + enetc_mdio_wait_bsy(priv); + + if (devad == MDIO_DEVAD_NONE) { + enetc_write(priv, ENETC_MDIO_CTL, ENETC_MDIO_CTL_READ | + (addr << 5) | reg); + } else { + enetc_write(priv, ENETC_MDIO_CTL, (addr << 5) + devad); + enetc_mdio_wait_bsy(priv); + + enetc_write(priv, ENETC_MDIO_STAT, reg); + enetc_mdio_wait_bsy(priv); + + enetc_write(priv, ENETC_MDIO_CTL, ENETC_MDIO_CTL_READ | + (addr << 5) | devad); + } + + enetc_mdio_wait_bsy(priv); + if (enetc_read(priv, ENETC_MDIO_CFG) & ENETC_EMDIO_CFG_RD_ER) + return ENETC_MDIO_READ_ERR; + + return enetc_read(priv, ENETC_MDIO_DATA); +} + +static int enetc_mdio_write_priv(struct enetc_mdio_priv *priv, int addr, + int devad, int reg, u16 val) +{ + if (devad == MDIO_DEVAD_NONE) + enetc_write(priv, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C22); + else + enetc_write(priv, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C45); + enetc_mdio_wait_bsy(priv); + + if (devad != MDIO_DEVAD_NONE) { + enetc_write(priv, ENETC_MDIO_CTL, (addr << 5) + devad); + enetc_write(priv, ENETC_MDIO_STAT, reg); + } else { + enetc_write(priv, ENETC_MDIO_CTL, (addr << 5) + reg); + } + enetc_mdio_wait_bsy(priv); + + enetc_write(priv, ENETC_MDIO_DATA, val); + enetc_mdio_wait_bsy(priv); + + return 0; +} + +/* DM wrappers */ +static int dm_enetc_mdio_read(struct udevice *dev, int addr, int devad, int reg) +{ + struct enetc_mdio_priv *priv = dev_get_priv(dev); + + return enetc_mdio_read_priv(priv, addr, devad, reg); +} + +static int dm_enetc_mdio_write(struct udevice *dev, int addr, int devad, + int reg, u16 val) +{ + struct enetc_mdio_priv *priv = dev_get_priv(dev); + + return enetc_mdio_write_priv(priv, addr, devad, reg, val); +} + +static const struct mdio_ops enetc_mdio_ops = { + .read = dm_enetc_mdio_read, + .write = dm_enetc_mdio_write, +}; + +static int enetc_mdio_bind(struct udevice *dev) +{ + char name[16]; + static int eth_num_devices; + + /* + * prefer using PCI function numbers to number interfaces, but these + * are only available if dts nodes are present. For PCI they are + * optional, handle that case too. Just in case some nodes are present + * and some are not, use different naming scheme - enetc-N based on + * PCI function # and enetc#N based on interface count + */ + if (ofnode_valid(dev->node)) + sprintf(name, "emdio-%u", PCI_FUNC(pci_get_devfn(dev))); + else + sprintf(name, "emdio#%u", eth_num_devices++); + device_set_name(dev, name); + + return 0; +} + +static int enetc_mdio_probe(struct udevice *dev) +{ + struct enetc_mdio_priv *priv = dev_get_priv(dev); + + priv->regs_base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, 0); + if (!priv->regs_base) { + enetc_dbg(dev, "failed to map BAR0\n"); + return -EINVAL; + } + + priv->regs_base += ENETC_MDIO_BASE; + + dm_pci_clrset_config16(dev, PCI_COMMAND, 0, PCI_COMMAND_MEMORY); + + return 0; +} + +U_BOOT_DRIVER(enetc_mdio) = { + .name = "enetc_mdio", + .id = UCLASS_MDIO, + .bind = enetc_mdio_bind, + .probe = enetc_mdio_probe, + .ops = &enetc_mdio_ops, + .priv_auto_alloc_size = sizeof(struct enetc_mdio_priv), +}; + +static struct pci_device_id enetc_mdio_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_ENETC_MDIO) }, +}; + +U_BOOT_PCI_DEVICE(enetc_mdio, enetc_mdio_ids);

On Thu, Jun 20, 2019 at 10:48 PM Alex Marginean alexandru.marginean@nxp.com wrote:
Adds a driver for the MDIO interface currently integrated in LS1028A SoC. This MDIO interface is shared by multiple ethernet interfaces and is presented as a stand-alone PCI function on the SoC ECAM. Ethernet has a functional dependency on MDIO, for simplicity there is a single config option for both.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com
drivers/net/Kconfig | 2 +- drivers/net/Makefile | 2 +- drivers/net/fsl_enetc.c | 57 ++++++++++++++ drivers/net/fsl_enetc.h | 21 +++++ drivers/net/fsl_enetc_mdio.c | 149 +++++++++++++++++++++++++++++++++++ 5 files changed, 229 insertions(+), 2 deletions(-) create mode 100644 drivers/net/fsl_enetc_mdio.c
Reviewed-by: Bin Meng bmeng.cn@gmail.com

Ethernet interfaces using serial protocols go through the serdes block integrated in the SoC. This is accessed over dedicated internal MDIOs which are part of the Ethernet PCI functions. Set up serdes at _start, along with other protocol specific port/MAC configuration. MDIO code is shared with enetc_mdio, read/write functions are exported from fsl_enetc_mdio for this reason.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com --- drivers/net/fsl_enetc.c | 148 ++++++++++++++++++++++++++++++++++- drivers/net/fsl_enetc.h | 38 +++++++++ drivers/net/fsl_enetc_mdio.c | 8 +- 3 files changed, 189 insertions(+), 5 deletions(-)
diff --git a/drivers/net/fsl_enetc.c b/drivers/net/fsl_enetc.c index f14b484b26..da533a1e5d 100644 --- a/drivers/net/fsl_enetc.c +++ b/drivers/net/fsl_enetc.c @@ -39,6 +39,152 @@ static int enetc_bind(struct udevice *dev) return 0; }
+/* MDIO wrappers, we're using these to drive internal MDIO to get to serdes */ +static int enetc_mdio_read(struct mii_dev *bus, int addr, int devad, int reg) +{ + struct enetc_mdio_priv priv; + + priv.regs_base = bus->priv; + return enetc_mdio_read_priv(&priv, addr, devad, reg); +} + +static int enetc_mdio_write(struct mii_dev *bus, int addr, int devad, int reg, + u16 val) +{ + struct enetc_mdio_priv priv; + + priv.regs_base = bus->priv; + return enetc_mdio_write_priv(&priv, addr, devad, reg, val); +} + +/* only interfaces that can pin out through serdes have internal MDIO */ +static bool enetc_has_imdio(struct udevice *dev) +{ + struct enetc_priv *priv = dev_get_priv(dev); + + return !!(priv->imdio.priv); +} + +/* set up serdes for SGMII */ +static int enetc_init_sgmii(struct udevice *dev) +{ + struct enetc_priv *priv = dev_get_priv(dev); + + if (!enetc_has_imdio(dev)) + return 0; + + /* Set to SGMII mode, use AN */ + enetc_mdio_write(&priv->imdio, ENETC_PCS_PHY_ADDR, MDIO_DEVAD_NONE, + ENETC_PCS_IF_MODE, ENETC_PCS_IF_MODE_SGMII_AN); + + /* Dev ability - SGMII */ + enetc_mdio_write(&priv->imdio, ENETC_PCS_PHY_ADDR, MDIO_DEVAD_NONE, + ENETC_PCS_DEV_ABILITY, ENETC_PCS_DEV_ABILITY_SGMII); + + /* Adjust link timer for SGMII */ + enetc_mdio_write(&priv->imdio, ENETC_PCS_PHY_ADDR, MDIO_DEVAD_NONE, + ENETC_PCS_LINK_TIMER1, ENETC_PCS_LINK_TIMER1_VAL); + enetc_mdio_write(&priv->imdio, ENETC_PCS_PHY_ADDR, MDIO_DEVAD_NONE, + ENETC_PCS_LINK_TIMER2, ENETC_PCS_LINK_TIMER2_VAL); + + /* restart PCS AN */ + enetc_mdio_write(&priv->imdio, ENETC_PCS_PHY_ADDR, MDIO_DEVAD_NONE, + ENETC_PCS_CR, + ENETC_PCS_CR_RESET_AN | ENETC_PCS_CR_DEF_VAL); + + return 0; +} + +/* set up MAC for RGMII */ +static int enetc_init_rgmii(struct udevice *dev) +{ + struct enetc_priv *priv = dev_get_priv(dev); + u32 if_mode; + + /* enable RGMII AN */ + if_mode = enetc_read_port(priv, ENETC_PM_IF_MODE); + if_mode |= ENETC_PM_IF_MODE_AN_ENA; + enetc_write_port(priv, ENETC_PM_IF_MODE, if_mode); + + return 0; +} + +/* set up MAC and serdes for SXGMII */ +static int enetc_init_sxgmii(struct udevice *dev) +{ + struct enetc_priv *priv = dev_get_priv(dev); + u32 if_mode; + + /* 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 (!enetc_has_imdio(dev)) + return 0; + + /* Dev ability - SXGMII */ + enetc_mdio_write(&priv->imdio, ENETC_PCS_PHY_ADDR, ENETC_PCS_DEVAD_REPL, + ENETC_PCS_DEV_ABILITY, ENETC_PCS_DEV_ABILITY_SXGMII); + + /* Restart PCS AN */ + enetc_mdio_write(&priv->imdio, ENETC_PCS_PHY_ADDR, ENETC_PCS_DEVAD_REPL, + ENETC_PCS_CR, + ENETC_PCS_CR_LANE_RESET | ENETC_PCS_CR_RESET_AN); + + return 0; +} + +/* Apply protocol specific configuration to MAC, serdes as needed */ +static void enetc_start_pcs(struct udevice *dev) +{ + struct enetc_priv *priv = dev_get_priv(dev); + const char *if_str; + + priv->if_type = PHY_INTERFACE_MODE_NONE; + + /* check internal mdio capability, not all ports need it */ + if (enetc_read_port(priv, ENETC_PCAPR0) & ENETC_PCAPRO_MDIO) { + /* + * set up internal MDIO, this is part of ETH PCI function and is + * used to access serdes / internal SoC PHYs. + * We don't currently register it as a MDIO bus as it goes away + * when the interface is removed, so it can't practically be + * used in the console. + */ + priv->imdio.read = enetc_mdio_read; + priv->imdio.write = enetc_mdio_write; + priv->imdio.priv = priv->port_regs + ENETC_PM_IMDIO_BASE; + strncpy(priv->imdio.name, dev->name, MDIO_NAME_LEN); + } + + if (!ofnode_valid(dev->node)) { + enetc_dbg(dev, "no enetc ofnode found, skipping PCS set-up\n"); + return; + } + + if_str = ofnode_read_string(dev->node, "phy-mode"); + if (if_str) + priv->if_type = phy_get_interface_by_name(if_str); + else + enetc_dbg(dev, + "phy-mode property not found, defaulting to SGMII\n"); + if (priv->if_type < 0) + priv->if_type = PHY_INTERFACE_MODE_NONE; + + switch (priv->if_type) { + case PHY_INTERFACE_MODE_SGMII: + enetc_init_sgmii(dev); + break; + case PHY_INTERFACE_MODE_RGMII: + enetc_init_rgmii(dev); + break; + case PHY_INTERFACE_MODE_XGMII: + enetc_init_sxgmii(dev); + break; + }; +} + /* Configure the actual/external ethernet PHY, if one is found */ static void enetc_start_phy(struct udevice *dev) { @@ -303,7 +449,7 @@ static int enetc_start(struct udevice *dev) enetc_setup_tx_bdr(dev); enetc_setup_rx_bdr(dev);
- priv->if_type = PHY_INTERFACE_MODE_NONE; + enetc_start_pcs(dev); enetc_start_phy(dev);
return 0; diff --git a/drivers/net/fsl_enetc.h b/drivers/net/fsl_enetc.h index fbb9dfa15e..ac8d38da92 100644 --- a/drivers/net/fsl_enetc.h +++ b/drivers/net/fsl_enetc.h @@ -61,6 +61,8 @@ enum enetc_bdr_type {TX, RX}; #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) @@ -70,6 +72,11 @@ enum enetc_bdr_type {TX, RX}; #define ENETC_PM_CC_RX_TX_EN 0x8813 #define ENETC_PM_MAXFRM 0x8014 #define ENETC_RX_MAXFRM_SIZE PKTSIZE_ALIGN +#define ENETC_PM_IMDIO_BASE 0x8030 +#define ENETC_PM_IF_MODE 0x8300 +#define ENETC_PM_IF_MODE_RG BIT(2) +#define ENETC_PM_IF_MODE_AN_ENA BIT(15) +#define ENETC_PM_IF_IFMODE_MASK GENMASK(1, 0)
/* buffer descriptors count must be multiple of 8 and aligned to 128 bytes */ #define ENETC_BD_CNT CONFIG_SYS_RX_ETH_BUFFER @@ -146,6 +153,7 @@ struct enetc_priv { struct bd_ring rx_bdr;
int if_type; + struct mii_dev imdio; };
/* register accessors */ @@ -168,6 +176,27 @@ struct enetc_priv { #define enetc_bdr_write(priv, t, n, off, val) \ enetc_write(priv, ENETC_BDR(t, n, off), val)
+/* PCS / internal SoC PHY ID, it defaults to 0 on all interfaces */ +#define ENETC_PCS_PHY_ADDR 0 + +/* PCS registers */ +#define ENETC_PCS_CR 0x00 +#define ENETC_PCS_CR_RESET_AN 0x1200 +#define ENETC_PCS_CR_DEF_VAL 0x0140 +#define ENETC_PCS_CR_LANE_RESET 0x8000 +#define ENETC_PCS_DEV_ABILITY 0x04 +#define ENETC_PCS_DEV_ABILITY_SGMII 0x4001 +#define ENETC_PCS_DEV_ABILITY_SXGMII 0x5001 +#define ENETC_PCS_LINK_TIMER1 0x12 +#define ENETC_PCS_LINK_TIMER1_VAL 0x06a0 +#define ENETC_PCS_LINK_TIMER2 0x13 +#define ENETC_PCS_LINK_TIMER2_VAL 0x0003 +#define ENETC_PCS_IF_MODE 0x14 +#define ENETC_PCS_IF_MODE_SGMII_AN 0x0003 + +/* PCS replicator block for USXGMII */ +#define ENETC_PCS_DEVAD_REPL 0x1f + /* ENETC external MDIO registers */ #define ENETC_MDIO_BASE 0x1c00 #define ENETC_MDIO_CFG 0x00 @@ -186,4 +215,13 @@ struct enetc_mdio_priv { void *regs_base; };
+/* + * these functions are implemented by ENETC_MDIO and are re-used by ENETC driver + * to drive serdes / internal SoC PHYs + */ +int enetc_mdio_read_priv(struct enetc_mdio_priv *priv, int addr, int devad, + int reg); +int enetc_mdio_write_priv(struct enetc_mdio_priv *priv, int addr, int devad, + int reg, u16 val); + #endif /* _ENETC_H */ diff --git a/drivers/net/fsl_enetc_mdio.c b/drivers/net/fsl_enetc_mdio.c index 46afac06d7..60d21537b8 100644 --- a/drivers/net/fsl_enetc_mdio.c +++ b/drivers/net/fsl_enetc_mdio.c @@ -21,8 +21,8 @@ static void enetc_mdio_wait_bsy(struct enetc_mdio_priv *priv) cpu_relax(); }
-static int enetc_mdio_read_priv(struct enetc_mdio_priv *priv, int addr, - int devad, int reg) +int enetc_mdio_read_priv(struct enetc_mdio_priv *priv, int addr, int devad, + int reg) { if (devad == MDIO_DEVAD_NONE) enetc_write(priv, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C22); @@ -51,8 +51,8 @@ static int enetc_mdio_read_priv(struct enetc_mdio_priv *priv, int addr, return enetc_read(priv, ENETC_MDIO_DATA); }
-static int enetc_mdio_write_priv(struct enetc_mdio_priv *priv, int addr, - int devad, int reg, u16 val) +int enetc_mdio_write_priv(struct enetc_mdio_priv *priv, int addr, int devad, + int reg, u16 val) { if (devad == MDIO_DEVAD_NONE) enetc_write(priv, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C22);

On Thu, Jun 20, 2019 at 10:48 PM Alex Marginean alexandru.marginean@nxp.com wrote:
Ethernet interfaces using serial protocols go through the serdes block integrated in the SoC. This is accessed over dedicated internal MDIOs which are part of the Ethernet PCI functions. Set up serdes at _start, along with other protocol specific port/MAC configuration. MDIO code is shared with enetc_mdio, read/write functions are exported from fsl_enetc_mdio for this reason.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com
drivers/net/fsl_enetc.c | 148 ++++++++++++++++++++++++++++++++++- drivers/net/fsl_enetc.h | 38 +++++++++ drivers/net/fsl_enetc_mdio.c | 8 +- 3 files changed, 189 insertions(+), 5 deletions(-)
Reviewed-by: Bin Meng bmeng.cn@gmail.com

Defines LS1028A RDB SGMII port, QDS RGMII port.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com --- arch/arm/dts/fsl-ls1028a-qds.dts | 13 +++++++++++++ arch/arm/dts/fsl-ls1028a-rdb.dts | 13 +++++++++++++ arch/arm/dts/fsl-ls1028a.dtsi | 24 ++++++++++++++++++++++++ 3 files changed, 50 insertions(+)
diff --git a/arch/arm/dts/fsl-ls1028a-qds.dts b/arch/arm/dts/fsl-ls1028a-qds.dts index 46a0419d77..94d0aa0f95 100644 --- a/arch/arm/dts/fsl-ls1028a-qds.dts +++ b/arch/arm/dts/fsl-ls1028a-qds.dts @@ -86,3 +86,16 @@ &usb2 { status = "okay"; }; + +&enetc1 { + status = "okay"; + phy-mode = "rgmii"; + phy-handle = <&qds_phy0>; +}; + +&mdio0 { + status = "okay"; + qds_phy0: phy@5 { + reg = <5>; + }; +}; diff --git a/arch/arm/dts/fsl-ls1028a-rdb.dts b/arch/arm/dts/fsl-ls1028a-rdb.dts index 932cfa2275..052538937b 100644 --- a/arch/arm/dts/fsl-ls1028a-rdb.dts +++ b/arch/arm/dts/fsl-ls1028a-rdb.dts @@ -86,3 +86,16 @@ &usb2 { status = "okay"; }; + +&enetc0 { + status = "okay"; + phy-mode = "sgmii"; + phy-handle = <&rdb_phy0>; +}; + +&mdio0 { + status = "okay"; + rdb_phy0: phy@2 { + reg = <2>; + }; +}; diff --git a/arch/arm/dts/fsl-ls1028a.dtsi b/arch/arm/dts/fsl-ls1028a.dtsi index b2b55398bb..613b362b50 100644 --- a/arch/arm/dts/fsl-ls1028a.dtsi +++ b/arch/arm/dts/fsl-ls1028a.dtsi @@ -117,6 +117,30 @@ #size-cells = <2>; device_type = "pci"; ranges= <0x82000000 0x0 0x00000000 0x1 0xf8000000 0x0 0x160000>; + enetc0: pci@0,0 { + reg = <0x000000 0 0 0 0>; + status = "disabled"; + }; + enetc1: pci@0,1 { + reg = <0x000100 0 0 0 0>; + status = "disabled"; + }; + enetc2: pci@0,2 { + reg = <0x000200 0 0 0 0>; + status = "okay"; + phy-mode = "internal"; + }; + mdio0: pci@0,3 { + #address-cells=<0>; + #size-cells=<1>; + reg = <0x000300 0 0 0 0>; + status = "disabled"; + }; + enetc6: pci@0,6 { + reg = <0x000600 0 0 0 0>; + status = "okay"; + phy-mode = "internal"; + }; };
i2c0: i2c@2000000 {

On Thu, Jun 20, 2019 at 10:48 PM Alex Marginean alexandru.marginean@nxp.com wrote:
Defines LS1028A RDB SGMII port, QDS RGMII port.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com
arch/arm/dts/fsl-ls1028a-qds.dts | 13 +++++++++++++ arch/arm/dts/fsl-ls1028a-rdb.dts | 13 +++++++++++++ arch/arm/dts/fsl-ls1028a.dtsi | 24 ++++++++++++++++++++++++ 3 files changed, 50 insertions(+)
Reviewed-by: Bin Meng bmeng.cn@gmail.com

Enables ethernet, MDIO, PHY drivers for LS1028A RDB and QDS.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com --- configs/ls1028aqds_tfa_defconfig | 5 ++++- configs/ls1028ardb_tfa_defconfig | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/configs/ls1028aqds_tfa_defconfig b/configs/ls1028aqds_tfa_defconfig index 7982ce4157..2ea594c43a 100644 --- a/configs/ls1028aqds_tfa_defconfig +++ b/configs/ls1028aqds_tfa_defconfig @@ -41,10 +41,13 @@ CONFIG_SPI_FLASH_SPANSION=y CONFIG_SPI_FLASH_STMICRO=y # CONFIG_SPI_FLASH_USE_4K_SECTORS is not set CONFIG_PHYLIB=y +CONFIG_PHY_AQUANTIA=y CONFIG_PHY_ATHEROS=y +CONFIG_PHY_VITESSE=y CONFIG_DM_ETH=y -CONFIG_PHY_GIGE=y +CONFIG_DM_MDIO=y CONFIG_E1000=y +CONFIG_FSL_ENETC=y CONFIG_PCI=y CONFIG_DM_PCI=y CONFIG_DM_PCI_COMPAT=y diff --git a/configs/ls1028ardb_tfa_defconfig b/configs/ls1028ardb_tfa_defconfig index c65e37df79..3f5bc2e139 100644 --- a/configs/ls1028ardb_tfa_defconfig +++ b/configs/ls1028ardb_tfa_defconfig @@ -43,8 +43,10 @@ CONFIG_SPI_FLASH_STMICRO=y CONFIG_PHYLIB=y CONFIG_PHY_ATHEROS=y CONFIG_DM_ETH=y +CONFIG_DM_MDIO=y CONFIG_PHY_GIGE=y CONFIG_E1000=y +CONFIG_FSL_ENETC=y CONFIG_PCI=y CONFIG_DM_PCI=y CONFIG_DM_PCI_COMPAT=y

Hi Alex,
On Thu, Jun 20, 2019 at 10:48 PM Alex Marginean alexandru.marginean@nxp.com wrote:
The commit title says: rds. It should be rdb.
Enables ethernet, MDIO, PHY drivers for LS1028A RDB and QDS.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com
configs/ls1028aqds_tfa_defconfig | 5 ++++- configs/ls1028ardb_tfa_defconfig | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-)
Other than that, Reviewed-by: Bin Meng bmeng.cn@gmail.com
Regards, Bin

On 7/1/2019 11:08 AM, Bin Meng wrote:
Hi Alex,
On Thu, Jun 20, 2019 at 10:48 PM Alex Marginean alexandru.marginean@nxp.com wrote:
The commit title says: rds. It should be rdb.
You're right, I'll fix it. You have a really good eye for detail! Thank you! Alex
Enables ethernet, MDIO, PHY drivers for LS1028A RDB and QDS.
Signed-off-by: Alex Marginean alexm.osslist@gmail.com
configs/ls1028aqds_tfa_defconfig | 5 ++++- configs/ls1028ardb_tfa_defconfig | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-)
Other than that, Reviewed-by: Bin Meng bmeng.cn@gmail.com
Regards, Bin _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot
participants (3)
-
Alex Marginean
-
Alex Marginean
-
Bin Meng