diff options
Diffstat (limited to 'roms/u-boot/drivers/net/pfe_eth')
-rw-r--r-- | roms/u-boot/drivers/net/pfe_eth/Kconfig | 12 | ||||
-rw-r--r-- | roms/u-boot/drivers/net/pfe_eth/Makefile | 13 | ||||
-rw-r--r-- | roms/u-boot/drivers/net/pfe_eth/pfe_cmd.c | 500 | ||||
-rw-r--r-- | roms/u-boot/drivers/net/pfe_eth/pfe_driver.c | 645 | ||||
-rw-r--r-- | roms/u-boot/drivers/net/pfe_eth/pfe_eth.c | 295 | ||||
-rw-r--r-- | roms/u-boot/drivers/net/pfe_eth/pfe_firmware.c | 399 | ||||
-rw-r--r-- | roms/u-boot/drivers/net/pfe_eth/pfe_hw.c | 1000 | ||||
-rw-r--r-- | roms/u-boot/drivers/net/pfe_eth/pfe_mdio.c | 291 |
8 files changed, 3155 insertions, 0 deletions
diff --git a/roms/u-boot/drivers/net/pfe_eth/Kconfig b/roms/u-boot/drivers/net/pfe_eth/Kconfig new file mode 100644 index 000000000..a13b331a5 --- /dev/null +++ b/roms/u-boot/drivers/net/pfe_eth/Kconfig @@ -0,0 +1,12 @@ +menuconfig FSL_PFE + bool "NXP PFE Ethernet driver" + help + This driver provides support for NXP's Packet Forwarding Engine. + +if FSL_PFE + +config SYS_FSL_PFE_ADDR + hex "PFE base address" + default 0x04000000 + +endif diff --git a/roms/u-boot/drivers/net/pfe_eth/Makefile b/roms/u-boot/drivers/net/pfe_eth/Makefile new file mode 100644 index 000000000..ab5a7b2c7 --- /dev/null +++ b/roms/u-boot/drivers/net/pfe_eth/Makefile @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright 2015-2016 Freescale Semiconductor, Inc. +# Copyright 2017 NXP +# + +# Layerscape PFE driver +obj-y += pfe_cmd.o \ + pfe_driver.o \ + pfe_eth.o \ + pfe_firmware.o \ + pfe_hw.o \ + pfe_mdio.o diff --git a/roms/u-boot/drivers/net/pfe_eth/pfe_cmd.c b/roms/u-boot/drivers/net/pfe_eth/pfe_cmd.c new file mode 100644 index 000000000..364750f65 --- /dev/null +++ b/roms/u-boot/drivers/net/pfe_eth/pfe_cmd.c @@ -0,0 +1,500 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2015-2016 Freescale Semiconductor, Inc. + * Copyright 2017 NXP + */ + +/* + * @file + * @brief PFE utility commands + */ + +#include <common.h> +#include <command.h> +#include <log.h> +#include <linux/delay.h> +#include <net/pfe_eth/pfe_eth.h> + +static inline void pfe_command_help(void) +{ + printf("Usage: pfe [pe | status | expt ] <options>\n"); +} + +static void pfe_command_pe(int argc, char *const argv[]) +{ + if (argc >= 3 && strcmp(argv[2], "pmem") == 0) { + if (argc >= 4 && strcmp(argv[3], "read") == 0) { + int i; + int num; + int id; + u32 addr; + u32 size; + u32 val; + + if (argc == 7) { + num = simple_strtoul(argv[6], NULL, 0); + } else if (argc == 6) { + num = 1; + } else { + printf("Usage: pfe pe pmem read <id> <addr> [<num>]\n"); + return; + } + + id = simple_strtoul(argv[4], NULL, 0); + addr = simple_strtoul(argv[5], NULL, 16); + size = 4; + + for (i = 0; i < num; i++, addr += 4) { + val = pe_pmem_read(id, addr, size); + val = be32_to_cpu(val); + if (!(i & 3)) + printf("%08x: ", addr); + printf("%08x%s", val, i == num - 1 || (i & 3) + == 3 ? "\n" : " "); + } + + } else { + printf("Usage: pfe pe pmem read <parameters>\n"); + } + } else if (argc >= 3 && strcmp(argv[2], "dmem") == 0) { + if (argc >= 4 && strcmp(argv[3], "read") == 0) { + int i; + int num; + int id; + u32 addr; + u32 size; + u32 val; + + if (argc == 7) { + num = simple_strtoul(argv[6], NULL, 0); + } else if (argc == 6) { + num = 1; + } else { + printf("Usage: pfe pe dmem read <id> <addr> [<num>]\n"); + return; + } + + id = simple_strtoul(argv[4], NULL, 0); + addr = simple_strtoul(argv[5], NULL, 16); + size = 4; + + for (i = 0; i < num; i++, addr += 4) { + val = pe_dmem_read(id, addr, size); + val = be32_to_cpu(val); + if (!(i & 3)) + printf("%08x: ", addr); + printf("%08x%s", val, i == num - 1 || (i & 3) + == 3 ? "\n" : " "); + } + + } else if (argc >= 4 && strcmp(argv[3], "write") == 0) { + int id; + u32 val; + u32 addr; + u32 size; + + if (argc != 7) { + printf("Usage: pfe pe dmem write <id> <val> <addr>\n"); + return; + } + + id = simple_strtoul(argv[4], NULL, 0); + val = simple_strtoul(argv[5], NULL, 16); + val = cpu_to_be32(val); + addr = simple_strtoul(argv[6], NULL, 16); + size = 4; + pe_dmem_write(id, val, addr, size); + } else { + printf("Usage: pfe pe dmem [read | write] <parameters>\n"); + } + } else if (argc >= 3 && strcmp(argv[2], "lmem") == 0) { + if (argc >= 4 && strcmp(argv[3], "read") == 0) { + int i; + int num; + u32 val; + u32 offset; + + if (argc == 6) { + num = simple_strtoul(argv[5], NULL, 0); + } else if (argc == 5) { + num = 1; + } else { + printf("Usage: pfe pe lmem read <offset> [<num>]\n"); + return; + } + + offset = simple_strtoul(argv[4], NULL, 16); + + for (i = 0; i < num; i++, offset += 4) { + pe_lmem_read(&val, 4, offset); + val = be32_to_cpu(val); + printf("%08x%s", val, i == num - 1 || (i & 7) + == 7 ? "\n" : " "); + } + + } else if (argc >= 4 && strcmp(argv[3], "write") == 0) { + u32 val; + u32 offset; + + if (argc != 6) { + printf("Usage: pfe pe lmem write <val> <offset>\n"); + return; + } + + val = simple_strtoul(argv[4], NULL, 16); + val = cpu_to_be32(val); + offset = simple_strtoul(argv[5], NULL, 16); + pe_lmem_write(&val, 4, offset); + } else { + printf("Usage: pfe pe lmem [read | write] <parameters>\n"); + } + } else { + if (strcmp(argv[2], "help") != 0) + printf("Unknown option: %s\n", argv[2]); + + printf("Usage: pfe pe <parameters>\n"); + } +} + +#define NUM_QUEUES 16 + +/* + * qm_read_drop_stat + * This function is used to read the drop statistics from the TMU + * hw drop counter. Since the hw counter is always cleared afer + * reading, this function maintains the previous drop count, and + * adds the new value to it. That value can be retrieved by + * passing a pointer to it with the total_drops arg. + * + * @param tmu TMU number (0 - 3) + * @param queue queue number (0 - 15) + * @param total_drops pointer to location to store total drops (or NULL) + * @param do_reset if TRUE, clear total drops after updating + * + */ +u32 qm_read_drop_stat(u32 tmu, u32 queue, u32 *total_drops, int do_reset) +{ + static u32 qtotal[TMU_MAX_ID + 1][NUM_QUEUES]; + u32 val; + + writel((tmu << 8) | queue, TMU_TEQ_CTRL); + writel((tmu << 8) | queue, TMU_LLM_CTRL); + val = readl(TMU_TEQ_DROP_STAT); + qtotal[tmu][queue] += val; + if (total_drops) + *total_drops = qtotal[tmu][queue]; + if (do_reset) + qtotal[tmu][queue] = 0; + return val; +} + +static ssize_t tmu_queue_stats(char *buf, int tmu, int queue) +{ + ssize_t len = 0; + u32 drops; + + printf("%d-%02d, ", tmu, queue); + + drops = qm_read_drop_stat(tmu, queue, NULL, 0); + + /* Select queue */ + writel((tmu << 8) | queue, TMU_TEQ_CTRL); + writel((tmu << 8) | queue, TMU_LLM_CTRL); + + printf("(teq) drop: %10u, tx: %10u (llm) head: %08x, tail: %08x, drop: %10u\n", + drops, readl(TMU_TEQ_TRANS_STAT), + readl(TMU_LLM_QUE_HEADPTR), readl(TMU_LLM_QUE_TAILPTR), + readl(TMU_LLM_QUE_DROPCNT)); + + return len; +} + +static ssize_t tmu_queues(char *buf, int tmu) +{ + ssize_t len = 0; + int queue; + + for (queue = 0; queue < 16; queue++) + len += tmu_queue_stats(buf + len, tmu, queue); + + return len; +} + +static inline void hif_status(void) +{ + printf("hif:\n"); + + printf(" tx curr bd: %x\n", readl(HIF_TX_CURR_BD_ADDR)); + printf(" tx status: %x\n", readl(HIF_TX_STATUS)); + printf(" tx dma status: %x\n", readl(HIF_TX_DMA_STATUS)); + + printf(" rx curr bd: %x\n", readl(HIF_RX_CURR_BD_ADDR)); + printf(" rx status: %x\n", readl(HIF_RX_STATUS)); + printf(" rx dma status: %x\n", readl(HIF_RX_DMA_STATUS)); + + printf("hif nocopy:\n"); + + printf(" tx curr bd: %x\n", readl(HIF_NOCPY_TX_CURR_BD_ADDR)); + printf(" tx status: %x\n", readl(HIF_NOCPY_TX_STATUS)); + printf(" tx dma status: %x\n", readl(HIF_NOCPY_TX_DMA_STATUS)); + + printf(" rx curr bd: %x\n", readl(HIF_NOCPY_RX_CURR_BD_ADDR)); + printf(" rx status: %x\n", readl(HIF_NOCPY_RX_STATUS)); + printf(" rx dma status: %x\n", readl(HIF_NOCPY_RX_DMA_STATUS)); +} + +static void gpi(int id, void *base) +{ + u32 val; + + printf("%s%d:\n", __func__, id); + + printf(" tx under stick: %x\n", readl(base + GPI_FIFO_STATUS)); + val = readl(base + GPI_FIFO_DEBUG); + printf(" tx pkts: %x\n", (val >> 23) & 0x3f); + printf(" rx pkts: %x\n", (val >> 18) & 0x3f); + printf(" tx bytes: %x\n", (val >> 9) & 0x1ff); + printf(" rx bytes: %x\n", (val >> 0) & 0x1ff); + printf(" overrun: %x\n", readl(base + GPI_OVERRUN_DROPCNT)); +} + +static void bmu(int id, void *base) +{ + printf("%s%d:\n", __func__, id); + + printf(" buf size: %x\n", (1 << readl(base + BMU_BUF_SIZE))); + printf(" buf count: %x\n", readl(base + BMU_BUF_CNT)); + printf(" buf rem: %x\n", readl(base + BMU_REM_BUF_CNT)); + printf(" buf curr: %x\n", readl(base + BMU_CURR_BUF_CNT)); + printf(" free err: %x\n", readl(base + BMU_FREE_ERR_ADDR)); +} + +#define PESTATUS_ADDR_CLASS 0x800 +#define PEMBOX_ADDR_CLASS 0x890 +#define PESTATUS_ADDR_TMU 0x80 +#define PEMBOX_ADDR_TMU 0x290 +#define PESTATUS_ADDR_UTIL 0x0 + +static void pfe_pe_status(int argc, char *const argv[]) +{ + int do_clear = 0; + u32 id; + u32 dmem_addr; + u32 cpu_state; + u32 activity_counter; + u32 rx; + u32 tx; + u32 drop; + char statebuf[5]; + u32 class_debug_reg = 0; + + if (argc == 4 && strcmp(argv[3], "clear") == 0) + do_clear = 1; + + for (id = CLASS0_ID; id < MAX_PE; id++) { + if (id >= TMU0_ID) { + if (id == TMU2_ID) + continue; + if (id == TMU0_ID) + printf("tmu:\n"); + dmem_addr = PESTATUS_ADDR_TMU; + } else { + if (id == CLASS0_ID) + printf("class:\n"); + dmem_addr = PESTATUS_ADDR_CLASS; + class_debug_reg = readl(CLASS_PE0_DEBUG + id * 4); + } + + cpu_state = pe_dmem_read(id, dmem_addr, 4); + dmem_addr += 4; + memcpy(statebuf, (char *)&cpu_state, 4); + statebuf[4] = '\0'; + activity_counter = pe_dmem_read(id, dmem_addr, 4); + dmem_addr += 4; + rx = pe_dmem_read(id, dmem_addr, 4); + if (do_clear) + pe_dmem_write(id, 0, dmem_addr, 4); + dmem_addr += 4; + tx = pe_dmem_read(id, dmem_addr, 4); + if (do_clear) + pe_dmem_write(id, 0, dmem_addr, 4); + dmem_addr += 4; + drop = pe_dmem_read(id, dmem_addr, 4); + if (do_clear) + pe_dmem_write(id, 0, dmem_addr, 4); + dmem_addr += 4; + + if (id >= TMU0_ID) { + printf("%d: state=%4s ctr=%08x rx=%x qstatus=%x\n", + id - TMU0_ID, statebuf, + cpu_to_be32(activity_counter), + cpu_to_be32(rx), cpu_to_be32(tx)); + } else { + printf("%d: pc=1%04x ldst=%04x state=%4s ctr=%08x rx=%x tx=%x drop=%x\n", + id - CLASS0_ID, class_debug_reg & 0xFFFF, + class_debug_reg >> 16, + statebuf, cpu_to_be32(activity_counter), + cpu_to_be32(rx), cpu_to_be32(tx), + cpu_to_be32(drop)); + } + } +} + +static void pfe_command_status(int argc, char *const argv[]) +{ + if (argc >= 3 && strcmp(argv[2], "pe") == 0) { + pfe_pe_status(argc, argv); + } else if (argc == 3 && strcmp(argv[2], "bmu") == 0) { + bmu(1, BMU1_BASE_ADDR); + bmu(2, BMU2_BASE_ADDR); + } else if (argc == 3 && strcmp(argv[2], "hif") == 0) { + hif_status(); + } else if (argc == 3 && strcmp(argv[2], "gpi") == 0) { + gpi(0, EGPI1_BASE_ADDR); + gpi(1, EGPI2_BASE_ADDR); + gpi(3, HGPI_BASE_ADDR); + } else if (argc == 3 && strcmp(argv[2], "tmu0_queues") == 0) { + tmu_queues(NULL, 0); + } else if (argc == 3 && strcmp(argv[2], "tmu1_queues") == 0) { + tmu_queues(NULL, 1); + } else if (argc == 3 && strcmp(argv[2], "tmu3_queues") == 0) { + tmu_queues(NULL, 3); + } else { + printf("Usage: pfe status [pe <clear> | bmu | gpi | hif | tmuX_queues ]\n"); + } +} + +#define EXPT_DUMP_ADDR 0x1fa8 +#define EXPT_REG_COUNT 20 +static const char *register_names[EXPT_REG_COUNT] = { + " pc", "ECAS", " EID", " ED", + " sp", " r1", " r2", " r3", + " r4", " r5", " r6", " r7", + " r8", " r9", " r10", " r11", + " r12", " r13", " r14", " r15" +}; + +static void pfe_command_expt(int argc, char *const argv[]) +{ + unsigned int id, i, val, addr; + + if (argc == 3) { + id = simple_strtoul(argv[2], NULL, 0); + addr = EXPT_DUMP_ADDR; + printf("Exception information for PE %d:\n", id); + for (i = 0; i < EXPT_REG_COUNT; i++) { + val = pe_dmem_read(id, addr, 4); + val = be32_to_cpu(val); + printf("%s:%08x%s", register_names[i], val, + (i & 3) == 3 ? "\n" : " "); + addr += 4; + } + } else { + printf("Usage: pfe expt <id>\n"); + } +} + +#ifdef PFE_RESET_WA +/*This function sends a dummy packet to HIF through TMU3 */ +static void send_dummy_pkt_to_hif(void) +{ + u32 buf; + static u32 dummy_pkt[] = { + 0x4200800a, 0x01000003, 0x00018100, 0x00000000, + 0x33221100, 0x2b785544, 0xd73093cb, 0x01000608, + 0x04060008, 0x2b780200, 0xd73093cb, 0x0a01a8c0, + 0x33221100, 0xa8c05544, 0x00000301, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0xbe86c51f }; + + /*Allocate BMU2 buffer */ + buf = readl(BMU2_BASE_ADDR + BMU_ALLOC_CTRL); + + debug("Sending a dummy pkt to HIF %x\n", buf); + buf += 0x80; + memcpy((void *)DDR_PFE_TO_VIRT(buf), dummy_pkt, sizeof(dummy_pkt)); + + /*Write length and pkt to TMU*/ + writel(0x03000042, TMU_PHY_INQ_PKTPTR); + writel(buf, TMU_PHY_INQ_PKTINFO); +} + +void pfe_command_stop(int argc, char *const argv[]) +{ + int pfe_pe_id, hif_stop_loop = 10; + u32 rx_status; + + printf("Stopping PFE...\n"); + + /*Mark all descriptors as LAST_BD */ + hif_rx_desc_disable(); + + /*If HIF Rx BDP is busy send a dummy packet */ + do { + rx_status = readl(HIF_RX_STATUS); + if (rx_status & BDP_CSR_RX_DMA_ACTV) + send_dummy_pkt_to_hif(); + udelay(10); + } while (hif_stop_loop--); + + if (readl(HIF_RX_STATUS) & BDP_CSR_RX_DMA_ACTV) + printf("Unable to stop HIF\n"); + + /*Disable Class PEs */ + for (pfe_pe_id = CLASS0_ID; pfe_pe_id <= CLASS_MAX_ID; pfe_pe_id++) { + /*Inform PE to stop */ + pe_dmem_write(pfe_pe_id, cpu_to_be32(1), PEMBOX_ADDR_CLASS, 4); + udelay(10); + + /*Read status */ + if (!pe_dmem_read(pfe_pe_id, PEMBOX_ADDR_CLASS + 4, 4)) + printf("Failed to stop PE%d\n", pfe_pe_id); + } + + /*Disable TMU PEs */ + for (pfe_pe_id = TMU0_ID; pfe_pe_id <= TMU_MAX_ID; pfe_pe_id++) { + if (pfe_pe_id == TMU2_ID) + continue; + + /*Inform PE to stop */ + pe_dmem_write(pfe_pe_id, 1, PEMBOX_ADDR_TMU, 4); + udelay(10); + + /*Read status */ + if (!pe_dmem_read(pfe_pe_id, PEMBOX_ADDR_TMU + 4, 4)) + printf("Failed to stop PE%d\n", pfe_pe_id); + } +} +#endif + +static int pfe_command(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + if (argc == 1 || strcmp(argv[1], "help") == 0) { + pfe_command_help(); + return CMD_RET_SUCCESS; + } + + if (strcmp(argv[1], "pe") == 0) { + pfe_command_pe(argc, argv); + } else if (strcmp(argv[1], "status") == 0) { + pfe_command_status(argc, argv); + } else if (strcmp(argv[1], "expt") == 0) { + pfe_command_expt(argc, argv); +#ifdef PFE_RESET_WA + } else if (strcmp(argv[1], "stop") == 0) { + pfe_command_stop(argc, argv); +#endif + } else { + printf("Unknown option: %s\n", argv[1]); + pfe_command_help(); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +U_BOOT_CMD( + pfe, 7, 1, pfe_command, + "Performs PFE lib utility functions", + "Usage:\n" + "pfe <options>" +); diff --git a/roms/u-boot/drivers/net/pfe_eth/pfe_driver.c b/roms/u-boot/drivers/net/pfe_eth/pfe_driver.c new file mode 100644 index 000000000..6f443b4ea --- /dev/null +++ b/roms/u-boot/drivers/net/pfe_eth/pfe_driver.c @@ -0,0 +1,645 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2015-2016 Freescale Semiconductor, Inc. + * Copyright 2017 NXP + */ + +#include <log.h> +#include <malloc.h> +#include <linux/delay.h> +#include <net/pfe_eth/pfe_eth.h> +#include <net/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 + * - increment the rx data pointer by pkt_head_room_size + * - decrement the data length by pkt_head_room_size + * - handover the packet to caller. + * + * @param[out] pkt_ptr - Pointer to store rx packet + * @param[out] phy_port - Pointer to store recv phy port + * + * @return -1 if no packet, else return length of packet. + */ +int pfe_recv(uchar **pkt_ptr, int *phy_port) +{ + struct rx_desc_s *rx_desc = g_rx_desc; + struct buf_desc *bd; + int len = 0; + + struct hif_header_s *hif_header; + + bd = rx_desc->rx_base + rx_desc->rx_to_read; + + if (readl(&bd->ctrl) & BD_CTRL_DESC_EN) + return len; /* No pending Rx packet */ + + /* this len include hif_header(8 bytes) */ + len = readl(&bd->ctrl) & 0xFFFF; + + hif_header = (struct hif_header_s *)DDR_PFE_TO_VIRT(readl(&bd->data)); + + /* Get the receive port info from the packet */ + debug("Pkt received:"); + debug(" Pkt ptr(%p), len(%d), gemac_port(%d) status(%08x)\n", + hif_header, len, hif_header->port_no, readl(&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 = (uchar *)(hif_header + 1); + *phy_port = hif_header->port_no; + len -= sizeof(struct hif_header_s); + + return len; +} + +/* + * HIF function to check the Rx done + * This function will check the rx done indication of the current rx_to_read + * locations + * if success, moves the rx_to_read to next location. + */ +int pfe_eth_free_pkt(struct udevice *dev, uchar *packet, int length) +{ + struct rx_desc_s *rx_desc = g_rx_desc; + struct buf_desc *bd; + + debug("%s:rx_base: %p, rx_to_read: %d\n", __func__, rx_desc->rx_base, + rx_desc->rx_to_read); + + bd = rx_desc->rx_base + rx_desc->rx_to_read; + + /* reset the control field */ + writel((MAX_FRAME_SIZE | BD_CTRL_LIFM | BD_CTRL_DESC_EN + | BD_CTRL_DIR), &bd->ctrl); + writel(0, &bd->status); + + debug("Rx Done : status: %08x, ctrl: %08x\n", readl(&bd->status), + readl(&bd->ctrl)); + + /* Give START_STROBE to BDP to fetch the descriptor __NOW__, + * BDP need not 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. + */ + writel((readl(HIF_RX_CTRL) | HIF_CTRL_BDP_CH_START_WSTB), HIF_RX_CTRL); + + /* increment the rx_to_read index to next location */ + rx_desc->rx_to_read = (rx_desc->rx_to_read + 1) + & (rx_desc->rx_ring_size - 1); + + debug("Rx next pkt location: %d\n", rx_desc->rx_to_read); + + return 0; +} + +/* + * HIF Tx interface function + * This function sends a single packet to PFE from HIF interface. + * - No interrupt indication on tx completion. + * - Data is copied to tx buffers before tx descriptor is updated + * and TX DMA is enabled. + * + * @param[in] phy_port Phy port number to send out this packet + * @param[in] data Pointer to the data + * @param[in] length Length of the ethernet packet to be transferred. + * + * @return -1 if tx Q is full, else returns the tx location where the pkt is + * placed. + */ +int pfe_send(int phy_port, void *data, int length) +{ + struct tx_desc_s *tx_desc = g_tx_desc; + struct buf_desc *bd; + struct hif_header_s hif_header; + u8 *tx_buf_va; + + debug("%s:pkt: %p, len: %d, tx_base: %p, tx_to_send: %d\n", __func__, + data, length, tx_desc->tx_base, tx_desc->tx_to_send); + + bd = tx_desc->tx_base + tx_desc->tx_to_send; + + /* check queue-full condition */ + if (readl(&bd->ctrl) & BD_CTRL_DESC_EN) + return -1; + + /* PFE checks for min pkt size */ + if (length < MIN_PKT_SIZE) + length = MIN_PKT_SIZE; + + tx_buf_va = (void *)DDR_PFE_TO_VIRT(readl(&bd->data)); + debug("%s: tx_buf_va: %p, tx_buf_pa: %08x\n", __func__, tx_buf_va, + readl(&bd->data)); + + /* Fill the gemac/phy port number to send this packet out */ + memset(&hif_header, 0, sizeof(struct hif_header_s)); + hif_header.port_no = phy_port; + + memcpy(tx_buf_va, (u8 *)&hif_header, sizeof(struct hif_header_s)); + memcpy(tx_buf_va + sizeof(struct hif_header_s), data, length); + length += sizeof(struct hif_header_s); + +#ifdef DEBUG + { + int i; + unsigned char *p = (unsigned char *)tx_buf_va; + + for (i = 0; i < length; i++) { + if (!(i % 16)) + printf("\n"); + printf("%02x ", p[i]); + } + } +#endif + + debug("Tx Done: status: %08x, ctrl: %08x\n", readl(&bd->status), + readl(&bd->ctrl)); + + /* fill the tx desc */ + writel((u32)(BD_CTRL_DESC_EN | BD_CTRL_LIFM | (length & 0xFFFF)), + &bd->ctrl); + writel(0, &bd->status); + + writel((HIF_CTRL_DMA_EN | HIF_CTRL_BDP_CH_START_WSTB), HIF_TX_CTRL); + + udelay(100); + + return tx_desc->tx_to_send; +} + +/* + * HIF function to check the Tx done + * This function will check the tx done indication of the current tx_to_send + * locations + * if success, moves the tx_to_send to next location. + * + * @return -1 if TX ownership bit is not cleared by hw. + * else on success (tx done completion) return zero. + */ +int pfe_tx_done(void) +{ + struct tx_desc_s *tx_desc = g_tx_desc; + struct buf_desc *bd; + + debug("%s:tx_base: %p, tx_to_send: %d\n", __func__, tx_desc->tx_base, + tx_desc->tx_to_send); + + bd = tx_desc->tx_base + tx_desc->tx_to_send; + + /* check queue-full condition */ + if (readl(&bd->ctrl) & BD_CTRL_DESC_EN) + return -1; + + /* reset the control field */ + writel(0, &bd->ctrl); + writel(0, &bd->status); + + debug("Tx Done : status: %08x, ctrl: %08x\n", readl(&bd->status), + readl(&bd->ctrl)); + + /* increment the txtosend index to next location */ + tx_desc->tx_to_send = (tx_desc->tx_to_send + 1) + & (tx_desc->tx_ring_size - 1); + + debug("Tx next pkt location: %d\n", tx_desc->tx_to_send); + + return 0; +} + +/* + * Helper function to dump Rx descriptors. + */ +static inline void hif_rx_desc_dump(void) +{ + struct buf_desc *bd_va; + int i; + struct rx_desc_s *rx_desc; + + if (!g_rx_desc) { + printf("%s: HIF Rx desc no init\n", __func__); + return; + } + + rx_desc = g_rx_desc; + bd_va = rx_desc->rx_base; + + debug("HIF rx desc: base_va: %p, base_pa: %08x\n", rx_desc->rx_base, + rx_desc->rx_base_pa); + for (i = 0; i < rx_desc->rx_ring_size; i++) { + debug("status: %08x, ctrl: %08x, data: %08x, next: 0x%08x\n", + readl(&bd_va->status), + readl(&bd_va->ctrl), + readl(&bd_va->data), + readl(&bd_va->next)); + bd_va++; + } +} + +/* + * This function mark all Rx descriptors as LAST_BD. + */ +void hif_rx_desc_disable(void) +{ + int i; + struct rx_desc_s *rx_desc; + struct buf_desc *bd_va; + + if (!g_rx_desc) { + printf("%s: HIF Rx desc not initialized\n", __func__); + return; + } + + rx_desc = g_rx_desc; + bd_va = rx_desc->rx_base; + + for (i = 0; i < rx_desc->rx_ring_size; i++) { + writel(readl(&bd_va->ctrl) | BD_CTRL_LAST_BD, &bd_va->ctrl); + bd_va++; + } +} + +/* + * HIF Rx Desc initialization function. + */ +static int hif_rx_desc_init(struct pfe_ddr_address *pfe_addr) +{ + u32 ctrl; + struct buf_desc *bd_va; + struct buf_desc *bd_pa; + struct rx_desc_s *rx_desc; + u32 rx_buf_pa; + int i; + + /* sanity check */ + if (g_rx_desc) { + printf("%s: HIF Rx desc re-init request\n", __func__); + return 0; + } + + rx_desc = (struct rx_desc_s *)malloc(sizeof(struct rx_desc_s)); + if (!rx_desc) { + printf("%s: Memory allocation failure\n", __func__); + return -ENOMEM; + } + memset(rx_desc, 0, sizeof(struct rx_desc_s)); + + /* init: Rx ring buffer */ + rx_desc->rx_ring_size = HIF_RX_DESC_NT; + + /* NOTE: must be 64bit aligned */ + bd_va = (struct buf_desc *)(pfe_addr->ddr_pfe_baseaddr + + RX_BD_BASEADDR); + bd_pa = (struct buf_desc *)(pfe_addr->ddr_pfe_phys_baseaddr + + RX_BD_BASEADDR); + + rx_desc->rx_base = bd_va; + rx_desc->rx_base_pa = (unsigned long)bd_pa; + + rx_buf_pa = pfe_addr->ddr_pfe_phys_baseaddr + HIF_RX_PKT_DDR_BASEADDR; + + debug("%s: Rx desc base: %p, base_pa: %08x, desc_count: %d\n", + __func__, rx_desc->rx_base, rx_desc->rx_base_pa, + rx_desc->rx_ring_size); + + memset(bd_va, 0, sizeof(struct buf_desc) * rx_desc->rx_ring_size); + + ctrl = (MAX_FRAME_SIZE | BD_CTRL_DESC_EN | BD_CTRL_DIR | BD_CTRL_LIFM); + + for (i = 0; i < rx_desc->rx_ring_size; i++) { + writel((unsigned long)(bd_pa + 1), &bd_va->next); + writel(ctrl, &bd_va->ctrl); + writel(rx_buf_pa + (i * MAX_FRAME_SIZE), &bd_va->data); + bd_va++; + bd_pa++; + } + --bd_va; + writel((u32)rx_desc->rx_base_pa, &bd_va->next); + + writel(rx_desc->rx_base_pa, HIF_RX_BDP_ADDR); + writel((readl(HIF_RX_CTRL) | HIF_CTRL_BDP_CH_START_WSTB), HIF_RX_CTRL); + + g_rx_desc = rx_desc; + + return 0; +} + +/* + * Helper function to dump Tx Descriptors. + */ +static inline void hif_tx_desc_dump(void) +{ + struct tx_desc_s *tx_desc; + int i; + struct buf_desc *bd_va; + + if (!g_tx_desc) { + printf("%s: HIF Tx desc no init\n", __func__); + return; + } + + tx_desc = g_tx_desc; + bd_va = tx_desc->tx_base; + + debug("HIF tx desc: base_va: %p, base_pa: %08x\n", tx_desc->tx_base, + tx_desc->tx_base_pa); + + for (i = 0; i < tx_desc->tx_ring_size; i++) + bd_va++; +} + +/* + * HIF Tx descriptor initialization function. + */ +static int hif_tx_desc_init(struct pfe_ddr_address *pfe_addr) +{ + struct buf_desc *bd_va; + struct buf_desc *bd_pa; + int i; + struct tx_desc_s *tx_desc; + u32 tx_buf_pa; + + /* sanity check */ + if (g_tx_desc) { + printf("%s: HIF Tx desc re-init request\n", __func__); + return 0; + } + + tx_desc = (struct tx_desc_s *)malloc(sizeof(struct tx_desc_s)); + if (!tx_desc) { + printf("%s:%d:Memory allocation failure\n", __func__, + __LINE__); + return -ENOMEM; + } + memset(tx_desc, 0, sizeof(struct tx_desc_s)); + + /* init: Tx ring buffer */ + tx_desc->tx_ring_size = HIF_TX_DESC_NT; + + /* NOTE: must be 64bit aligned */ + bd_va = (struct buf_desc *)(pfe_addr->ddr_pfe_baseaddr + + TX_BD_BASEADDR); + bd_pa = (struct buf_desc *)(pfe_addr->ddr_pfe_phys_baseaddr + + TX_BD_BASEADDR); + + tx_desc->tx_base_pa = (unsigned long)bd_pa; + tx_desc->tx_base = bd_va; + + debug("%s: Tx desc_base: %p, base_pa: %08x, desc_count: %d\n", + __func__, tx_desc->tx_base, tx_desc->tx_base_pa, + tx_desc->tx_ring_size); + + memset(bd_va, 0, sizeof(struct buf_desc) * tx_desc->tx_ring_size); + + tx_buf_pa = pfe_addr->ddr_pfe_phys_baseaddr + HIF_TX_PKT_DDR_BASEADDR; + + for (i = 0; i < tx_desc->tx_ring_size; i++) { + writel((unsigned long)(bd_pa + 1), &bd_va->next); + writel(tx_buf_pa + (i * MAX_FRAME_SIZE), &bd_va->data); + bd_va++; + bd_pa++; + } + --bd_va; + writel((u32)tx_desc->tx_base_pa, &bd_va->next); + + writel(tx_desc->tx_base_pa, HIF_TX_BDP_ADDR); + + g_tx_desc = tx_desc; + + return 0; +} + +/* + * PFE/Class initialization. + */ +static void pfe_class_init(struct pfe_ddr_address *pfe_addr) +{ + struct class_cfg class_cfg = { + .route_table_baseaddr = pfe_addr->ddr_pfe_phys_baseaddr + + ROUTE_TABLE_BASEADDR, + .route_table_hash_bits = ROUTE_TABLE_HASH_BITS, + }; + + class_init(&class_cfg); + + debug("class init complete\n"); +} + +/* + * PFE/TMU initialization. + */ +static void pfe_tmu_init(struct pfe_ddr_address *pfe_addr) +{ + struct tmu_cfg tmu_cfg = { + .llm_base_addr = pfe_addr->ddr_pfe_phys_baseaddr + + TMU_LLM_BASEADDR, + .llm_queue_len = TMU_LLM_QUEUE_LEN, + }; + + tmu_init(&tmu_cfg); + + debug("tmu init complete\n"); +} + +/* + * PFE/BMU (both BMU1 & BMU2) initialization. + */ +static void pfe_bmu_init(struct pfe_ddr_address *pfe_addr) +{ + struct bmu_cfg bmu1_cfg = { + .baseaddr = CBUS_VIRT_TO_PFE(LMEM_BASE_ADDR + + BMU1_LMEM_BASEADDR), + .count = BMU1_BUF_COUNT, + .size = BMU1_BUF_SIZE, + }; + + struct bmu_cfg bmu2_cfg = { + .baseaddr = pfe_addr->ddr_pfe_phys_baseaddr + BMU2_DDR_BASEADDR, + .count = BMU2_BUF_COUNT, + .size = BMU2_BUF_SIZE, + }; + + bmu_init(BMU1_BASE_ADDR, &bmu1_cfg); + debug("bmu1 init: done\n"); + + bmu_init(BMU2_BASE_ADDR, &bmu2_cfg); + debug("bmu2 init: done\n"); +} + +/* + * PFE/GPI initialization function. + * - egpi1, egpi2, egpi3, hgpi + */ +static void pfe_gpi_init(struct pfe_ddr_address *pfe_addr) +{ + struct gpi_cfg egpi1_cfg = { + .lmem_rtry_cnt = EGPI1_LMEM_RTRY_CNT, + .tmlf_txthres = EGPI1_TMLF_TXTHRES, + .aseq_len = EGPI1_ASEQ_LEN, + }; + + struct gpi_cfg egpi2_cfg = { + .lmem_rtry_cnt = EGPI2_LMEM_RTRY_CNT, + .tmlf_txthres = EGPI2_TMLF_TXTHRES, + .aseq_len = EGPI2_ASEQ_LEN, + }; + + struct gpi_cfg hgpi_cfg = { + .lmem_rtry_cnt = HGPI_LMEM_RTRY_CNT, + .tmlf_txthres = HGPI_TMLF_TXTHRES, + .aseq_len = HGPI_ASEQ_LEN, + }; + + gpi_init(EGPI1_BASE_ADDR, &egpi1_cfg); + debug("GPI1 init complete\n"); + + gpi_init(EGPI2_BASE_ADDR, &egpi2_cfg); + debug("GPI2 init complete\n"); + + gpi_init(HGPI_BASE_ADDR, &hgpi_cfg); + debug("HGPI init complete\n"); +} + +/* + * PFE/HIF initialization function. + */ +static int pfe_hif_init(struct pfe_ddr_address *pfe_addr) +{ + int ret = 0; + + hif_tx_disable(); + hif_rx_disable(); + + ret = hif_tx_desc_init(pfe_addr); + if (ret) + return ret; + ret = hif_rx_desc_init(pfe_addr); + if (ret) + return ret; + + hif_init(); + + hif_tx_enable(); + hif_rx_enable(); + + hif_rx_desc_dump(); + hif_tx_desc_dump(); + + debug("HIF init complete\n"); + return ret; +} + +/* + * PFE initialization + * - Firmware loading (CLASS-PE and TMU-PE) + * - BMU1 and BMU2 init + * - GEMAC init + * - GPI init + * - CLASS-PE init + * - TMU-PE init + * - HIF tx and rx descriptors init + * + * @param[in] edev Pointer to eth device structure. + * + * @return 0, on success. + */ +static int pfe_hw_init(struct pfe_ddr_address *pfe_addr) +{ + int ret = 0; + + debug("%s: start\n", __func__); + + writel(0x3, CLASS_PE_SYS_CLK_RATIO); + writel(0x3, TMU_PE_SYS_CLK_RATIO); + writel(0x3, UTIL_PE_SYS_CLK_RATIO); + udelay(10); + + pfe_class_init(pfe_addr); + + pfe_tmu_init(pfe_addr); + + pfe_bmu_init(pfe_addr); + + pfe_gpi_init(pfe_addr); + + ret = pfe_hif_init(pfe_addr); + if (ret) + return ret; + + bmu_enable(BMU1_BASE_ADDR); + debug("bmu1 enabled\n"); + + bmu_enable(BMU2_BASE_ADDR); + debug("bmu2 enabled\n"); + + debug("%s: done\n", __func__); + + return ret; +} + +/* + * PFE driver init function. + * - Initializes pfe_lib + * - pfe hw init + * - fw loading and enables PEs + * - should be executed once. + * + * @param[in] pfe Pointer the pfe control block + */ +int pfe_drv_init(struct pfe_ddr_address *pfe_addr) +{ + int ret = 0; + + pfe_lib_init(); + + ret = pfe_hw_init(pfe_addr); + if (ret) + return ret; + + /* Load the class,TM, Util fw. + * By now pfe is: + * - out of reset + disabled + configured. + * Fw loading should be done after pfe_hw_init() + */ + /* It loads default inbuilt sbl firmware */ + pfe_firmware_init(); + + return ret; +} + +/* + * PFE remove function + * - stops PEs + * - frees tx/rx descriptor resources + * - should be called once. + * + * @param[in] pfe Pointer to pfe control block. + */ +int pfe_eth_remove(struct udevice *dev) +{ + if (g_tx_desc) + free(g_tx_desc); + + if (g_rx_desc) + free(g_rx_desc); + + pfe_firmware_exit(); + + return 0; +} diff --git a/roms/u-boot/drivers/net/pfe_eth/pfe_eth.c b/roms/u-boot/drivers/net/pfe_eth/pfe_eth.c new file mode 100644 index 000000000..0c27a668b --- /dev/null +++ b/roms/u-boot/drivers/net/pfe_eth/pfe_eth.c @@ -0,0 +1,295 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2015-2016 Freescale Semiconductor, Inc. + * Copyright 2017 NXP + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <malloc.h> +#include <dm/platform_data/pfe_dm_eth.h> +#include <net.h> +#include <linux/delay.h> +#include <net/pfe_eth/pfe_eth.h> +#include <net/pfe_eth/pfe_mdio.h> + +struct gemac_s gem_info[] = { + /* PORT_0 configuration */ + { + /* GEMAC config */ + .gemac_speed = PFE_MAC_SPEED_1000M, + .gemac_duplex = DUPLEX_FULL, + + /* phy iface */ + .phy_address = CONFIG_PFE_EMAC1_PHY_ADDR, + .phy_mode = PHY_INTERFACE_MODE_SGMII, + }, + /* PORT_1 configuration */ + { + /* GEMAC config */ + .gemac_speed = PFE_MAC_SPEED_1000M, + .gemac_duplex = DUPLEX_FULL, + + /* phy iface */ + .phy_address = CONFIG_PFE_EMAC2_PHY_ADDR, + .phy_mode = PHY_INTERFACE_MODE_RGMII_ID, + }, +}; + +static inline void pfe_gemac_enable(void *gemac_base) +{ + writel(readl(gemac_base + EMAC_ECNTRL_REG) | + EMAC_ECNTRL_ETHER_EN, gemac_base + EMAC_ECNTRL_REG); +} + +static inline void pfe_gemac_disable(void *gemac_base) +{ + writel(readl(gemac_base + EMAC_ECNTRL_REG) & + ~EMAC_ECNTRL_ETHER_EN, gemac_base + EMAC_ECNTRL_REG); +} + +static inline void pfe_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 int pfe_eth_write_hwaddr(struct udevice *dev) +{ + struct pfe_eth_dev *priv = dev_get_priv(dev); + struct gemac_s *gem = priv->gem; + struct eth_pdata *pdata = dev_get_plat(dev); + uchar *mac = pdata->enetaddr; + + writel((mac[0] << 24) + (mac[1] << 16) + (mac[2] << 8) + mac[3], + gem->gemac_base + EMAC_PHY_ADDR_LOW); + writel((mac[4] << 24) + (mac[5] << 16) + 0x8808, gem->gemac_base + + EMAC_PHY_ADDR_HIGH); + return 0; +} + +/** Stops or Disables GEMAC pointing to this eth iface. + * + * @param[in] edev Pointer to eth device structure. + * + * @return none + */ +static inline void pfe_eth_stop(struct udevice *dev) +{ + struct pfe_eth_dev *priv = dev_get_priv(dev); + + pfe_gemac_disable(priv->gem->gemac_base); + + gpi_disable(priv->gem->egpi_base); +} + +static int pfe_eth_start(struct udevice *dev) +{ + struct pfe_eth_dev *priv = dev_get_priv(dev); + struct gemac_s *gem = priv->gem; + int speed; + + /* set ethernet mac address */ + pfe_eth_write_hwaddr(dev); + + writel(EMAC_TFWR, gem->gemac_base + EMAC_TFWR_STR_FWD); + writel(EMAC_RX_SECTION_FULL_32, gem->gemac_base + EMAC_RX_SECTIOM_FULL); + writel(EMAC_TRUNC_FL_16K, gem->gemac_base + EMAC_TRUNC_FL); + writel(EMAC_TX_SECTION_EMPTY_30, gem->gemac_base + + EMAC_TX_SECTION_EMPTY); + writel(EMAC_MIBC_NO_CLR_NO_DIS, gem->gemac_base + + EMAC_MIB_CTRL_STS_REG); + +#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 + + pfe_gemac_set_speed(gem->gemac_base, speed); + + /* Enable GPI */ + gpi_enable(gem->egpi_base); + + /* Enable GEMAC */ + pfe_gemac_enable(gem->gemac_base); + + return 0; +} + +static int pfe_eth_send(struct udevice *dev, void *packet, int length) +{ + struct pfe_eth_dev *priv = (struct pfe_eth_dev *)dev_get_priv(dev); + + int rc; + int i = 0; + + rc = pfe_send(priv->gemac_port, packet, length); + + if (rc < 0) { + printf("Tx Queue full\n"); + return rc; + } + + 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 pfe_eth_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct pfe_eth_dev *priv = dev_get_priv(dev); + uchar *pkt_buf; + int len; + int phy_port; + + len = pfe_recv(&pkt_buf, &phy_port); + + if (len == 0) + return -EAGAIN; /* no packet in rx */ + else if (len < 0) + return -EAGAIN; + + debug("Rx pkt: pkt_buf(0x%p), 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 -EAGAIN; + } + + *packetp = pkt_buf; + + return len; +} + +static int pfe_eth_probe(struct udevice *dev) +{ + struct pfe_eth_dev *priv = dev_get_priv(dev); + struct pfe_ddr_address pfe_addr; + struct pfe_eth_pdata *pdata = dev_get_plat(dev); + int ret = 0; + static int init_done; + + if (!init_done) { + pfe_addr.ddr_pfe_baseaddr = + (void *)pdata->pfe_ddr_addr.ddr_pfe_baseaddr; + pfe_addr.ddr_pfe_phys_baseaddr = + (unsigned long)pdata->pfe_ddr_addr.ddr_pfe_phys_baseaddr; + + debug("ddr_pfe_baseaddr: %p, ddr_pfe_phys_baseaddr: %08x\n", + pfe_addr.ddr_pfe_baseaddr, + (u32)pfe_addr.ddr_pfe_phys_baseaddr); + + ret = pfe_drv_init(&pfe_addr); + if (ret) + return ret; + + init_pfe_scfg_dcfg_regs(); + init_done = 1; + } + + priv->gemac_port = pdata->pfe_eth_pdata_mac.phy_interface; + priv->gem = &gem_info[priv->gemac_port]; + priv->dev = dev; + + 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; + } + + ret = pfe_eth_board_init(dev); + if (ret) + return ret; + +#if defined(CONFIG_PHYLIB) + ret = pfe_phy_configure(priv, pdata->pfe_eth_pdata_mac.phy_interface, + gem_info[priv->gemac_port].phy_address); +#endif + return ret; +} + +static int pfe_eth_bind(struct udevice *dev) +{ + struct pfe_eth_pdata *pdata = dev_get_plat(dev); + char name[20]; + + sprintf(name, "pfe_eth%u", pdata->pfe_eth_pdata_mac.phy_interface); + + return device_set_name(dev, name); +} + +static const struct eth_ops pfe_eth_ops = { + .start = pfe_eth_start, + .send = pfe_eth_send, + .recv = pfe_eth_recv, + .free_pkt = pfe_eth_free_pkt, + .stop = pfe_eth_stop, + .write_hwaddr = pfe_eth_write_hwaddr, +}; + +U_BOOT_DRIVER(pfe_eth) = { + .name = "pfe_eth", + .id = UCLASS_ETH, + .bind = pfe_eth_bind, + .probe = pfe_eth_probe, + .remove = pfe_eth_remove, + .ops = &pfe_eth_ops, + .priv_auto = sizeof(struct pfe_eth_dev), + .plat_auto = sizeof(struct pfe_eth_pdata) +}; diff --git a/roms/u-boot/drivers/net/pfe_eth/pfe_firmware.c b/roms/u-boot/drivers/net/pfe_eth/pfe_firmware.c new file mode 100644 index 000000000..ac86e33c5 --- /dev/null +++ b/roms/u-boot/drivers/net/pfe_eth/pfe_firmware.c @@ -0,0 +1,399 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2015-2016 Freescale Semiconductor, Inc. + * Copyright 2017,2021 NXP + */ + +/* + * @file + * Contains all the functions to handle parsing and loading of PE firmware + * files. + */ + +#include <dm.h> +#include <dm/device-internal.h> +#include <image.h> +#include <log.h> +#include <malloc.h> +#include <linux/bitops.h> +#include <net/pfe_eth/pfe_eth.h> +#include <net/pfe_eth/pfe_firmware.h> +#include <spi_flash.h> +#ifdef CONFIG_CHAIN_OF_TRUST +#include <fsl_validate.h> +#endif + +#define PFE_FIRMWARE_FIT_CNF_NAME "config@1" + +static const void *pfe_fit_addr; +#ifdef CONFIG_CHAIN_OF_TRUST +static const void *pfe_esbc_hdr_addr; +#endif + +/* + * PFE elf firmware loader. + * Loads an elf firmware image into a list of PE's (specified using a bitmask) + * + * @param pe_mask Mask of PE id's to load firmware to + * @param pfe_firmware Pointer to the firmware image + * + * @return 0 on success, a negative value on error + */ +static int pfe_load_elf(int pe_mask, uint8_t *pfe_firmware) +{ + Elf32_Ehdr *elf_hdr = (Elf32_Ehdr *)pfe_firmware; + Elf32_Half sections = be16_to_cpu(elf_hdr->e_shnum); + Elf32_Shdr *shdr = (Elf32_Shdr *)(pfe_firmware + + be32_to_cpu(elf_hdr->e_shoff)); + int id, section; + int ret; + + debug("%s: no of sections: %d\n", __func__, sections); + + /* Some sanity checks */ + if (strncmp((char *)&elf_hdr->e_ident[EI_MAG0], ELFMAG, SELFMAG)) { + printf("%s: incorrect elf magic number\n", __func__); + return -1; + } + + if (elf_hdr->e_ident[EI_CLASS] != ELFCLASS32) { + printf("%s: incorrect elf class(%x)\n", __func__, + elf_hdr->e_ident[EI_CLASS]); + return -1; + } + + if (elf_hdr->e_ident[EI_DATA] != ELFDATA2MSB) { + printf("%s: incorrect elf data(%x)\n", __func__, + elf_hdr->e_ident[EI_DATA]); + return -1; + } + + if (be16_to_cpu(elf_hdr->e_type) != ET_EXEC) { + printf("%s: incorrect elf file type(%x)\n", __func__, + be16_to_cpu(elf_hdr->e_type)); + return -1; + } + + for (section = 0; section < sections; section++, shdr++) { + if (!(be32_to_cpu(shdr->sh_flags) & (SHF_WRITE | SHF_ALLOC | + SHF_EXECINSTR))) + continue; + for (id = 0; id < MAX_PE; id++) + if (pe_mask & BIT(id)) { + ret = pe_load_elf_section(id, + pfe_firmware, shdr); + if (ret < 0) + goto err; + } + } + return 0; + +err: + return ret; +} + +/* + * Get PFE firmware from FIT image + * + * @param data pointer to PFE firmware + * @param size pointer to size of the firmware + * @param fw_name pfe firmware name, either class or tmu + * + * @return 0 on success, a negative value on error + */ +static int pfe_get_fw(const void **data, + size_t *size, char *fw_name) +{ + int conf_node_off, fw_node_off; + char *conf_node_name = NULL; + char *desc; + int ret = 0; + + conf_node_name = PFE_FIRMWARE_FIT_CNF_NAME; + + conf_node_off = fit_conf_get_node(pfe_fit_addr, conf_node_name); + if (conf_node_off < 0) { + printf("PFE Firmware: %s: no such config\n", conf_node_name); + return -ENOENT; + } + + fw_node_off = fit_conf_get_prop_node(pfe_fit_addr, conf_node_off, + fw_name); + if (fw_node_off < 0) { + printf("PFE Firmware: No '%s' in config\n", + fw_name); + return -ENOLINK; + } + + if (!(fit_image_verify(pfe_fit_addr, fw_node_off))) { + printf("PFE Firmware: Bad firmware image (bad CRC)\n"); + return -EINVAL; + } + + if (fit_image_get_data(pfe_fit_addr, fw_node_off, data, size)) { + printf("PFE Firmware: Can't get %s subimage data/size", + fw_name); + return -ENOENT; + } + + ret = fit_get_desc(pfe_fit_addr, fw_node_off, &desc); + if (ret) + printf("PFE Firmware: Can't get description\n"); + else + printf("%s\n", desc); + + return ret; +} + +/* + * Check PFE FIT image + * + * @return 0 on success, a negative value on error + */ +static int pfe_fit_check(void) +{ + int ret = 0; + + ret = fdt_check_header(pfe_fit_addr); + if (ret) { + printf("PFE Firmware: Bad firmware image (not a FIT image)\n"); + return ret; + } + + if (fit_check_format(pfe_fit_addr, IMAGE_SIZE_INVAL)) { + printf("PFE Firmware: Bad firmware image (bad FIT header)\n"); + ret = -1; + return ret; + } + + return ret; +} + +int pfe_spi_flash_init(void) +{ + struct spi_flash *pfe_flash; + struct udevice *new; + int ret = 0; + void *addr = malloc(CONFIG_SYS_LS_PFE_FW_LENGTH); + + if (!addr) + return -ENOMEM; + + ret = spi_flash_probe_bus_cs(CONFIG_ENV_SPI_BUS, + CONFIG_ENV_SPI_CS, + CONFIG_ENV_SPI_MAX_HZ, + CONFIG_ENV_SPI_MODE, + &new); + if (ret) { + printf("SF: failed to probe spi\n"); + free(addr); + device_remove(new, DM_REMOVE_NORMAL); + return ret; + } + + + pfe_flash = dev_get_uclass_priv(new); + if (!pfe_flash) { + printf("SF: probe for pfe failed\n"); + free(addr); + device_remove(new, DM_REMOVE_NORMAL); + return -ENODEV; + } + + ret = spi_flash_read(pfe_flash, + CONFIG_SYS_LS_PFE_FW_ADDR, + CONFIG_SYS_LS_PFE_FW_LENGTH, + addr); + if (ret) { + printf("SF: read for pfe failed\n"); + free(addr); + spi_flash_free(pfe_flash); + return ret; + } + +#ifdef CONFIG_CHAIN_OF_TRUST + void *hdr_addr = malloc(CONFIG_SYS_LS_PFE_ESBC_LENGTH); + + if (!hdr_addr) { + free(addr); + spi_flash_free(pfe_flash); + return -ENOMEM; + } + + ret = spi_flash_read(pfe_flash, + CONFIG_SYS_LS_PFE_ESBC_ADDR, + CONFIG_SYS_LS_PFE_ESBC_LENGTH, + hdr_addr); + if (ret) { + printf("SF: failed to read pfe esbc header\n"); + free(addr); + free(hdr_addr); + spi_flash_free(pfe_flash); + return ret; + } + + pfe_esbc_hdr_addr = hdr_addr; +#endif + pfe_fit_addr = addr; + spi_flash_free(pfe_flash); + + return ret; +} + +/* + * PFE firmware initialization. + * Loads different firmware files from FIT image. + * Initializes PE IMEM/DMEM and UTIL-PE DDR + * Initializes control path symbol addresses (by looking them up in the elf + * firmware files + * Takes PE's out of reset + * + * @return 0 on success, a negative value on error + */ +int pfe_firmware_init(void) +{ +#define PFE_KEY_HASH NULL + char *pfe_firmware_name; + const void *raw_image_addr; + size_t raw_image_size = 0; + u8 *pfe_firmware; +#ifdef CONFIG_CHAIN_OF_TRUST + uintptr_t pfe_esbc_hdr = 0; + uintptr_t pfe_img_addr = 0; +#endif + int ret = 0; + int fw_count, max_fw_count; + const char *p; + + ret = pfe_spi_flash_init(); + if (ret) + goto err; + + ret = pfe_fit_check(); + if (ret) + goto err; + +#ifdef CONFIG_CHAIN_OF_TRUST + pfe_esbc_hdr = (uintptr_t)pfe_esbc_hdr_addr; + pfe_img_addr = (uintptr_t)pfe_fit_addr; + if (fsl_check_boot_mode_secure() != 0) { + /* + * In case of failure in validation, fsl_secboot_validate + * would not return back in case of Production environment + * with ITS=1. In Development environment (ITS=0 and + * SB_EN=1), the function may return back in case of + * non-fatal failures. + */ + ret = fsl_secboot_validate(pfe_esbc_hdr, + PFE_KEY_HASH, + &pfe_img_addr); + if (ret != 0) + printf("PFE firmware(s) validation failed\n"); + else + printf("PFE firmware(s) validation Successful\n"); + } +#endif + + p = env_get("load_util"); + if (!p) { + max_fw_count = 2; + } else { + max_fw_count = simple_strtoul(p, NULL, 10); + if (max_fw_count) + max_fw_count = 3; + else + max_fw_count = 2; + } + + for (fw_count = 0; fw_count < max_fw_count; fw_count++) { + switch (fw_count) { + case 0: + pfe_firmware_name = "class_slowpath"; + break; + case 1: + pfe_firmware_name = "tmu_slowpath"; + break; + case 2: + pfe_firmware_name = "util_slowpath"; + break; + } + + if (pfe_get_fw(&raw_image_addr, &raw_image_size, + pfe_firmware_name)) { + printf("%s firmware couldn't be found in FIT image\n", + pfe_firmware_name); + break; + } + pfe_firmware = malloc(raw_image_size); + if (!pfe_firmware) + return -ENOMEM; + memcpy((void *)pfe_firmware, (void *)raw_image_addr, + raw_image_size); + + switch (fw_count) { + case 0: + env_set_addr("class_elf_firmware", pfe_firmware); + env_set_addr("class_elf_size", (void *)raw_image_size); + break; + case 1: + env_set_addr("tmu_elf_firmware", pfe_firmware); + env_set_addr("tmu_elf_size", (void *)raw_image_size); + break; + case 2: + env_set_addr("util_elf_firmware", pfe_firmware); + env_set_addr("util_elf_size", (void *)raw_image_size); + break; + } + } + + raw_image_addr = NULL; + pfe_firmware = NULL; + raw_image_size = 0; + for (fw_count = 0; fw_count < 2; fw_count++) { + if (fw_count == 0) + pfe_firmware_name = "class"; + else if (fw_count == 1) + pfe_firmware_name = "tmu"; + + pfe_get_fw(&raw_image_addr, &raw_image_size, pfe_firmware_name); + pfe_firmware = malloc(raw_image_size); + if (!pfe_firmware) + return -ENOMEM; + memcpy((void *)pfe_firmware, (void *)raw_image_addr, + raw_image_size); + + if (fw_count == 0) + ret = pfe_load_elf(CLASS_MASK, pfe_firmware); + else if (fw_count == 1) + ret = pfe_load_elf(TMU_MASK, pfe_firmware); + + if (ret < 0) { + printf("%s: %s firmware load failed\n", __func__, + pfe_firmware_name); + goto err; + } + debug("%s: %s firmware loaded\n", __func__, pfe_firmware_name); + free(pfe_firmware); + } + + tmu_enable(0xb); + class_enable(); + gpi_enable(HGPI_BASE_ADDR); + +err: + return ret; +} + +/* + * PFE firmware cleanup + * Puts PE's in reset + */ +void pfe_firmware_exit(void) +{ + debug("%s\n", __func__); + + class_disable(); + tmu_disable(0xf); + hif_tx_disable(); + hif_rx_disable(); +} diff --git a/roms/u-boot/drivers/net/pfe_eth/pfe_hw.c b/roms/u-boot/drivers/net/pfe_eth/pfe_hw.c new file mode 100644 index 000000000..722f5c238 --- /dev/null +++ b/roms/u-boot/drivers/net/pfe_eth/pfe_hw.c @@ -0,0 +1,1000 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2015-2016 Freescale Semiconductor, Inc. + * Copyright 2017 NXP + */ +#include <log.h> +#include <linux/bitops.h> +#include <net/pfe_eth/pfe_eth.h> +#include <net/pfe_eth/pfe/pfe_hw.h> + +static struct pe_info pe[MAX_PE]; + +/* + * Initializes the PFE library. + * Must be called before using any of the library functions. + */ +void pfe_lib_init(void) +{ + int pfe_pe_id; + + for (pfe_pe_id = CLASS0_ID; pfe_pe_id <= CLASS_MAX_ID; pfe_pe_id++) { + pe[pfe_pe_id].dmem_base_addr = + (u32)CLASS_DMEM_BASE_ADDR(pfe_pe_id); + pe[pfe_pe_id].pmem_base_addr = + (u32)CLASS_IMEM_BASE_ADDR(pfe_pe_id); + pe[pfe_pe_id].pmem_size = (u32)CLASS_IMEM_SIZE; + pe[pfe_pe_id].mem_access_wdata = + (void *)CLASS_MEM_ACCESS_WDATA; + pe[pfe_pe_id].mem_access_addr = (void *)CLASS_MEM_ACCESS_ADDR; + pe[pfe_pe_id].mem_access_rdata = (void *)CLASS_MEM_ACCESS_RDATA; + } + + for (pfe_pe_id = TMU0_ID; pfe_pe_id <= TMU_MAX_ID; pfe_pe_id++) { + if (pfe_pe_id == TMU2_ID) + continue; + pe[pfe_pe_id].dmem_base_addr = + (u32)TMU_DMEM_BASE_ADDR(pfe_pe_id - TMU0_ID); + pe[pfe_pe_id].pmem_base_addr = + (u32)TMU_IMEM_BASE_ADDR(pfe_pe_id - TMU0_ID); + pe[pfe_pe_id].pmem_size = (u32)TMU_IMEM_SIZE; + pe[pfe_pe_id].mem_access_wdata = (void *)TMU_MEM_ACCESS_WDATA; + pe[pfe_pe_id].mem_access_addr = (void *)TMU_MEM_ACCESS_ADDR; + pe[pfe_pe_id].mem_access_rdata = (void *)TMU_MEM_ACCESS_RDATA; + } +} + +/* + * Writes a buffer to PE internal memory from the host + * through indirect access registers. + * + * @param[in] id PE identification (CLASS0_ID, ..., TMU0_ID, + * ..., UTIL_ID) + * @param[in] mem_access_addr DMEM destination address (must be 32bit + * aligned) + * @param[in] src Buffer source address + * @param[in] len Number of bytes to copy + */ +static void pe_mem_memcpy_to32(int id, u32 mem_access_addr, const void *src, + unsigned int len) +{ + u32 offset = 0, val, addr; + unsigned int len32 = len >> 2; + int i; + + addr = mem_access_addr | PE_MEM_ACCESS_WRITE | + PE_MEM_ACCESS_BYTE_ENABLE(0, 4); + + for (i = 0; i < len32; i++, offset += 4, src += 4) { + val = *(u32 *)src; + writel(cpu_to_be32(val), pe[id].mem_access_wdata); + writel(addr + offset, pe[id].mem_access_addr); + } + + len = (len & 0x3); + if (len) { + val = 0; + + addr = (mem_access_addr | PE_MEM_ACCESS_WRITE | + PE_MEM_ACCESS_BYTE_ENABLE(0, len)) + offset; + + for (i = 0; i < len; i++, src++) + val |= (*(u8 *)src) << (8 * i); + + writel(cpu_to_be32(val), pe[id].mem_access_wdata); + writel(addr, pe[id].mem_access_addr); + } +} + +/* + * Writes a buffer to PE internal data memory (DMEM) from the host + * through indirect access registers. + * @param[in] id PE identification (CLASS0_ID, ..., TMU0_ID, + * ..., UTIL_ID) + * @param[in] dst DMEM destination address (must be 32bit + * aligned) + * @param[in] src Buffer source address + * @param[in] len Number of bytes to copy + */ +static void pe_dmem_memcpy_to32(int id, u32 dst, const void *src, + unsigned int len) +{ + pe_mem_memcpy_to32(id, pe[id].dmem_base_addr | dst | PE_MEM_ACCESS_DMEM, + src, len); +} + +/* + * Writes a buffer to PE internal program memory (PMEM) from the host + * through indirect access registers. + * @param[in] id PE identification (CLASS0_ID, ..., TMU0_ID, + * ..., TMU3_ID) + * @param[in] dst PMEM destination address (must be 32bit + * aligned) + * @param[in] src Buffer source address + * @param[in] len Number of bytes to copy + */ +static void pe_pmem_memcpy_to32(int id, u32 dst, const void *src, + unsigned int len) +{ + pe_mem_memcpy_to32(id, pe[id].pmem_base_addr | (dst & (pe[id].pmem_size + - 1)) | PE_MEM_ACCESS_IMEM, src, len); +} + +/* + * Reads PE internal program memory (IMEM) from the host + * through indirect access registers. + * @param[in] id PE identification (CLASS0_ID, ..., TMU0_ID, + * ..., TMU3_ID) + * @param[in] addr PMEM read address (must be aligned on size) + * @param[in] size Number of bytes to read (maximum 4, must not + * cross 32bit boundaries) + * @return the data read (in PE endianness, i.e BE). + */ +u32 pe_pmem_read(int id, u32 addr, u8 size) +{ + u32 offset = addr & 0x3; + u32 mask = 0xffffffff >> ((4 - size) << 3); + u32 val; + + addr = pe[id].pmem_base_addr | ((addr & ~0x3) & (pe[id].pmem_size - 1)) + | PE_MEM_ACCESS_READ | PE_MEM_ACCESS_IMEM | + PE_MEM_ACCESS_BYTE_ENABLE(offset, size); + + writel(addr, pe[id].mem_access_addr); + val = be32_to_cpu(readl(pe[id].mem_access_rdata)); + + return (val >> (offset << 3)) & mask; +} + +/* + * Writes PE internal data memory (DMEM) from the host + * through indirect access registers. + * @param[in] id PE identification (CLASS0_ID, ..., TMU0_ID, + * ..., UTIL_ID) + * @param[in] val Value to write (in PE endianness, i.e BE) + * @param[in] addr DMEM write address (must be aligned on size) + * @param[in] size Number of bytes to write (maximum 4, must not + * cross 32bit boundaries) + */ +void pe_dmem_write(int id, u32 val, u32 addr, u8 size) +{ + u32 offset = addr & 0x3; + + addr = pe[id].dmem_base_addr | (addr & ~0x3) | PE_MEM_ACCESS_WRITE | + PE_MEM_ACCESS_DMEM | PE_MEM_ACCESS_BYTE_ENABLE(offset, size); + + /* Indirect access interface is byte swapping data being written */ + writel(cpu_to_be32(val << (offset << 3)), pe[id].mem_access_wdata); + writel(addr, pe[id].mem_access_addr); +} + +/* + * Reads PE internal data memory (DMEM) from the host + * through indirect access registers. + * @param[in] id PE identification (CLASS0_ID, ..., TMU0_ID, + * ..., UTIL_ID) + * @param[in] addr DMEM read address (must be aligned on size) + * @param[in] size Number of bytes to read (maximum 4, must not + * cross 32bit boundaries) + * @return the data read (in PE endianness, i.e BE). + */ +u32 pe_dmem_read(int id, u32 addr, u8 size) +{ + u32 offset = addr & 0x3; + u32 mask = 0xffffffff >> ((4 - size) << 3); + u32 val; + + addr = pe[id].dmem_base_addr | (addr & ~0x3) | PE_MEM_ACCESS_READ | + PE_MEM_ACCESS_DMEM | PE_MEM_ACCESS_BYTE_ENABLE(offset, size); + + writel(addr, pe[id].mem_access_addr); + + /* Indirect access interface is byte swapping data being read */ + val = be32_to_cpu(readl(pe[id].mem_access_rdata)); + + return (val >> (offset << 3)) & mask; +} + +/* + * This function is used to write to CLASS internal bus peripherals (ccu, + * pe-lem) from the host + * through indirect access registers. + * @param[in] val value to write + * @param[in] addr Address to write to (must be aligned on size) + * @param[in] size Number of bytes to write (1, 2 or 4) + * + */ +static void class_bus_write(u32 val, u32 addr, u8 size) +{ + u32 offset = addr & 0x3; + + writel((addr & CLASS_BUS_ACCESS_BASE_MASK), CLASS_BUS_ACCESS_BASE); + + addr = (addr & ~CLASS_BUS_ACCESS_BASE_MASK) | PE_MEM_ACCESS_WRITE | + (size << 24); + + writel(cpu_to_be32(val << (offset << 3)), CLASS_BUS_ACCESS_WDATA); + writel(addr, CLASS_BUS_ACCESS_ADDR); +} + +/* + * Reads from CLASS internal bus peripherals (ccu, pe-lem) from the host + * through indirect access registers. + * @param[in] addr Address to read from (must be aligned on size) + * @param[in] size Number of bytes to read (1, 2 or 4) + * @return the read data + */ +static u32 class_bus_read(u32 addr, u8 size) +{ + u32 offset = addr & 0x3; + u32 mask = 0xffffffff >> ((4 - size) << 3); + u32 val; + + writel((addr & CLASS_BUS_ACCESS_BASE_MASK), CLASS_BUS_ACCESS_BASE); + + addr = (addr & ~CLASS_BUS_ACCESS_BASE_MASK) | (size << 24); + + writel(addr, CLASS_BUS_ACCESS_ADDR); + val = be32_to_cpu(readl(CLASS_BUS_ACCESS_RDATA)); + + return (val >> (offset << 3)) & mask; +} + +/* + * Writes data to the cluster memory (PE_LMEM) + * @param[in] dst PE LMEM destination address (must be 32bit aligned) + * @param[in] src Buffer source address + * @param[in] len Number of bytes to copy + */ +static void class_pe_lmem_memcpy_to32(u32 dst, const void *src, + unsigned int len) +{ + u32 len32 = len >> 2; + int i; + + for (i = 0; i < len32; i++, src += 4, dst += 4) + class_bus_write(*(u32 *)src, dst, 4); + + if (len & 0x2) { + class_bus_write(*(u16 *)src, dst, 2); + src += 2; + dst += 2; + } + + if (len & 0x1) { + class_bus_write(*(u8 *)src, dst, 1); + src++; + dst++; + } +} + +/* + * Writes value to the cluster memory (PE_LMEM) + * @param[in] dst PE LMEM destination address (must be 32bit aligned) + * @param[in] val Value to write + * @param[in] len Number of bytes to write + */ +static void class_pe_lmem_memset(u32 dst, int val, unsigned int len) +{ + u32 len32 = len >> 2; + int i; + + val = val | (val << 8) | (val << 16) | (val << 24); + + for (i = 0; i < len32; i++, dst += 4) + class_bus_write(val, dst, 4); + + if (len & 0x2) { + class_bus_write(val, dst, 2); + dst += 2; + } + + if (len & 0x1) { + class_bus_write(val, dst, 1); + dst++; + } +} + +/* + * Reads data from the cluster memory (PE_LMEM) + * @param[out] dst pointer to the source buffer data are copied to + * @param[in] len length in bytes of the amount of data to read + * from cluster memory + * @param[in] offset offset in bytes in the cluster memory where data are + * read from + */ +void pe_lmem_read(u32 *dst, u32 len, u32 offset) +{ + u32 len32 = len >> 2; + int i = 0; + + for (i = 0; i < len32; dst++, i++, offset += 4) + *dst = class_bus_read(PE_LMEM_BASE_ADDR + offset, 4); + + if (len & 0x03) + *dst = class_bus_read(PE_LMEM_BASE_ADDR + offset, (len & 0x03)); +} + +/* + * Writes data to the cluster memory (PE_LMEM) + * @param[in] src pointer to the source buffer data are copied from + * @param[in] len length in bytes of the amount of data to write to the + * cluster memory + * @param[in] offset offset in bytes in the cluster memory where data are + * written to + */ +void pe_lmem_write(u32 *src, u32 len, u32 offset) +{ + u32 len32 = len >> 2; + int i = 0; + + for (i = 0; i < len32; src++, i++, offset += 4) + class_bus_write(*src, PE_LMEM_BASE_ADDR + offset, 4); + + if (len & 0x03) + class_bus_write(*src, PE_LMEM_BASE_ADDR + offset, (len & + 0x03)); +} + +/* + * Loads an elf section into pmem + * Code needs to be at least 16bit aligned and only PROGBITS sections are + * supported + * + * @param[in] id PE identification (CLASS0_ID, ..., TMU0_ID, ..., + * TMU3_ID) + * @param[in] data pointer to the elf firmware + * @param[in] shdr pointer to the elf section header + */ +static int pe_load_pmem_section(int id, const void *data, Elf32_Shdr *shdr) +{ + u32 offset = be32_to_cpu(shdr->sh_offset); + u32 addr = be32_to_cpu(shdr->sh_addr); + u32 size = be32_to_cpu(shdr->sh_size); + u32 type = be32_to_cpu(shdr->sh_type); + + if (((unsigned long)(data + offset) & 0x3) != (addr & 0x3)) { + printf( + "%s: load address(%x) and elf file address(%lx) don't have the same alignment\n", + __func__, addr, (unsigned long)data + offset); + + return -1; + } + + if (addr & 0x1) { + printf("%s: load address(%x) is not 16bit aligned\n", + __func__, addr); + return -1; + } + + if (size & 0x1) { + printf("%s: load size(%x) is not 16bit aligned\n", __func__, + size); + return -1; + } + + debug("pmem pe%d @%x len %d\n", id, addr, size); + switch (type) { + case SHT_PROGBITS: + pe_pmem_memcpy_to32(id, addr, data + offset, size); + break; + + default: + printf("%s: unsupported section type(%x)\n", __func__, type); + return -1; + } + + return 0; +} + +/* + * Loads an elf section into dmem + * Data needs to be at least 32bit aligned, NOBITS sections are correctly + * initialized to 0 + * + * @param[in] id PE identification (CLASS0_ID, ..., TMU0_ID, + * ..., UTIL_ID) + * @param[in] data pointer to the elf firmware + * @param[in] shdr pointer to the elf section header + */ +static int pe_load_dmem_section(int id, const void *data, Elf32_Shdr *shdr) +{ + u32 offset = be32_to_cpu(shdr->sh_offset); + u32 addr = be32_to_cpu(shdr->sh_addr); + u32 size = be32_to_cpu(shdr->sh_size); + u32 type = be32_to_cpu(shdr->sh_type); + u32 size32 = size >> 2; + int i; + + if (((unsigned long)(data + offset) & 0x3) != (addr & 0x3)) { + printf( + "%s: load address(%x) and elf file address(%lx) don't have the same alignment\n", + __func__, addr, (unsigned long)data + offset); + + return -1; + } + + if (addr & 0x3) { + printf("%s: load address(%x) is not 32bit aligned\n", + __func__, addr); + return -1; + } + + switch (type) { + case SHT_PROGBITS: + debug("dmem pe%d @%x len %d\n", id, addr, size); + pe_dmem_memcpy_to32(id, addr, data + offset, size); + break; + + case SHT_NOBITS: + debug("dmem zero pe%d @%x len %d\n", id, addr, size); + for (i = 0; i < size32; i++, addr += 4) + pe_dmem_write(id, 0, addr, 4); + + if (size & 0x3) + pe_dmem_write(id, 0, addr, size & 0x3); + + break; + + default: + printf("%s: unsupported section type(%x)\n", __func__, type); + return -1; + } + + return 0; +} + +/* + * Loads an elf section into DDR + * Data needs to be at least 32bit aligned, NOBITS sections are correctly + * initialized to 0 + * + * @param[in] id PE identification (CLASS0_ID, ..., TMU0_ID, + * ..., UTIL_ID) + * @param[in] data pointer to the elf firmware + * @param[in] shdr pointer to the elf section header + */ +static int pe_load_ddr_section(int id, const void *data, Elf32_Shdr *shdr) +{ + u32 offset = be32_to_cpu(shdr->sh_offset); + u32 addr = be32_to_cpu(shdr->sh_addr); + u32 size = be32_to_cpu(shdr->sh_size); + u32 type = be32_to_cpu(shdr->sh_type); + u32 flags = be32_to_cpu(shdr->sh_flags); + + switch (type) { + case SHT_PROGBITS: + debug("ddr pe%d @%x len %d\n", id, addr, size); + if (flags & SHF_EXECINSTR) { + if (id <= CLASS_MAX_ID) { + /* DO the loading only once in DDR */ + if (id == CLASS0_ID) { + debug( + "%s: load address(%x) and elf file address(%lx) rcvd\n" + , __func__, addr, + (unsigned long)data + offset); + if (((unsigned long)(data + offset) + & 0x3) != (addr & 0x3)) { + printf( + "%s: load address(%x) and elf file address(%lx) don't have the same alignment\n", + __func__, addr, + (unsigned long)data + + offset); + + return -1; + } + + if (addr & 0x1) { + printf( + "%s: load address(%x) is not 16bit aligned\n" + , __func__, addr); + return -1; + } + + if (size & 0x1) { + printf( + "%s: load length(%x) is not 16bit aligned\n" + , __func__, size); + return -1; + } + + memcpy((void *)DDR_PFE_TO_VIRT(addr), + data + offset, size); + } + } else { + printf( + "%s: unsupported ddr section type(%x) for PE(%d)\n" + , __func__, type, id); + return -1; + } + + } else { + memcpy((void *)DDR_PFE_TO_VIRT(addr), data + offset, + size); + } + + break; + + case SHT_NOBITS: + debug("ddr zero pe%d @%x len %d\n", id, addr, size); + memset((void *)DDR_PFE_TO_VIRT(addr), 0, size); + + break; + + default: + printf("%s: unsupported section type(%x)\n", __func__, type); + return -1; + } + + return 0; +} + +/* + * Loads an elf section into pe lmem + * Data needs to be at least 32bit aligned, NOBITS sections are correctly + * initialized to 0 + * + * @param[in] id PE identification (CLASS0_ID,..., CLASS5_ID) + * @param[in] data pointer to the elf firmware + * @param[in] shdr pointer to the elf section header + */ +static int pe_load_pe_lmem_section(int id, const void *data, Elf32_Shdr *shdr) +{ + u32 offset = be32_to_cpu(shdr->sh_offset); + u32 addr = be32_to_cpu(shdr->sh_addr); + u32 size = be32_to_cpu(shdr->sh_size); + u32 type = be32_to_cpu(shdr->sh_type); + + if (id > CLASS_MAX_ID) { + printf("%s: unsupported pe-lmem section type(%x) for PE(%d)\n", + __func__, type, id); + return -1; + } + + if (((unsigned long)(data + offset) & 0x3) != (addr & 0x3)) { + printf( + "%s: load address(%x) and elf file address(%lx) don't have the same alignment\n", + __func__, addr, (unsigned long)data + offset); + + return -1; + } + + if (addr & 0x3) { + printf("%s: load address(%x) is not 32bit aligned\n", + __func__, addr); + return -1; + } + + debug("lmem pe%d @%x len %d\n", id, addr, size); + + switch (type) { + case SHT_PROGBITS: + class_pe_lmem_memcpy_to32(addr, data + offset, size); + break; + + case SHT_NOBITS: + class_pe_lmem_memset(addr, 0, size); + break; + + default: + printf("%s: unsupported section type(%x)\n", __func__, type); + return -1; + } + + return 0; +} + +/* + * Loads an elf section into a PE + * For now only supports loading a section to dmem (all PE's), pmem (class and + * tmu PE's), DDDR (util PE code) + * @param[in] id PE identification (CLASS0_ID, ..., TMU0_ID, + * ..., UTIL_ID) + * @param[in] data pointer to the elf firmware + * @param[in] shdr pointer to the elf section header + */ +int pe_load_elf_section(int id, const void *data, Elf32_Shdr *shdr) +{ + u32 addr = be32_to_cpu(shdr->sh_addr); + u32 size = be32_to_cpu(shdr->sh_size); + + if (IS_DMEM(addr, size)) + return pe_load_dmem_section(id, data, shdr); + else if (IS_PMEM(addr, size)) + return pe_load_pmem_section(id, data, shdr); + else if (IS_PFE_LMEM(addr, size)) + return 0; + else if (IS_PHYS_DDR(addr, size)) + return pe_load_ddr_section(id, data, shdr); + else if (IS_PE_LMEM(addr, size)) + return pe_load_pe_lmem_section(id, data, shdr); + + printf("%s: unsupported memory range(%x)\n", __func__, addr); + + return 0; +} + +/**************************** BMU ***************************/ +/* + * Resets a BMU block. + * @param[in] base BMU block base address + */ +static inline void bmu_reset(void *base) +{ + writel(CORE_SW_RESET, base + BMU_CTRL); + + /* Wait for self clear */ + while (readl(base + BMU_CTRL) & CORE_SW_RESET) + ; +} + +/* + * Enabled a BMU block. + * @param[in] base BMU block base address + */ +void bmu_enable(void *base) +{ + writel(CORE_ENABLE, base + BMU_CTRL); +} + +/* + * Disables a BMU block. + * @param[in] base BMU block base address + */ +static inline void bmu_disable(void *base) +{ + writel(CORE_DISABLE, base + BMU_CTRL); +} + +/* + * Sets the configuration of a BMU block. + * @param[in] base BMU block base address + * @param[in] cfg BMU configuration + */ +static inline void bmu_set_config(void *base, struct bmu_cfg *cfg) +{ + writel(cfg->baseaddr, base + BMU_UCAST_BASE_ADDR); + writel(cfg->count & 0xffff, base + BMU_UCAST_CONFIG); + writel(cfg->size & 0xffff, base + BMU_BUF_SIZE); + + /* Interrupts are never used */ + writel(0x0, base + BMU_INT_ENABLE); +} + +/* + * Initializes a BMU block. + * @param[in] base BMU block base address + * @param[in] cfg BMU configuration + */ +void bmu_init(void *base, struct bmu_cfg *cfg) +{ + bmu_disable(base); + + bmu_set_config(base, cfg); + + bmu_reset(base); +} + +/**************************** GPI ***************************/ +/* + * Resets a GPI block. + * @param[in] base GPI base address + */ +static inline void gpi_reset(void *base) +{ + writel(CORE_SW_RESET, base + GPI_CTRL); +} + +/* + * Enables a GPI block. + * @param[in] base GPI base address + */ +void gpi_enable(void *base) +{ + writel(CORE_ENABLE, base + GPI_CTRL); +} + +/* + * Disables a GPI block. + * @param[in] base GPI base address + */ +void gpi_disable(void *base) +{ + writel(CORE_DISABLE, base + GPI_CTRL); +} + +/* + * Sets the configuration of a GPI block. + * @param[in] base GPI base address + * @param[in] cfg GPI configuration + */ +static inline void gpi_set_config(void *base, struct gpi_cfg *cfg) +{ + writel(CBUS_VIRT_TO_PFE(BMU1_BASE_ADDR + BMU_ALLOC_CTRL), base + + GPI_LMEM_ALLOC_ADDR); + writel(CBUS_VIRT_TO_PFE(BMU1_BASE_ADDR + BMU_FREE_CTRL), base + + GPI_LMEM_FREE_ADDR); + writel(CBUS_VIRT_TO_PFE(BMU2_BASE_ADDR + BMU_ALLOC_CTRL), base + + GPI_DDR_ALLOC_ADDR); + writel(CBUS_VIRT_TO_PFE(BMU2_BASE_ADDR + BMU_FREE_CTRL), base + + GPI_DDR_FREE_ADDR); + writel(CBUS_VIRT_TO_PFE(CLASS_INQ_PKTPTR), base + GPI_CLASS_ADDR); + writel(DDR_HDR_SIZE, base + GPI_DDR_DATA_OFFSET); + writel(LMEM_HDR_SIZE, base + GPI_LMEM_DATA_OFFSET); + writel(0, base + GPI_LMEM_SEC_BUF_DATA_OFFSET); + writel(0, base + GPI_DDR_SEC_BUF_DATA_OFFSET); + writel((DDR_HDR_SIZE << 16) | LMEM_HDR_SIZE, base + GPI_HDR_SIZE); + writel((DDR_BUF_SIZE << 16) | LMEM_BUF_SIZE, base + GPI_BUF_SIZE); + + writel(((cfg->lmem_rtry_cnt << 16) | (GPI_DDR_BUF_EN << 1) | + GPI_LMEM_BUF_EN), base + GPI_RX_CONFIG); + writel(cfg->tmlf_txthres, base + GPI_TMLF_TX); + writel(cfg->aseq_len, base + GPI_DTX_ASEQ); + + /*Make GPI AXI transactions non-bufferable */ + writel(0x1, base + GPI_AXI_CTRL); +} + +/* + * Initializes a GPI block. + * @param[in] base GPI base address + * @param[in] cfg GPI configuration + */ +void gpi_init(void *base, struct gpi_cfg *cfg) +{ + gpi_reset(base); + + gpi_disable(base); + + gpi_set_config(base, cfg); +} + +/**************************** CLASSIFIER ***************************/ +/* + * Resets CLASSIFIER block. + */ +static inline void class_reset(void) +{ + writel(CORE_SW_RESET, CLASS_TX_CTRL); +} + +/* + * Enables all CLASS-PE's cores. + */ +void class_enable(void) +{ + writel(CORE_ENABLE, CLASS_TX_CTRL); +} + +/* + * Disables all CLASS-PE's cores. + */ +void class_disable(void) +{ + writel(CORE_DISABLE, CLASS_TX_CTRL); +} + +/* + * Sets the configuration of the CLASSIFIER block. + * @param[in] cfg CLASSIFIER configuration + */ +static inline void class_set_config(struct class_cfg *cfg) +{ + if (PLL_CLK_EN == 0) { + /* Clock ratio: for 1:1 the value is 0 */ + writel(0x0, CLASS_PE_SYS_CLK_RATIO); + } else { + /* Clock ratio: for 1:2 the value is 1 */ + writel(0x1, CLASS_PE_SYS_CLK_RATIO); + } + writel((DDR_HDR_SIZE << 16) | LMEM_HDR_SIZE, CLASS_HDR_SIZE); + writel(LMEM_BUF_SIZE, CLASS_LMEM_BUF_SIZE); + writel(CLASS_ROUTE_ENTRY_SIZE(CLASS_ROUTE_SIZE) | + CLASS_ROUTE_HASH_SIZE(cfg->route_table_hash_bits), + CLASS_ROUTE_HASH_ENTRY_SIZE); + writel(HASH_CRC_PORT_IP | QB2BUS_LE, CLASS_ROUTE_MULTI); + + writel(cfg->route_table_baseaddr, CLASS_ROUTE_TABLE_BASE); + memset((void *)DDR_PFE_TO_VIRT(cfg->route_table_baseaddr), 0, + ROUTE_TABLE_SIZE); + + writel(CLASS_PE0_RO_DM_ADDR0_VAL, CLASS_PE0_RO_DM_ADDR0); + writel(CLASS_PE0_RO_DM_ADDR1_VAL, CLASS_PE0_RO_DM_ADDR1); + writel(CLASS_PE0_QB_DM_ADDR0_VAL, CLASS_PE0_QB_DM_ADDR0); + writel(CLASS_PE0_QB_DM_ADDR1_VAL, CLASS_PE0_QB_DM_ADDR1); + writel(CBUS_VIRT_TO_PFE(TMU_PHY_INQ_PKTPTR), CLASS_TM_INQ_ADDR); + + writel(23, CLASS_AFULL_THRES); + writel(23, CLASS_TSQ_FIFO_THRES); + + writel(24, CLASS_MAX_BUF_CNT); + writel(24, CLASS_TSQ_MAX_CNT); + + /*Make Class AXI transactions non-bufferable */ + writel(0x1, CLASS_AXI_CTRL); + + /*Make Util AXI transactions non-bufferable */ + /*Util is disabled in U-boot, do it from here */ + writel(0x1, UTIL_AXI_CTRL); +} + +/* + * Initializes CLASSIFIER block. + * @param[in] cfg CLASSIFIER configuration + */ +void class_init(struct class_cfg *cfg) +{ + class_reset(); + + class_disable(); + + class_set_config(cfg); +} + +/**************************** TMU ***************************/ +/* + * Enables TMU-PE cores. + * @param[in] pe_mask TMU PE mask + */ +void tmu_enable(u32 pe_mask) +{ + writel(readl(TMU_TX_CTRL) | (pe_mask & 0xF), TMU_TX_CTRL); +} + +/* + * Disables TMU cores. + * @param[in] pe_mask TMU PE mask + */ +void tmu_disable(u32 pe_mask) +{ + writel(readl(TMU_TX_CTRL) & ~(pe_mask & 0xF), TMU_TX_CTRL); +} + +/* + * Initializes TMU block. + * @param[in] cfg TMU configuration + */ +void tmu_init(struct tmu_cfg *cfg) +{ + int q, phyno; + + /* keep in soft reset */ + writel(SW_RESET, TMU_CTRL); + + /*Make Class AXI transactions non-bufferable */ + writel(0x1, TMU_AXI_CTRL); + + /* enable EMAC PHY ports */ + writel(0x3, TMU_SYS_GENERIC_CONTROL); + + writel(750, TMU_INQ_WATERMARK); + + writel(CBUS_VIRT_TO_PFE(EGPI1_BASE_ADDR + GPI_INQ_PKTPTR), + TMU_PHY0_INQ_ADDR); + writel(CBUS_VIRT_TO_PFE(EGPI2_BASE_ADDR + GPI_INQ_PKTPTR), + TMU_PHY1_INQ_ADDR); + + writel(CBUS_VIRT_TO_PFE(HGPI_BASE_ADDR + GPI_INQ_PKTPTR), + TMU_PHY3_INQ_ADDR); + writel(CBUS_VIRT_TO_PFE(HIF_NOCPY_RX_INQ0_PKTPTR), TMU_PHY4_INQ_ADDR); + writel(CBUS_VIRT_TO_PFE(UTIL_INQ_PKTPTR), TMU_PHY5_INQ_ADDR); + writel(CBUS_VIRT_TO_PFE(BMU2_BASE_ADDR + BMU_FREE_CTRL), + TMU_BMU_INQ_ADDR); + + /* enabling all 10 schedulers [9:0] of each TDQ */ + writel(0x3FF, TMU_TDQ0_SCH_CTRL); + writel(0x3FF, TMU_TDQ1_SCH_CTRL); + writel(0x3FF, TMU_TDQ3_SCH_CTRL); + + if (PLL_CLK_EN == 0) { + /* Clock ratio: for 1:1 the value is 0 */ + writel(0x0, TMU_PE_SYS_CLK_RATIO); + } else { + /* Clock ratio: for 1:2 the value is 1 */ + writel(0x1, TMU_PE_SYS_CLK_RATIO); + } + + /* Extra packet pointers will be stored from this address onwards */ + debug("TMU_LLM_BASE_ADDR %x\n", cfg->llm_base_addr); + writel(cfg->llm_base_addr, TMU_LLM_BASE_ADDR); + + debug("TMU_LLM_QUE_LEN %x\n", cfg->llm_queue_len); + writel(cfg->llm_queue_len, TMU_LLM_QUE_LEN); + + writel(5, TMU_TDQ_IIFG_CFG); + writel(DDR_BUF_SIZE, TMU_BMU_BUF_SIZE); + + writel(0x0, TMU_CTRL); + + /* MEM init */ + writel(MEM_INIT, TMU_CTRL); + + while (!(readl(TMU_CTRL) & MEM_INIT_DONE)) + ; + + /* LLM init */ + writel(LLM_INIT, TMU_CTRL); + + while (!(readl(TMU_CTRL) & LLM_INIT_DONE)) + ; + + /* set up each queue for tail drop */ + for (phyno = 0; phyno < 4; phyno++) { + if (phyno == 2) + continue; + for (q = 0; q < 16; q++) { + u32 qmax; + + writel((phyno << 8) | q, TMU_TEQ_CTRL); + writel(BIT(22), TMU_TEQ_QCFG); + + if (phyno == 3) + qmax = DEFAULT_TMU3_QDEPTH; + else + qmax = (q == 0) ? DEFAULT_Q0_QDEPTH : + DEFAULT_MAX_QDEPTH; + + writel(qmax << 18, TMU_TEQ_HW_PROB_CFG2); + writel(qmax >> 14, TMU_TEQ_HW_PROB_CFG3); + } + } + writel(0x05, TMU_TEQ_DISABLE_DROPCHK); + writel(0, TMU_CTRL); +} + +/**************************** HIF ***************************/ +/* + * Enable hif tx DMA and interrupt + */ +void hif_tx_enable(void) +{ + writel(HIF_CTRL_DMA_EN, HIF_TX_CTRL); +} + +/* + * Disable hif tx DMA and interrupt + */ +void hif_tx_disable(void) +{ + u32 hif_int; + + writel(0, HIF_TX_CTRL); + + hif_int = readl(HIF_INT_ENABLE); + hif_int &= HIF_TXPKT_INT_EN; + writel(hif_int, HIF_INT_ENABLE); +} + +/* + * Enable hif rx DMA and interrupt + */ +void hif_rx_enable(void) +{ + writel((HIF_CTRL_DMA_EN | HIF_CTRL_BDP_CH_START_WSTB), HIF_RX_CTRL); +} + +/* + * Disable hif rx DMA and interrupt + */ +void hif_rx_disable(void) +{ + u32 hif_int; + + writel(0, HIF_RX_CTRL); + + hif_int = readl(HIF_INT_ENABLE); + hif_int &= HIF_RXPKT_INT_EN; + writel(hif_int, HIF_INT_ENABLE); +} + +/* + * Initializes HIF copy block. + */ +void hif_init(void) +{ + /* Initialize HIF registers */ + writel(HIF_RX_POLL_CTRL_CYCLE << 16 | HIF_TX_POLL_CTRL_CYCLE, + HIF_POLL_CTRL); + /* Make HIF AXI transactions non-bufferable */ + writel(0x1, HIF_AXI_CTRL); +} diff --git a/roms/u-boot/drivers/net/pfe_eth/pfe_mdio.c b/roms/u-boot/drivers/net/pfe_eth/pfe_mdio.c new file mode 100644 index 000000000..3228b8df4 --- /dev/null +++ b/roms/u-boot/drivers/net/pfe_eth/pfe_mdio.c @@ -0,0 +1,291 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2015-2016 Freescale Semiconductor, Inc. + * Copyright 2017 NXP + */ +#include <common.h> +#include <dm.h> +#include <log.h> +#include <malloc.h> +#include <dm/platform_data/pfe_dm_eth.h> +#include <net.h> +#include <linux/delay.h> +#include <net/pfe_eth/pfe_eth.h> + +extern struct gemac_s gem_info[]; +#if defined(CONFIG_PHYLIB) + +#define MDIO_TIMEOUT 5000 +static int pfe_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 pfe_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 { + pfe_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 pfe_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; + + if (dev_addr == MDIO_DEVAD_NONE) { + reg = ((reg_addr & EMAC_MII_DATA_RA_MASK) << + EMAC_MII_DATA_RA_SHIFT); + } else { + pfe_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 0; +} + +static void pfe_configure_serdes(struct pfe_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; + + + /* PCS configuration done with corresponding GEMAC */ + bus.priv = gem_info[priv->gemac_port].gemac_base; + + pfe_phy_read(&bus, 0, MDIO_DEVAD_NONE, 0x0); + pfe_phy_read(&bus, 0, MDIO_DEVAD_NONE, 0x1); + pfe_phy_read(&bus, 0, MDIO_DEVAD_NONE, 0x2); + pfe_phy_read(&bus, 0, MDIO_DEVAD_NONE, 0x3); + + /* Reset serdes */ + pfe_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; + + pfe_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0x14, value); + + /* Dev ability according to SGMII specification */ + value = PHY_SGMII_DEV_ABILITY_SGMII; + pfe_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0x4, value); + + /* These values taken from validation team */ + if (!sgmii_2500) { + pfe_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0x13, 0x0); + pfe_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0x12, 0x400); + } else { + pfe_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0x13, 0x7); + pfe_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; + /* Disable Auto neg for 2.5G SGMII as it doesn't support auto neg*/ + if (sgmii_2500) + value &= ~PHY_SGMII_ENABLE_AN; + pfe_phy_write(&bus, 0, MDIO_DEVAD_NONE, 0, value); +} + +int pfe_phy_configure(struct pfe_eth_dev *priv, int dev_id, int phy_id) +{ + struct phy_device *phydev = NULL; + struct udevice *dev = priv->dev; + struct gemac_s *gem = priv->gem; + struct ccsr_scfg *scfg = (struct ccsr_scfg *)CONFIG_SYS_FSL_SCFG_ADDR; + + if (!gem->bus) + return -1; + + /* Configure SGMII PCS */ + if (gem->phy_mode == PHY_INTERFACE_MODE_SGMII || + gem->phy_mode == PHY_INTERFACE_MODE_SGMII_2500) { + out_be32(&scfg->mdioselcr, 0x00000000); + pfe_configure_serdes(priv); + } + + mdelay(100); + + /* By this time on-chip SGMII initialization is done + * we can switch mdio interface to external PHYs + */ + out_be32(&scfg->mdioselcr, 0x80000000); + + phydev = phy_connect(gem->bus, phy_id, dev, gem->phy_mode); + if (!phydev) { + printf("phy_connect failed\n"); + return -ENODEV; + } + + phy_config(phydev); + + priv->phydev = phydev; + + return 0; +} +#endif + +struct mii_dev *pfe_mdio_init(struct pfe_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 = pfe_phy_read; + bus->write = pfe_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; +} + +void pfe_set_mdio(int dev_id, struct mii_dev *bus) +{ + gem_info[dev_id].bus = bus; +} + +void pfe_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; +} |