On Mon, Oct 9, 2017 at 4:11 AM, Calvin Johnson <calvin.john...@nxp.com> wrote: > This patch adds PFE driver into U-Boot. > > Following are the main driver files:- > pfe.c: provides low level helper functions to initialize PFE internal > processor engines and other hardware blocks. > pfe_driver.c: provides probe functions, initialization functions > and packet send and receive functions. > pfe_eth.c: provides high level gemac, phy and mdio initialization > functions. > pfe_firmware.c: provides functions to load firmware into PFE > internal processor engines. > > Signed-off-by: Calvin Johnson <calvin.john...@nxp.com> > Signed-off-by: Anjaneyulu Jagarlmudi <anji.jagarlm...@nxp.com> > --- > drivers/net/pfe_eth/Kconfig | 8 + > drivers/net/pfe_eth/Makefile | 10 + > drivers/net/pfe_eth/pfe.c | 1161 > ++++++++++++++++++++++++++++++++++++ > drivers/net/pfe_eth/pfe_driver.c | 626 +++++++++++++++++++ > drivers/net/pfe_eth/pfe_eth.c | 545 +++++++++++++++++ > drivers/net/pfe_eth/pfe_firmware.c | 230 +++++++ > 6 files changed, 2580 insertions(+) > create mode 100644 drivers/net/pfe_eth/Kconfig > create mode 100644 drivers/net/pfe_eth/Makefile > create mode 100644 drivers/net/pfe_eth/pfe.c > create mode 100644 drivers/net/pfe_eth/pfe_driver.c > create mode 100644 drivers/net/pfe_eth/pfe_eth.c > create mode 100644 drivers/net/pfe_eth/pfe_firmware.c > > diff --git a/drivers/net/pfe_eth/Kconfig b/drivers/net/pfe_eth/Kconfig > new file mode 100644 > index 0000000..b9996df > --- /dev/null > +++ b/drivers/net/pfe_eth/Kconfig > @@ -0,0 +1,8 @@ > +config UTIL_PE_DISABLED > + bool > + help > + Disable UTIL processor engine of PFE > + > +config SYS_FSL_PPFE_ADDR > + hex "PFE base address" > + default 0x04000000 > diff --git a/drivers/net/pfe_eth/Makefile b/drivers/net/pfe_eth/Makefile > new file mode 100644 > index 0000000..e78f1bf > --- /dev/null > +++ b/drivers/net/pfe_eth/Makefile > @@ -0,0 +1,10 @@ > +# Copyright 2015-2016 Freescale Semiconductor, Inc. > +# Copyright 2017 NXP > +# > +# SPDX-License-Identifier:GPL-2.0+ > + > +# Layerscape PFE driver > +obj-y += pfe.o \ > + pfe_driver.o \ > + pfe_eth.o \ > + pfe_firmware.o > diff --git a/drivers/net/pfe_eth/pfe.c b/drivers/net/pfe_eth/pfe.c > new file mode 100644 > index 0000000..fc6631e > --- /dev/null > +++ b/drivers/net/pfe_eth/pfe.c > @@ -0,0 +1,1161 @@ > +/* > + * Copyright 2015-2016 Freescale Semiconductor, Inc. > + * Copyright 2017 NXP > + * > + * SPDX-License-Identifier:GPL-2.0+ > + */ > +#include <pfe_eth/pfe_eth.h> > +#include <pfe_eth/pfe/pfe.h> > + > +void *ddr_base_addr; > +unsigned long ddr_phys_base_addr; > +static struct pe_info pe[MAX_PE]; > + > +/* > + * Initializes the PFE library. > + * Must be called before using any of the library functions. > + * > + * @param[in] cbus_base CBUS virtual base address (as mapped > in > + * the host CPU address space) > + * @param[in] ddr_base DDR virtual base address (as mapped in > + * the host CPU address space) > + * @param[in] ddr_phys_base DDR physical base address (as mapped in > + * platform) > + */ > +void pfe_lib_init(void *ddr_base, unsigned long ddr_phys_base)
Could you use some loops here to consolidate this code some? > +{ > + ddr_base_addr = ddr_base; > + ddr_phys_base_addr = ddr_phys_base; > + > + pe[CLASS0_ID].dmem_base_addr = (u32)CLASS_DMEM_BASE_ADDR(0); > + pe[CLASS0_ID].pmem_base_addr = (u32)CLASS_IMEM_BASE_ADDR(0); > + pe[CLASS0_ID].pmem_size = (u32)CLASS_IMEM_SIZE; > + pe[CLASS0_ID].mem_access_wdata = (void *)CLASS_MEM_ACCESS_WDATA; > + pe[CLASS0_ID].mem_access_addr = (void *)CLASS_MEM_ACCESS_ADDR; > + pe[CLASS0_ID].mem_access_rdata = (void *)CLASS_MEM_ACCESS_RDATA; > + > + pe[CLASS1_ID].dmem_base_addr = (u32)CLASS_DMEM_BASE_ADDR(1); > + pe[CLASS1_ID].pmem_base_addr = (u32)CLASS_IMEM_BASE_ADDR(1); > + pe[CLASS1_ID].pmem_size = (u32)CLASS_IMEM_SIZE; > + pe[CLASS1_ID].mem_access_wdata = (void *)CLASS_MEM_ACCESS_WDATA; > + pe[CLASS1_ID].mem_access_addr = (void *)CLASS_MEM_ACCESS_ADDR; > + pe[CLASS1_ID].mem_access_rdata = (void *)CLASS_MEM_ACCESS_RDATA; > + > + pe[CLASS2_ID].dmem_base_addr = (u32)CLASS_DMEM_BASE_ADDR(2); > + pe[CLASS2_ID].pmem_base_addr = (u32)CLASS_IMEM_BASE_ADDR(2); > + pe[CLASS2_ID].pmem_size = (u32)CLASS_IMEM_SIZE; > + pe[CLASS2_ID].mem_access_wdata = (void *)CLASS_MEM_ACCESS_WDATA; > + pe[CLASS2_ID].mem_access_addr = (void *)CLASS_MEM_ACCESS_ADDR; > + pe[CLASS2_ID].mem_access_rdata = (void *)CLASS_MEM_ACCESS_RDATA; > + > + pe[CLASS3_ID].dmem_base_addr = (u32)CLASS_DMEM_BASE_ADDR(3); > + pe[CLASS3_ID].pmem_base_addr = (u32)CLASS_IMEM_BASE_ADDR(3); > + pe[CLASS3_ID].pmem_size = (u32)CLASS_IMEM_SIZE; > + pe[CLASS3_ID].mem_access_wdata = (void *)CLASS_MEM_ACCESS_WDATA; > + pe[CLASS3_ID].mem_access_addr = (void *)CLASS_MEM_ACCESS_ADDR; > + pe[CLASS3_ID].mem_access_rdata = (void *)CLASS_MEM_ACCESS_RDATA; > + > + pe[CLASS4_ID].dmem_base_addr = (u32)CLASS_DMEM_BASE_ADDR(4); > + pe[CLASS4_ID].pmem_base_addr = (u32)CLASS_IMEM_BASE_ADDR(4); > + pe[CLASS4_ID].pmem_size = (u32)CLASS_IMEM_SIZE; > + pe[CLASS4_ID].mem_access_wdata = (void *)CLASS_MEM_ACCESS_WDATA; > + pe[CLASS4_ID].mem_access_addr = (void *)CLASS_MEM_ACCESS_ADDR; > + pe[CLASS4_ID].mem_access_rdata = (void *)CLASS_MEM_ACCESS_RDATA; > + > + pe[CLASS5_ID].dmem_base_addr = (u32)CLASS_DMEM_BASE_ADDR(5); > + pe[CLASS5_ID].pmem_base_addr = (u32)CLASS_IMEM_BASE_ADDR(5); > + pe[CLASS5_ID].pmem_size = (u32)CLASS_IMEM_SIZE; > + pe[CLASS5_ID].mem_access_wdata = (void *)CLASS_MEM_ACCESS_WDATA; > + pe[CLASS5_ID].mem_access_addr = (void *)CLASS_MEM_ACCESS_ADDR; > + pe[CLASS5_ID].mem_access_rdata = (void *)CLASS_MEM_ACCESS_RDATA; > + > + pe[TMU0_ID].dmem_base_addr = (u32)TMU_DMEM_BASE_ADDR(0); > + pe[TMU0_ID].pmem_base_addr = (u32)TMU_IMEM_BASE_ADDR(0); > + pe[TMU0_ID].pmem_size = (u32)TMU_IMEM_SIZE; > + pe[TMU0_ID].mem_access_wdata = (void *)TMU_MEM_ACCESS_WDATA; > + pe[TMU0_ID].mem_access_addr = (void *)TMU_MEM_ACCESS_ADDR; > + pe[TMU0_ID].mem_access_rdata = (void *)TMU_MEM_ACCESS_RDATA; > + > + pe[TMU1_ID].dmem_base_addr = (u32)TMU_DMEM_BASE_ADDR(1); > + pe[TMU1_ID].pmem_base_addr = (u32)TMU_IMEM_BASE_ADDR(1); > + pe[TMU1_ID].pmem_size = (u32)TMU_IMEM_SIZE; > + pe[TMU1_ID].mem_access_wdata = (void *)TMU_MEM_ACCESS_WDATA; > + pe[TMU1_ID].mem_access_addr = (void *)TMU_MEM_ACCESS_ADDR; > + pe[TMU1_ID].mem_access_rdata = (void *)TMU_MEM_ACCESS_RDATA; > + > + pe[TMU3_ID].dmem_base_addr = (u32)TMU_DMEM_BASE_ADDR(3); > + pe[TMU3_ID].pmem_base_addr = (u32)TMU_IMEM_BASE_ADDR(3); > + pe[TMU3_ID].pmem_size = (u32)TMU_IMEM_SIZE; > + pe[TMU3_ID].mem_access_wdata = (void *)TMU_MEM_ACCESS_WDATA; > + pe[TMU3_ID].mem_access_addr = (void *)TMU_MEM_ACCESS_ADDR; > + pe[TMU3_ID].mem_access_rdata = (void *)TMU_MEM_ACCESS_RDATA; > + > +#if !defined(CONFIG_UTIL_PE_DISABLED) > + pe[UTIL_ID].dmem_base_addr = (u32)UTIL_DMEM_BASE_ADDR; > + pe[UTIL_ID].mem_access_wdata = (void *)UTIL_MEM_ACCESS_WDATA; > + pe[UTIL_ID].mem_access_addr = (void *)UTIL_MEM_ACCESS_ADDR; > + pe[UTIL_ID].mem_access_rdata = (void *)UTIL_MEM_ACCESS_RDATA; > +#endif > +} [ ... ] > diff --git a/drivers/net/pfe_eth/pfe_driver.c > b/drivers/net/pfe_eth/pfe_driver.c > new file mode 100644 > index 0000000..5336ba7 > --- /dev/null > +++ b/drivers/net/pfe_eth/pfe_driver.c > @@ -0,0 +1,626 @@ > +/* > + * Copyright 2015-2016 Freescale Semiconductor, Inc. > + * Copyright 2017 NXP > + * > + * SPDX-License-Identifier: GPL-2.0+ > + */ > + > +#include <pfe_eth/pfe_eth.h> > +#include <pfe_eth/pfe_firmware.h> > + > +static struct tx_desc_s *g_tx_desc; > +static struct rx_desc_s *g_rx_desc; > + > +/* > + * HIF Rx interface function > + * Reads the rx descriptor from the current location (rx_to_read). > + * - If the descriptor has a valid data/pkt, then get the data pointer > + * - check for the input rx phy number > + * - increments the rx data pointer by pkt_head_room_size > + * - decrements the data length by pkt_head_room_size > + * - handover the packet to caller. > + * > + * @param[out] pkt_ptr Pointer to store rx packet pointer > + * @param[out] phy_port Pointer to store recv phy port > + * > + * @return -1 if no packet, else returns length of packet. > + */ > +int pfe_recv(unsigned int *pkt_ptr, int *phy_port) > +{ > + struct rx_desc_s *rx_desc = g_rx_desc; > + struct buf_desc *bd; > + int len = -1; > + > + struct hif_header_s *hif_header; > + > + bd = rx_desc->rx_base + rx_desc->rx_to_read; > + > + if (bd->ctrl & BD_CTRL_DESC_EN) > + return len; /* No pending Rx packet */ > + > + /* this len include hif_header(8bytes) */ > + len = bd->ctrl & 0xFFFF; > + > + hif_header = (struct hif_header_s *)DDR_PFE_TO_VIRT(bd->data); > + > + /* Get the recive port info from the packet */ Typo: receive > + debug( > + "Pkt recv'd: Pkt ptr(%p), len(%d), gemac_port(%d) > status(%08x)\n", > + hif_header, len, hif_header->port_no, bd->status); > + > +#ifdef DEBUG > + { > + int i; > + unsigned char *p = (unsigned char *)hif_header; > + > + for (i = 0; i < len; i++) { > + if (!(i % 16)) > + printf("\n"); > + printf(" %02x", p[i]); > + } > + printf("\n"); > + } > +#endif > + > + *pkt_ptr = (unsigned long)(hif_header + 1); > + *phy_port = hif_header->port_no; > + len -= sizeof(struct hif_header_s); > + > + rx_desc->rx_to_read = (rx_desc->rx_to_read + 1) > + & (rx_desc->rx_ring_size - 1); > + > + /* reset bd control field */ > + bd->ctrl = (MAX_FRAME_SIZE | BD_CTRL_LIFM | BD_CTRL_DESC_EN > + | BD_CTRL_DIR); > + bd->status = 0; > + > + /* Give START_STROBE to BDP to fetch the descriptor __NOW__, > + * BDP need not to wait for rx_poll_cycle time to fetch the > descriptor, > + * In idle state (ie., no rx pkt), BDP will not fetch > + * the descriptor even if strobe is given(I think) > + */ > + writel((readl(HIF_RX_CTRL) | HIF_CTRL_BDP_CH_START_WSTB), > HIF_RX_CTRL); > + > + return len; > +} [ ... ] > diff --git a/drivers/net/pfe_eth/pfe_eth.c b/drivers/net/pfe_eth/pfe_eth.c > new file mode 100644 > index 0000000..8d8de40 > --- /dev/null > +++ b/drivers/net/pfe_eth/pfe_eth.c > @@ -0,0 +1,545 @@ > +/* > + * Copyright 2015-2016 Freescale Semiconductor, Inc. > + * Copyright 2017 NXP > + * > + * SPDX-License-Identifier: GPL-2.0+ > + */ > + > +#include <common.h> > +#include <pfe_eth/pfe_eth.h> > + > +struct gemac_s gem_info[] = { > + /* PORT_0 configuration */ { > + /* GEMAC config */ > + .gemac_mode = GMII, > + .gemac_speed = SPEED_1000M, > + .gemac_duplex = DUPLEX_FULL, > + > + /* phy iface */ > + .phy_address = EMAC1_PHY_ADDR, > + .phy_mode = PHY_INTERFACE_MODE_SGMII, > + }, > + /* PORT_1 configuration */ { > + /* GEMAC config */ > + .gemac_mode = GMII, > + .gemac_speed = SPEED_1000M, > + .gemac_duplex = DUPLEX_FULL, > + > + /* phy iface */ > + .phy_address = EMAC2_PHY_ADDR, > + .phy_mode = PHY_INTERFACE_MODE_RGMII, > + }, > +}; > + > +#define MAX_GEMACS 2 > + > +static struct ls1012a_eth_dev *gemac_list[MAX_GEMACS]; > + > +#define MDIO_TIMEOUT 5000 > + > +static inline void ls1012a_gemac_enable(void *gemac_base) > +{ > + writel(readl(gemac_base + EMAC_ECNTRL_REG) | > + EMAC_ECNTRL_ETHER_EN, gemac_base + EMAC_ECNTRL_REG); > +} > + > +static inline void ls1012a_gemac_disable(void *gemac_base) > +{ > + writel(readl(gemac_base + EMAC_ECNTRL_REG) & > + ~EMAC_ECNTRL_ETHER_EN, gemac_base + EMAC_ECNTRL_REG); > +} > + > +static inline void ls1012a_gemac_set_speed(void *gemac_base, u32 speed) > +{ > + struct ccsr_scfg *scfg = (struct ccsr_scfg *)CONFIG_SYS_FSL_SCFG_ADDR; > + u32 ecr = readl(gemac_base + EMAC_ECNTRL_REG) & ~EMAC_ECNTRL_SPEED; > + u32 rcr = readl(gemac_base + EMAC_RCNTRL_REG) & ~EMAC_RCNTRL_RMII_10T; > + u32 rgmii_pcr = in_be32(&scfg->rgmiipcr) & > + ~(SCFG_RGMIIPCR_SETSP_1000M|SCFG_RGMIIPCR_SETSP_10M); > + > + if (speed == _1000BASET) { > + ecr |= EMAC_ECNTRL_SPEED; > + rgmii_pcr |= SCFG_RGMIIPCR_SETSP_1000M; > + } else if (speed != _100BASET) { > + rcr |= EMAC_RCNTRL_RMII_10T; > + rgmii_pcr |= SCFG_RGMIIPCR_SETSP_10M; > + } > + > + writel(ecr, gemac_base + EMAC_ECNTRL_REG); > + out_be32(&scfg->rgmiipcr, rgmii_pcr | SCFG_RGMIIPCR_SETFD); > + > + /* remove loop back */ > + rcr &= ~EMAC_RCNTRL_LOOP; > + /* enable flow control */ > + rcr |= EMAC_RCNTRL_FCE; > + > + /* Enable MII mode */ > + rcr |= EMAC_RCNTRL_MII_MODE; > + > + writel(rcr, gemac_base + EMAC_RCNTRL_REG); > + > + /* Enable Tx full duplex */ > + writel(readl(gemac_base + EMAC_TCNTRL_REG) | EMAC_TCNTRL_FDEN, > + gemac_base + EMAC_TCNTRL_REG); > +} > + > +static inline void ls1012a_gemac_set_ethaddr(void *gemac_base, uchar *mac) > +{ > + writel((mac[0] << 24) + (mac[1] << 16) + (mac[2] << 8) + mac[3], > + gemac_base + EMAC_PHY_ADDR_LOW); > + writel((mac[4] << 24) + (mac[5] << 16) + 0x8808, gemac_base + > + EMAC_PHY_ADDR_HIGH); > +} > + > +/** Stops or Disables GEMAC pointing to this eth iface. > + * > + * @param[in] edev Pointer to eth device structure. > + * > + * @return none > + */ > +static inline void ls1012a_eth_halt(struct eth_device *edev) > +{ > + struct ls1012a_eth_dev *priv = (struct ls1012a_eth_dev *)edev->priv; > + > + ls1012a_gemac_disable(priv->gem->gemac_base); > + > + gpi_disable(priv->gem->egpi_base); > +} > + > +static int ls1012a_eth_init(struct eth_device *dev, bd_t *bd) > +{ > + struct ls1012a_eth_dev *priv = (struct ls1012a_eth_dev *)dev->priv; > + struct gemac_s *gem = priv->gem; > + int speed; > + > + /* set ethernet mac address */ > + ls1012a_gemac_set_ethaddr(gem->gemac_base, dev->enetaddr); > + > + writel(0x00000004, gem->gemac_base + EMAC_TFWR_STR_FWD); > + writel(0x00000005, gem->gemac_base + EMAC_RX_SECTIOM_FULL); > + writel(0x00003fff, gem->gemac_base + EMAC_TRUNC_FL); > + writel(0x00000030, gem->gemac_base + EMAC_TX_SECTION_EMPTY); > + writel(0x00000000, gem->gemac_base + EMAC_MIB_CTRL_STS_REG); Why all the magic numbers? > + > +#ifdef CONFIG_PHYLIB > + /* Start up the PHY */ > + if (phy_startup(priv->phydev)) { > + printf("Could not initialize PHY %s\n", > + priv->phydev->dev->name); > + return -1; > + } > + speed = priv->phydev->speed; > + printf("Speed detected %x\n", speed); > + if (priv->phydev->duplex == DUPLEX_HALF) { > + printf("Half duplex not supported\n"); > + return -1; > + } > +#endif > + > + ls1012a_gemac_set_speed(gem->gemac_base, speed); > + > + /* Enable GPI */ > + gpi_enable(gem->egpi_base); > + > + /* Enable GEMAC */ > + ls1012a_gemac_enable(gem->gemac_base); > + > + return 0; > +} > + > +static int ls1012a_eth_send(struct eth_device *dev, void *data, int length) > +{ > + struct ls1012a_eth_dev *priv = (struct ls1012a_eth_dev *)dev->priv; > + > + int rc; > + int i = 0; > + > + rc = pfe_send(priv->gemac_port, data, length); > + > + if (rc < 0) { > + printf("Tx Q full\n"); > + return 0; > + } > + > + while (1) { > + rc = pfe_tx_done(); > + if (rc == 0) > + break; > + > + udelay(100); > + i++; > + if (i == 30000) > + printf("Tx timeout, send failed\n"); > + break; > + } > + > + return 0; > +} > + > +static int ls1012a_eth_recv(struct eth_device *dev) > +{ > + struct ls1012a_eth_dev *priv = (struct ls1012a_eth_dev *)dev->priv; > + u32 pkt_buf; > + int len; > + int phy_port; > + > + len = pfe_recv(&pkt_buf, &phy_port); > + > + if (len < 0) > + return 0; /* no packet in rx */ > + > + debug("Rx pkt: pkt_buf(%08x), phy_port(%d), len(%d)\n", pkt_buf, > + phy_port, len); > + if (phy_port != priv->gemac_port) { > + printf("Rx pkt not on expected port\n"); > + return 0; > + } > + > + /* Pass the packet up to the protocol layers. */ > + net_process_received_packet((void *)(long int)pkt_buf, len); Please don't call this directly. The layer above will call it for you if you return the size of the packet that is valid (assuming driver model, see below). > + return 0; > +} > + > +#if defined(CONFIG_PHYLIB) > + > +#define MDIO_TIMEOUT 5000 > +static int ls1012a_write_addr(struct mii_dev *bus, int phy_addr, int > dev_addr, > + int reg_addr) > +{ > + void *reg_base = bus->priv; > + u32 devadr; > + u32 phy; > + u32 reg_data; > + int timeout = MDIO_TIMEOUT; > + > + devadr = ((dev_addr & EMAC_MII_DATA_RA_MASK) << > EMAC_MII_DATA_RA_SHIFT); > + phy = ((phy_addr & EMAC_MII_DATA_PA_MASK) << EMAC_MII_DATA_PA_SHIFT); > + > + reg_data = (EMAC_MII_DATA_TA | phy | devadr | reg_addr); > + > + > + writel(reg_data, reg_base + EMAC_MII_DATA_REG); > + > + /* > + * wait for the MII interrupt > + */ > + while (!(readl(reg_base + EMAC_IEVENT_REG) & EMAC_IEVENT_MII)) { > + if (timeout-- <= 0) { > + printf("Phy MDIO read/write timeout\n"); > + return -1; > + } > + } > + > + /* > + * clear MII interrupt > + */ > + writel(EMAC_IEVENT_MII, reg_base + EMAC_IEVENT_REG); > + > + > + return 0; > +} > + > +static int ls1012a_phy_read(struct mii_dev *bus, int phy_addr, int dev_addr, > int > + reg_addr) > +{ > + void *reg_base = bus->priv; > + u32 reg; > + u32 phy; > + u32 reg_data; > + u16 val; > + int timeout = MDIO_TIMEOUT; > + > + if (dev_addr == MDIO_DEVAD_NONE) { > + reg = ((reg_addr & EMAC_MII_DATA_RA_MASK) << > + EMAC_MII_DATA_RA_SHIFT); > + } else { > + ls1012a_write_addr(bus, phy_addr, dev_addr, reg_addr); > + reg = ((dev_addr & EMAC_MII_DATA_RA_MASK) << > + EMAC_MII_DATA_RA_SHIFT); > + } > + > + phy = ((phy_addr & EMAC_MII_DATA_PA_MASK) << EMAC_MII_DATA_PA_SHIFT); > + > + if (dev_addr == MDIO_DEVAD_NONE) > + reg_data = (EMAC_MII_DATA_ST | EMAC_MII_DATA_OP_RD | > + EMAC_MII_DATA_TA | phy | reg); > + else > + reg_data = (EMAC_MII_DATA_OP_CL45_RD | EMAC_MII_DATA_TA | > + phy | reg); > + > + writel(reg_data, reg_base + EMAC_MII_DATA_REG); > + > + /* > + * wait for the MII interrupt > + */ > + while (!(readl(reg_base + EMAC_IEVENT_REG) & EMAC_IEVENT_MII)) { > + if (timeout-- <= 0) { > + printf("Phy MDIO read/write timeout\n"); > + return -1; > + } > + } > + > + /* > + * clear MII interrupt > + */ > + writel(EMAC_IEVENT_MII, reg_base + EMAC_IEVENT_REG); > + > + /* > + * it's now safe to read the PHY's register > + */ > + val = (u16)readl(reg_base + EMAC_MII_DATA_REG); > + debug("%s: %p phy: 0x%x reg:0x%08x val:%#x\n", __func__, reg_base, > + phy_addr, reg_addr, val); > + > + return val; > +} > + > +static int ls1012a_phy_write(struct mii_dev *bus, int phy_addr, int dev_addr, > + int reg_addr, u16 data) > +{ > + void *reg_base = bus->priv; > + u32 reg; > + u32 phy; > + u32 reg_data; > + int timeout = MDIO_TIMEOUT; > + int val; > + > + if (dev_addr == MDIO_DEVAD_NONE) { > + reg = ((reg_addr & EMAC_MII_DATA_RA_MASK) << > + EMAC_MII_DATA_RA_SHIFT); > + } else { > + ls1012a_write_addr(bus, phy_addr, dev_addr, reg_addr); > + reg = ((dev_addr & EMAC_MII_DATA_RA_MASK) << > + EMAC_MII_DATA_RA_SHIFT); > + } > + > + phy = ((phy_addr & EMAC_MII_DATA_PA_MASK) << EMAC_MII_DATA_PA_SHIFT); > + > + if (dev_addr == MDIO_DEVAD_NONE) > + reg_data = (EMAC_MII_DATA_ST | EMAC_MII_DATA_OP_WR | > + EMAC_MII_DATA_TA | phy | reg | data); > + else > + reg_data = (EMAC_MII_DATA_OP_CL45_WR | EMAC_MII_DATA_TA | > + phy | reg | data); > + > + writel(reg_data, reg_base + EMAC_MII_DATA_REG); > + > + /* > + * wait for the MII interrupt > + */ > + while (!(readl(reg_base + EMAC_IEVENT_REG) & EMAC_IEVENT_MII)) { > + if (timeout-- <= 0) { > + printf("Phy MDIO read/write timeout\n"); > + return -1; > + } > + } > + > + /* > + * clear MII interrupt > + */ > + writel(EMAC_IEVENT_MII, reg_base + EMAC_IEVENT_REG); > + > + debug("%s: phy: %02x reg:%02x val:%#x\n", __func__, phy_addr, > + reg_addr, data); > + > + return val; > +} > + > +struct mii_dev *ls1012a_mdio_init(struct mdio_info *mdio_info) > +{ > + struct mii_dev *bus; > + int ret; > + u32 mdio_speed; > + u32 pclk = 250000000; > + > + bus = mdio_alloc(); > + if (!bus) { > + printf("mdio_alloc failed\n"); > + return NULL; > + } > + bus->read = ls1012a_phy_read; > + bus->write = ls1012a_phy_write; > + /* MAC1 MDIO used to communicate with external PHYS */ > + bus->priv = mdio_info->reg_base; > + sprintf(bus->name, mdio_info->name); > + > + /* configure mdio speed */ > + mdio_speed = (DIV_ROUND_UP(pclk, 4000000) << EMAC_MII_SPEED_SHIFT); > + mdio_speed |= EMAC_HOLDTIME(0x5); > + writel(mdio_speed, mdio_info->reg_base + EMAC_MII_CTRL_REG); > + > + ret = mdio_register(bus); > + if (ret) { > + printf("mdio_register failed\n"); > + free(bus); > + return NULL; > + } > + return bus; > +} > + > +static void ls1012a_configure_serdes(struct ls1012a_eth_dev *priv) > +{ > + struct mii_dev bus; > + int value, sgmii_2500 = 0; > + struct gemac_s *gem = priv->gem; > + > + if (gem->phy_mode == PHY_INTERFACE_MODE_SGMII_2500) > + sgmii_2500 = 1; > + > + printf("%s %d\n", __func__, priv->gemac_port); > + > + /* PCS configuration done with corresponding GEMAC */ > + bus.priv = gem_info[priv->gemac_port].gemac_base; > + > + ls1012a_phy_read(&bus, 0, MDIO_DEVAD_NONE, 0x0); > + ls1012a_phy_read(&bus, 0, MDIO_DEVAD_NONE, 0x1); > + ls1012a_phy_read(&bus, 0, MDIO_DEVAD_NONE, 0x2); > + ls1012a_phy_read(&bus, 0, MDIO_DEVAD_NONE, 0x3); > + > + /* Reset serdes */ > + ls1012a_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0x0, 0x8000); > + > + /* SGMII IF mode + AN enable only for 1G SGMII, not for 2.5G */ > + value = PHY_SGMII_IF_MODE_SGMII; > + if (!sgmii_2500) > + value |= PHY_SGMII_IF_MODE_AN; > + else > + value |= PHY_SGMII_IF_MODE_SGMII_GBT; > + > + ls1012a_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0x14, value); > + > + /* Dev ability according to SGMII specification */ > + value = PHY_SGMII_DEV_ABILITY_SGMII; > + ls1012a_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0x4, value); > + > + /* These values taken from validation team */ > + if (!sgmii_2500) { > + ls1012a_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0x13, 0x0); > + ls1012a_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0x12, 0x400); > + } else { > + ls1012a_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0x13, 0x7); > + ls1012a_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0x12, 0xa120); > + } > + > + /* Restart AN */ > + value = PHY_SGMII_CR_DEF_VAL; > + if (!sgmii_2500) > + value |= PHY_SGMII_CR_RESET_AN; > + ls1012a_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0, value); > +} > + > +void ls1012a_set_mdio(int dev_id, struct mii_dev *bus) > +{ > + gem_info[dev_id].bus = bus; > +} > + > +void ls1012a_set_phy_address_mode(int dev_id, int phy_id, int phy_mode) > +{ > + gem_info[dev_id].phy_address = phy_id; > + gem_info[dev_id].phy_mode = phy_mode; > +} > + > +int ls1012a_phy_configure(struct ls1012a_eth_dev *priv, int dev_id, int > phy_id) > +{ > + struct phy_device *phydev = NULL; > + struct eth_device *dev = priv->dev; > + struct gemac_s *gem = priv->gem; > + struct ccsr_scfg *scfg = (struct ccsr_scfg *)CONFIG_SYS_FSL_SCFG_ADDR; > + > + /* Configure SGMII PCS */ > + if (gem->phy_mode == PHY_INTERFACE_MODE_SGMII || > + gem->phy_mode == PHY_INTERFACE_MODE_SGMII_2500) { > + out_be32(&scfg->mdioselcr, 0x00000000); > + ls1012a_configure_serdes(priv); > + } > + > + /* By this time on-chip SGMII initialization is done > + * we can switch mdio interface to external PHYs > + */ > + out_be32(&scfg->mdioselcr, 0x80000000); > + > + if (!gem->bus) > + return -1; > + phydev = phy_connect(gem->bus, phy_id, dev, gem->phy_mode); > + if (!phydev) { > + printf("phy_connect failed\n"); > + return -1; > + } > + > + phy_config(phydev); > + > + priv->phydev = phydev; > + > + return 0; > +} > +#endif > + > +int gemac_initialize(bd_t *bis, int dev_id, char *devname) > +{ > + struct eth_device *dev; > + struct ls1012a_eth_dev *priv; > + struct pfe *pfe; > + int i; > + > + if (dev_id > 1) { > + printf("Invalid port\n"); > + return -1; > + } > + > + dev = (struct eth_device *)malloc(sizeof(struct eth_device)); Please don't add a new driver that uses the legacy API. Make this a driver model driver. > + if (!dev) > + return -1; > + > + memset(dev, 0, sizeof(struct eth_device)); > + > + priv = (struct ls1012a_eth_dev *)malloc(sizeof(struct > ls1012a_eth_dev)); > + if (!priv) > + return -1; > + > + gemac_list[dev_id] = priv; > + priv->gemac_port = dev_id; > + priv->gem = &gem_info[priv->gemac_port]; > + priv->dev = dev; > + > + pfe = &priv->pfe; > + > + pfe->cbus_baseaddr = (void *)CONFIG_SYS_FSL_PFE_ADDR; > + pfe->ddr_baseaddr = (void *)CONFIG_DDR_PFE_BASEADDR; > + pfe->ddr_phys_baseaddr = (unsigned long)CONFIG_DDR_PFE_PHYS_BASEADDR; > + > + sprintf(dev->name, devname); > + dev->priv = priv; > + dev->init = ls1012a_eth_init; > + dev->halt = ls1012a_eth_halt; > + dev->send = ls1012a_eth_send; > + dev->recv = ls1012a_eth_recv; > + > + /* Tell u-boot to get the addr from the env */ > + for (i = 0; i < 6; i++) > + dev->enetaddr[i] = 0; > + > + pfe_probe(pfe); > + > + switch (priv->gemac_port) { > + case EMAC_PORT_0: > + default: > + priv->gem->gemac_base = EMAC1_BASE_ADDR; > + priv->gem->egpi_base = EGPI1_BASE_ADDR; > + break; > + case EMAC_PORT_1: > + priv->gem->gemac_base = EMAC2_BASE_ADDR; > + priv->gem->egpi_base = EGPI2_BASE_ADDR; > + break; > + } > + > +#if defined(CONFIG_PHYLIB) > + if (ls1012a_phy_configure(priv, dev_id, > + gem_info[priv->gemac_port].phy_address)) > + return -1; > +#endif > + > + eth_register(dev); > + > + return 0; > +} [ ... ] _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot