[U-Boot] [PATCH 1/9] drivers: net: pfe_eth: LS1012A PFE driver introduction

Joe Hershberger joe.hershberger at ni.com
Tue Dec 5 20:13:39 UTC 2017


On Mon, Oct 9, 2017 at 4:11 AM, Calvin Johnson <calvin.johnson at 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.johnson at nxp.com>
> Signed-off-by: Anjaneyulu Jagarlmudi <anji.jagarlmudi at 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;
> +}

[ ... ]


More information about the U-Boot mailing list