From 55f010356763d567ec9ec23f29a58af7b0fd2185 Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Mon, 16 Nov 2020 18:02:28 +0100 Subject: [PATCH 1/9] net: e1000: Remove unused bus_to_phys() macro bus_to_phys() is defined but not referenced at all. This patch removes it completely. Signed-off-by: Stefan Roese Cc: Joe Hershberger Cc: Aaron Williams Cc: Chandrakala Chavva --- drivers/net/e1000.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/e1000.c b/drivers/net/e1000.c index 733588302db..372f6c30ba6 100644 --- a/drivers/net/e1000.c +++ b/drivers/net/e1000.c @@ -47,10 +47,8 @@ tested on both gig copper and gig fiber boards #ifdef CONFIG_DM_ETH #define virt_to_bus(devno, v) dm_pci_virt_to_mem(devno, (void *) (v)) -#define bus_to_phys(devno, a) dm_pci_mem_to_phys(devno, a) #else #define virt_to_bus(devno, v) pci_virt_to_mem(devno, (void *) (v)) -#define bus_to_phys(devno, a) pci_mem_to_phys(devno, a) #endif #define E1000_DEFAULT_PCI_PBA 0x00000030 From 919c8ede869caeb1a60fb596e759a2bf74030801 Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Mon, 16 Nov 2020 18:02:29 +0100 Subject: [PATCH 2/9] net: e1000: Use virt_to_phys() instead of pci_virt_to_mem() Using (dm_)pci_virt_to_mem() is incorrect to translate the virtual address in local DRAM to a physical address. The correct macro here is virt_to_phys() so switch to using this macro. As virt_to_bus() is now not used any more, this patch also removes both definitions (DM and non-DM). This issue was detected while testing the e1000 driver on the MIPS Octeon III platform, which needs address translation. Signed-off-by: Stefan Roese Cc: Joe Hershberger Cc: Aaron Williams Cc: Chandrakala Chavva --- drivers/net/e1000.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/net/e1000.c b/drivers/net/e1000.c index 372f6c30ba6..6d6bf8c827b 100644 --- a/drivers/net/e1000.c +++ b/drivers/net/e1000.c @@ -45,12 +45,6 @@ tested on both gig copper and gig fiber boards #define TOUT_LOOP 100000 -#ifdef CONFIG_DM_ETH -#define virt_to_bus(devno, v) dm_pci_virt_to_mem(devno, (void *) (v)) -#else -#define virt_to_bus(devno, v) pci_virt_to_mem(devno, (void *) (v)) -#endif - #define E1000_DEFAULT_PCI_PBA 0x00000030 #define E1000_DEFAULT_PCIE_PBA 0x000a0026 @@ -5385,7 +5379,7 @@ static int _e1000_transmit(struct e1000_hw *hw, void *txpacket, int length) txp = tx_base + tx_tail; tx_tail = (tx_tail + 1) % 8; - txp->buffer_addr = cpu_to_le64(virt_to_bus(hw->pdev, nv_packet)); + txp->buffer_addr = cpu_to_le64(virt_to_phys(nv_packet)); txp->lower.data = cpu_to_le32(hw->txd_cmd | length); txp->upper.data = 0; From 14807449a4a04fa478c5aba2142b6c157cfcfe4b Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Mon, 16 Nov 2020 18:02:30 +0100 Subject: [PATCH 3/9] net: e1000: Add missing address translations Add some missing address translations from virtual address in local DRAM to physical address, which is needed for the DMA transactions to work correctly. This issue was detected while testing the e1000 driver on the MIPS Octeon III platform, which needs address translation. Signed-off-by: Stefan Roese Cc: Joe Hershberger Cc: Aaron Williams Cc: Chandrakala Chavva --- drivers/net/e1000.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/e1000.c b/drivers/net/e1000.c index 6d6bf8c827b..694114eca79 100644 --- a/drivers/net/e1000.c +++ b/drivers/net/e1000.c @@ -5141,7 +5141,7 @@ fill_rx(struct e1000_hw *hw) rd = rx_base + rx_tail; rx_tail = (rx_tail + 1) % 8; memset(rd, 0, 16); - rd->buffer_addr = cpu_to_le64((unsigned long)packet); + rd->buffer_addr = cpu_to_le64(virt_to_phys(packet)); /* * Make sure there are no stale data in WB over this area, which @@ -5172,8 +5172,8 @@ e1000_configure_tx(struct e1000_hw *hw) unsigned long tipg, tarc; uint32_t ipgr1, ipgr2; - E1000_WRITE_REG(hw, TDBAL, lower_32_bits((unsigned long)tx_base)); - E1000_WRITE_REG(hw, TDBAH, upper_32_bits((unsigned long)tx_base)); + E1000_WRITE_REG(hw, TDBAL, lower_32_bits(virt_to_phys(tx_base))); + E1000_WRITE_REG(hw, TDBAH, upper_32_bits(virt_to_phys(tx_base))); E1000_WRITE_REG(hw, TDLEN, 128); @@ -5317,8 +5317,8 @@ e1000_configure_rx(struct e1000_hw *hw) E1000_WRITE_FLUSH(hw); } /* Setup the Base and Length of the Rx Descriptor Ring */ - E1000_WRITE_REG(hw, RDBAL, lower_32_bits((unsigned long)rx_base)); - E1000_WRITE_REG(hw, RDBAH, upper_32_bits((unsigned long)rx_base)); + E1000_WRITE_REG(hw, RDBAL, lower_32_bits(virt_to_phys(rx_base))); + E1000_WRITE_REG(hw, RDBAH, upper_32_bits(virt_to_phys(rx_base))); E1000_WRITE_REG(hw, RDLEN, 128); From d9506cd41ce98e10a29c25c4766878367103bb2d Mon Sep 17 00:00:00 2001 From: Yang Liu Date: Mon, 21 Dec 2020 14:44:39 +1100 Subject: [PATCH 4/9] net: fix ping in netconsole Should not init eth device when doing ping in netconsole. Signed-off-by: Yang Liu Cc: Joe Hershberger --- net/net.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/net.c b/net/net.c index ad7e3b3cf8e..b58f3062b20 100644 --- a/net/net.c +++ b/net/net.c @@ -412,7 +412,7 @@ int net_loop(enum proto_t protocol) bootstage_mark_name(BOOTSTAGE_ID_ETH_START, "eth_start"); net_init(); - if (eth_is_on_demand_init() || protocol != NETCONS) { + if (eth_is_on_demand_init()) { eth_halt(); eth_set_current(); ret = eth_init(); From febe13b438b3b02f0629f91f36928f3f300c7372 Mon Sep 17 00:00:00 2001 From: Aaron Tseng Date: Thu, 14 Jan 2021 13:34:11 -0800 Subject: [PATCH 5/9] net: cortina_ni: Add eth support for Cortina Access CAxxxx SoCs Add Cortina Access Ethernet device driver for CAxxxx SoCs. This driver supports both legacy and DM_ETH network models. Signed-off-by: Aaron Tseng Signed-off-by: Alex Nemirovsky Signed-off-by: Abbie Chang CC: Joe Hershberger CC: Abbie Chang CC: Tom Rini --- MAINTAINERS | 4 + drivers/net/Kconfig | 7 + drivers/net/Makefile | 1 + drivers/net/cortina_ni.c | 1103 ++++++++++++++++++++++++++++++++++++++ drivers/net/cortina_ni.h | 401 ++++++++++++++ 5 files changed, 1516 insertions(+) create mode 100644 drivers/net/cortina_ni.c create mode 100644 drivers/net/cortina_ni.h diff --git a/MAINTAINERS b/MAINTAINERS index 1864fd8748d..1e5a361a87e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -200,6 +200,8 @@ F: drivers/i2c/i2c-cortina.c F: drivers/i2c/i2c-cortina.h F: drivers/mtd/nand/raw/cortina_nand.c F: drivers/mtd/nand/raw/cortina_nand.h +F: drivers/net/cortina_ni.c +F: drivers/net/cortina_ni.h F: configs/cortina_presidio-asic-pnand_defconfig ARM/CZ.NIC TURRIS MOX SUPPORT @@ -811,6 +813,8 @@ F: drivers/mmc/ca_dw_mmc.c F: drivers/spi/ca_sflash.c F: drivers/i2c/i2c-cortina.c F: drivers/i2c/i2c-cortina.h +F: drivers/net/cortina_ni.c +F: drivers/net/cortina_ni.h MIPS MEDIATEK M: Weijie Gao diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index d1a52c72643..971a5722482 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -149,6 +149,13 @@ config BCMGENET help This driver supports the BCMGENET Ethernet MAC. +config CORTINA_NI_ENET + bool "Cortina-Access Ethernet driver" + depends on DM_ETH && CORTINA_PLATFORM + help + This driver supports the Cortina-Access Ethernet MAC for + all supported CAxxxx SoCs. + config DWC_ETH_QOS bool "Synopsys DWC Ethernet QOS device support" depends on DM_ETH diff --git a/drivers/net/Makefile b/drivers/net/Makefile index f2a0df509d3..6712b74a897 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_DRIVER_AX88180) += ax88180.o obj-$(CONFIG_BCM_SF2_ETH) += bcm-sf2-eth.o obj-$(CONFIG_BCM_SF2_ETH_GMAC) += bcm-sf2-eth-gmac.o obj-$(CONFIG_CALXEDA_XGMAC) += calxedaxgmac.o +obj-$(CONFIG_CORTINA_NI_ENET) += cortina_ni.o obj-$(CONFIG_CS8900) += cs8900.o obj-$(CONFIG_TULIP) += dc2114x.o obj-$(CONFIG_ETH_DESIGNWARE) += designware.o diff --git a/drivers/net/cortina_ni.c b/drivers/net/cortina_ni.c new file mode 100644 index 00000000000..ee424d95bc0 --- /dev/null +++ b/drivers/net/cortina_ni.c @@ -0,0 +1,1103 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/* + * Copyright (C) 2020 Cortina Access Inc. + * Author: Aaron Tseng + * + * Ethernet MAC Driver for all supported CAxxxx SoCs + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cortina_ni.h" + +#define HEADER_A_SIZE 8 + +enum ca_led_state_t { + CA_LED_OFF = 0, + CA_LED_ON = 1, +}; + +enum ca_port_t { + NI_PORT_0 = 0, + NI_PORT_1, + NI_PORT_2, + NI_PORT_3, + NI_PORT_4, + NI_PORT_5, + NI_PORT_MAX, +}; + +static struct udevice *curr_dev; + +static u32 *ca_rdwrptr_adv_one(u32 *x, unsigned long base, unsigned long max) +{ + if (x + 1 >= (u32 *)max) + return (u32 *)base; + else + return (x + 1); +} + +static void ca_reg_read(void *reg, u64 base, u64 offset) +{ + u32 *val = (u32 *)reg; + + *val = readl(KSEG1_ATU_XLAT(base + offset)); +} + +static void ca_reg_write(void *reg, u64 base, u64 offset) +{ + u32 val = *(u32 *)reg; + + writel(val, KSEG1_ATU_XLAT(base + offset)); +} + +static int ca_mdio_write_rgmii(u32 addr, u32 offset, u16 data) +{ + /* up to 10000 cycles*/ + u32 loop_wait = __MDIO_ACCESS_TIMEOUT; + struct PER_MDIO_ADDR_t mdio_addr; + struct PER_MDIO_CTRL_t mdio_ctrl; + struct cortina_ni_priv *priv = dev_get_priv(curr_dev); + + memset(&mdio_addr, 0, sizeof(mdio_addr)); + mdio_addr.mdio_addr = addr; + mdio_addr.mdio_offset = offset; + mdio_addr.mdio_rd_wr = __MDIO_WR_FLAG; + ca_reg_write(&mdio_addr, (u64)priv->per_mdio_base_addr, + PER_MDIO_ADDR_OFFSET); + ca_reg_write(&data, (u64)priv->per_mdio_base_addr, + PER_MDIO_WRDATA_OFFSET); + + memset(&mdio_ctrl, 0, sizeof(mdio_ctrl)); + mdio_ctrl.mdiostart = 1; + ca_reg_write(&mdio_ctrl, (u64)priv->per_mdio_base_addr, + PER_MDIO_CTRL_OFFSET); + + debug("%s: phy_addr=%d, offset=%d, data=0x%x\n", + __func__, addr, offset, data); + + do { + ca_reg_read(&mdio_ctrl, (u64)priv->per_mdio_base_addr, + PER_MDIO_CTRL_OFFSET); + if (mdio_ctrl.mdiodone) { + ca_reg_write(&mdio_ctrl, (u64)priv->per_mdio_base_addr, + PER_MDIO_CTRL_OFFSET); + return 0; + } + } while (--loop_wait); + + printf("CA NI %s: PHY write timeout!!!\n", __func__); + return -ETIMEDOUT; +} + +int ca_mdio_write(u32 addr, u32 offset, u16 data) +{ + u32 reg_addr, reg_val; + struct NI_MDIO_OPER_T mdio_oper; + + /* support range: 1~31*/ + if (addr < CA_MDIO_ADDR_MIN || addr > CA_MDIO_ADDR_MAX) + return -EINVAL; + + /* the phy addr 5 is connect to RGMII */ + if (addr >= 5) + return ca_mdio_write_rgmii(addr, offset, data); + + memset(&mdio_oper, 0, sizeof(mdio_oper)); + mdio_oper.reg_off = offset; + mdio_oper.phy_addr = addr; + mdio_oper.reg_base = CA_NI_MDIO_REG_BASE; + reg_val = data; + memcpy(®_addr, &mdio_oper, sizeof(reg_addr)); + ca_reg_write(®_val, (u64)reg_addr, 0); + + return 0; +} + +static int ca_mdio_read_rgmii(u32 addr, u32 offset, u16 *data) +{ + u32 loop_wait = __MDIO_ACCESS_TIMEOUT; + struct PER_MDIO_ADDR_t mdio_addr; + struct PER_MDIO_CTRL_t mdio_ctrl; + struct PER_MDIO_RDDATA_t read_data; + struct cortina_ni_priv *priv = dev_get_priv(curr_dev); + + memset(&mdio_addr, 0, sizeof(mdio_addr)); + mdio_addr.mdio_addr = addr; + mdio_addr.mdio_offset = offset; + mdio_addr.mdio_rd_wr = __MDIO_RD_FLAG; + ca_reg_write(&mdio_addr, (u64)priv->per_mdio_base_addr, + PER_MDIO_ADDR_OFFSET); + + memset(&mdio_ctrl, 0, sizeof(mdio_ctrl)); + mdio_ctrl.mdiostart = 1; + ca_reg_write(&mdio_ctrl, (u64)priv->per_mdio_base_addr, + PER_MDIO_CTRL_OFFSET); + + do { + ca_reg_read(&mdio_ctrl, (u64)priv->per_mdio_base_addr, + PER_MDIO_CTRL_OFFSET); + if (mdio_ctrl.mdiodone) { + ca_reg_write(&mdio_ctrl, (u64)priv->per_mdio_base_addr, + PER_MDIO_CTRL_OFFSET); + ca_reg_read(&read_data, (u64)priv->per_mdio_base_addr, + PER_MDIO_RDDATA_OFFSET); + *data = read_data.mdio_rddata; + return 0; + } + } while (--loop_wait); + + printf("CA NI %s: TIMEOUT!!\n", __func__); + return -ETIMEDOUT; +} + +int ca_mdio_read(u32 addr, u32 offset, u16 *data) +{ + u32 reg_addr, reg_val; + struct NI_MDIO_OPER_T mdio_oper; + + if (!data) + return -EINVAL; + + /* support range: 1~31*/ + if (addr < CA_MDIO_ADDR_MIN || addr > CA_MDIO_ADDR_MAX) + return -EINVAL; + + /* the phy addr 5 is connect to RGMII */ + if (addr >= 5) + return ca_mdio_read_rgmii(addr, offset, data); + + memset(&mdio_oper, 0, sizeof(mdio_oper)); + mdio_oper.reg_off = offset; + mdio_oper.phy_addr = addr; + mdio_oper.reg_base = CA_NI_MDIO_REG_BASE; + reg_val = *data; + memcpy(®_addr, &mdio_oper, sizeof(reg_addr)); + ca_reg_read(®_val, (u64)reg_addr, 0); + *data = reg_val; + return 0; +} + +int ca_miiphy_read(const char *devname, u8 addr, u8 reg, u16 *value) +{ + return ca_mdio_read(addr, reg, value); +} + +int ca_miiphy_write(const char *devname, u8 addr, u8 reg, u16 value) +{ + return ca_mdio_write(addr, reg, value); +} + +static int cortina_mdio_read(struct mii_dev *bus, int addr, int devad, int reg) +{ + u16 data; + + ca_mdio_read(addr, reg, &data); + return data; +} + +static int cortina_mdio_write(struct mii_dev *bus, int addr, int devad, int reg, + u16 val) +{ + return ca_mdio_write(addr, reg, val); +} + +static void ca_ni_setup_mac_addr(void) +{ + u8 mac[6]; + struct NI_HV_GLB_MAC_ADDR_CFG0_t mac_addr_cfg0; + struct NI_HV_GLB_MAC_ADDR_CFG1_t mac_addr_cfg1; + struct NI_HV_PT_PORT_STATIC_CFG_t port_static_cfg; + struct NI_HV_XRAM_CPUXRAM_CFG_t cpuxram_cfg; + struct cortina_ni_priv *priv = dev_get_priv(curr_dev); + + /* parsing ethaddr and set to NI registers. */ + if (eth_env_get_enetaddr("ethaddr", mac)) { + /* The complete MAC address consists of + * {MAC_ADDR0_mac_addr0[0-3], MAC_ADDR1_mac_addr1[4], + * PT_PORT_STATIC_CFG_mac_addr6[5]}. + */ + mac_addr_cfg0.mac_addr0 = (mac[0] << 24) + (mac[1] << 16) + + (mac[2] << 8) + mac[3]; + ca_reg_write(&mac_addr_cfg0, (u64)priv->ni_hv_base_addr, + NI_HV_GLB_MAC_ADDR_CFG0_OFFSET); + + memset(&mac_addr_cfg1, 0, sizeof(mac_addr_cfg1)); + mac_addr_cfg1.mac_addr1 = mac[4]; + ca_reg_write(&mac_addr_cfg1, (u64)priv->ni_hv_base_addr, + NI_HV_GLB_MAC_ADDR_CFG1_OFFSET); + + ca_reg_read(&port_static_cfg, (u64)priv->ni_hv_base_addr, + NI_HV_PT_PORT_STATIC_CFG_OFFSET + + (APB0_NI_HV_PT_STRIDE * priv->active_port)); + + port_static_cfg.mac_addr6 = mac[5]; + ca_reg_write(&port_static_cfg, (u64)priv->ni_hv_base_addr, + NI_HV_PT_PORT_STATIC_CFG_OFFSET + + (APB0_NI_HV_PT_STRIDE * priv->active_port)); + + /* received only Broadcast and Address matched packets */ + ca_reg_read(&cpuxram_cfg, (u64)priv->ni_hv_base_addr, + NI_HV_XRAM_CPUXRAM_CFG_OFFSET); + cpuxram_cfg.xram_mgmt_promisc_mode = 0; + cpuxram_cfg.rx_0_cpu_pkt_dis = 0; + cpuxram_cfg.tx_0_cpu_pkt_dis = 0; + ca_reg_write(&cpuxram_cfg, (u64)priv->ni_hv_base_addr, + NI_HV_XRAM_CPUXRAM_CFG_OFFSET); + } else { + /* received all packets(promiscuous mode) */ + ca_reg_read(&cpuxram_cfg, (u64)priv->ni_hv_base_addr, + NI_HV_XRAM_CPUXRAM_CFG_OFFSET); + cpuxram_cfg.xram_mgmt_promisc_mode = 3; + cpuxram_cfg.rx_0_cpu_pkt_dis = 0; + cpuxram_cfg.tx_0_cpu_pkt_dis = 0; + ca_reg_write(&cpuxram_cfg, (u64)priv->ni_hv_base_addr, + NI_HV_XRAM_CPUXRAM_CFG_OFFSET); + } +} + +static void ca_ni_enable_tx_rx(void) +{ + struct NI_HV_PT_RXMAC_CFG_t rxmac_cfg; + struct NI_HV_PT_TXMAC_CFG_t txmac_cfg; + struct cortina_ni_priv *priv = dev_get_priv(curr_dev); + + /* Enable TX and RX functions */ + ca_reg_read(&rxmac_cfg, (u64)priv->ni_hv_base_addr, + NI_HV_PT_RXMAC_CFG_OFFSET + + (APB0_NI_HV_PT_STRIDE * priv->active_port)); + rxmac_cfg.rx_en = 1; + ca_reg_write(&rxmac_cfg, (u64)priv->ni_hv_base_addr, + NI_HV_PT_RXMAC_CFG_OFFSET + + (APB0_NI_HV_PT_STRIDE * priv->active_port)); + + ca_reg_read(&txmac_cfg, (u64)priv->ni_hv_base_addr, + NI_HV_PT_TXMAC_CFG_OFFSET + + (APB0_NI_HV_PT_STRIDE * priv->active_port)); + txmac_cfg.tx_en = 1; + ca_reg_write(&txmac_cfg, (u64)priv->ni_hv_base_addr, + NI_HV_PT_TXMAC_CFG_OFFSET + + (APB0_NI_HV_PT_STRIDE * priv->active_port)); +} + +#define AUTO_SCAN_TIMEOUT 3000 /* 3 seconds */ +static int ca_ni_auto_scan_active_port(struct cortina_ni_priv *priv) +{ + u8 i; + u16 data; + u32 start_time; + + start_time = get_timer(0); + while (get_timer(start_time) < AUTO_SCAN_TIMEOUT) { + for (i = 0; i < priv->valid_port_num; i++) { + if (!priv->port_map[i].phy_addr) + continue; + + ca_mdio_read(priv->port_map[i].phy_addr, 1, &data); + if (data & 0x04) { + priv->active_port = priv->port_map[i].port; + return 0; + } + } + } + + printf("CA NI %s: auto scan active_port timeout.\n", __func__); + return -1; +} + +static void ca_ni_led(int port, int status) +{ + char label[10]; + struct udevice *led_dev; + + if (IS_ENABLED(CONFIG_LED_CORTINA)) { + snprintf(label, sizeof(label), "led%d", port); + debug("%s: set port %d led %s.\n", + __func__, port, status ? "on" : "off"); + led_get_by_label(label, &led_dev); + led_set_state(led_dev, status); + } +} + +static void ca_ni_reset(void) +{ + int i; + struct NI_HV_GLB_INIT_DONE_t init_done; + struct NI_HV_GLB_INTF_RST_CONFIG_t intf_rst_config; + struct NI_HV_GLB_STATIC_CFG_t static_cfg; + struct GLOBAL_BLOCK_RESET_t glb_blk_reset; + struct cortina_ni_priv *priv = dev_get_priv(curr_dev); + + /* NI global resets */ + ca_reg_read(&glb_blk_reset, (u64)priv->glb_base_addr, + GLOBAL_BLOCK_RESET_OFFSET); + glb_blk_reset.reset_ni = 1; + ca_reg_write(&glb_blk_reset, (u64)priv->glb_base_addr, + GLOBAL_BLOCK_RESET_OFFSET); + /* Remove resets */ + glb_blk_reset.reset_ni = 0; + ca_reg_write(&glb_blk_reset, (u64)priv->glb_base_addr, + GLOBAL_BLOCK_RESET_OFFSET); + + /* check the ready bit of NI module */ + for (i = 0; i < NI_READ_POLL_COUNT; i++) { + ca_reg_read(&init_done, (u64)priv->ni_hv_base_addr, + NI_HV_GLB_INIT_DONE_OFFSET); + if (init_done.ni_init_done) + break; + } + if (i == NI_READ_POLL_COUNT) { + printf("CA NI %s: NI init done not ready, init_done=0x%x!!!\n", + __func__, init_done.ni_init_done); + } + + ca_reg_read(&intf_rst_config, (u64)priv->ni_hv_base_addr, + NI_HV_GLB_INTF_RST_CONFIG_OFFSET); + switch (priv->active_port) { + case NI_PORT_0: + intf_rst_config.intf_rst_p0 = 0; + intf_rst_config.mac_rx_rst_p0 = 0; + intf_rst_config.mac_tx_rst_p0 = 0; + break; + case NI_PORT_1: + intf_rst_config.intf_rst_p1 = 0; + intf_rst_config.mac_rx_rst_p1 = 0; + intf_rst_config.mac_tx_rst_p1 = 0; + break; + case NI_PORT_2: + intf_rst_config.intf_rst_p2 = 0; + intf_rst_config.mac_rx_rst_p2 = 0; + intf_rst_config.mac_tx_rst_p2 = 0; + break; + case NI_PORT_3: + intf_rst_config.intf_rst_p3 = 0; + intf_rst_config.mac_tx_rst_p3 = 0; + intf_rst_config.mac_rx_rst_p3 = 0; + break; + case NI_PORT_4: + intf_rst_config.intf_rst_p4 = 0; + intf_rst_config.mac_tx_rst_p4 = 0; + intf_rst_config.mac_rx_rst_p4 = 0; + break; + } + + ca_reg_write(&intf_rst_config, (u64)priv->ni_hv_base_addr, + NI_HV_GLB_INTF_RST_CONFIG_OFFSET); + + /* Only one GMAC can connect to CPU */ + ca_reg_read(&static_cfg, (u64)priv->ni_hv_base_addr, + NI_HV_GLB_STATIC_CFG_OFFSET); + static_cfg.port_to_cpu = priv->active_port; + static_cfg.txmib_mode = 1; + static_cfg.rxmib_mode = 1; + + ca_reg_write(&static_cfg, (u64)priv->ni_hv_base_addr, + NI_HV_GLB_STATIC_CFG_OFFSET); +} + +static void ca_internal_gphy_cal(struct cortina_ni_priv *priv) +{ + int i, port, num; + u32 reg_off, value; + + num = priv->gphy_num; + for (port = 0; port < 4; port++) { + for (i = 0; i < num; i++) { + reg_off = priv->gphy_values[i].reg_off + (port * 0x80); + value = priv->gphy_values[i].value; + ca_reg_write(&value, reg_off, 0); + mdelay(50); + } + } +} + +static int ca_mdio_register(struct udevice *dev) +{ + int ret; + struct cortina_ni_priv *priv = dev_get_priv(dev); + struct mii_dev *mdio_bus = mdio_alloc(); + + if (!mdio_bus) + return -ENOMEM; + + mdio_bus->read = cortina_mdio_read; + mdio_bus->write = cortina_mdio_write; + snprintf(mdio_bus->name, sizeof(mdio_bus->name), dev->name); + + mdio_bus->priv = (void *)priv; + + ret = mdio_register(mdio_bus); + if (ret) + return ret; + + priv->mdio_bus = mdio_bus; + return 0; +} + +static void ca_rgmii_init(struct cortina_ni_priv *priv) +{ + struct GLOBAL_GLOBAL_CONFIG_t glb_config; + struct GLOBAL_IO_DRIVE_CONTROL_t io_drive_control; + + /* Generating 25Mhz reference clock for switch */ + ca_reg_read(&glb_config, (u64)priv->glb_base_addr, + GLOBAL_GLOBAL_CONFIG_OFFSET); + glb_config.refclk_sel = 0x01; + glb_config.ext_reset = 0x01; + ca_reg_write(&glb_config, (u64)priv->glb_base_addr, + GLOBAL_GLOBAL_CONFIG_OFFSET); + + mdelay(20); + + /* Do external reset */ + ca_reg_read(&glb_config, (u64)priv->glb_base_addr, + GLOBAL_GLOBAL_CONFIG_OFFSET); + glb_config.ext_reset = 0x0; + ca_reg_write(&glb_config, (u64)priv->glb_base_addr, + GLOBAL_GLOBAL_CONFIG_OFFSET); + + ca_reg_read(&io_drive_control, (u64)priv->glb_base_addr, + GLOBAL_IO_DRIVE_CONTROL_OFFSET); + io_drive_control.gmac_mode = 2; + io_drive_control.gmac_dn = 1; + io_drive_control.gmac_dp = 1; + ca_reg_write(&io_drive_control, (u64)priv->glb_base_addr, + GLOBAL_IO_DRIVE_CONTROL_OFFSET); +} + +static int ca_phy_probe(struct udevice *dev) +{ + int auto_scan_active_port = 0, tmp_port; + char *buf; + struct cortina_ni_priv *priv = dev_get_priv(dev); + struct phy_device *int_phydev, *ext_phydev; + + /* Initialize internal phy device */ + int_phydev = phy_connect(priv->mdio_bus, + priv->port_map[NI_PORT_3].phy_addr, + dev, priv->phy_interface); + if (int_phydev) { + int_phydev->supported &= PHY_GBIT_FEATURES; + int_phydev->advertising = int_phydev->supported; + phy_config(int_phydev); + } else { + printf("CA NI %s: There is no internal phy device\n", __func__); + } + + /* Initialize external phy device */ + ext_phydev = phy_connect(priv->mdio_bus, + priv->port_map[NI_PORT_4].phy_addr, + dev, priv->phy_interface); + if (ext_phydev) { + ext_phydev->supported &= PHY_GBIT_FEATURES; + ext_phydev->advertising = int_phydev->supported; + phy_config(ext_phydev); + } else { + printf("CA NI %s: There is no external phy device\n", __func__); + } + + /* auto scan the first link up port as active_port */ + buf = env_get("auto_scan_active_port"); + if (buf != 0) { + auto_scan_active_port = simple_strtoul(buf, NULL, 0); + printf("CA NI %s: auto_scan_active_port=%d\n", __func__, + auto_scan_active_port); + } + + if (auto_scan_active_port) { + ca_ni_auto_scan_active_port(priv); + } else { + buf = env_get("active_port"); + if (buf != 0) { + tmp_port = simple_strtoul(buf, NULL, 0); + if (tmp_port < 0 && + !(priv->valid_port_map && BIT(tmp_port))) { + printf("CA NI ERROR: not support this port."); + free(dev); + free(priv); + return 1; + } + + priv->active_port = tmp_port; + } + } + + printf("CA NI %s: active_port=%d\n", __func__, priv->active_port); + if (priv->active_port == NI_PORT_4) + priv->phydev = ext_phydev; + else + priv->phydev = int_phydev; + + return 0; +} + +static int cortina_eth_start(struct udevice *dev) +{ + int ret; + struct NI_HV_XRAM_CPUXRAM_ADRCFG_RX_t cpuxram_adrcfg_rx; + struct NI_HV_XRAM_CPUXRAM_ADRCFG_TX_0_t cpuxram_adrcfg_tx; + struct NI_HV_XRAM_CPUXRAM_CFG_t cpuxram_cfg; + struct NI_HV_PT_PORT_STATIC_CFG_t port_static_cfg; + struct NI_HV_PT_PORT_GLB_CFG_t port_glb_cfg; + struct cortina_ni_priv *priv = dev_get_priv(dev); + struct phy_device *phydev = priv->phydev; + + ret = phy_startup(priv->phydev); + if (ret) { + ca_ni_led(priv->active_port, CA_LED_OFF); + printf("CA NI Could not initialize PHY %s, active_port=%d\n", + priv->phydev->dev->name, priv->active_port); + return ret; + } + + if (!priv->phydev->link) { + printf("CA NI %s: link down.\n", priv->phydev->dev->name); + return 0; + } + + ca_ni_led(priv->active_port, CA_LED_ON); + printf("CA NI PHY ID 0x%08X %dMbps %s duplex\n", + phydev->phy_id, phydev->speed, + phydev->duplex == DUPLEX_HALF ? "half" : "full"); + + /* RX XRAM ADDRESS CONFIG (start and end address) */ + memset(&cpuxram_adrcfg_rx, 0, sizeof(cpuxram_adrcfg_rx)); + cpuxram_adrcfg_rx.rx_top_addr = RX_TOP_ADDR; + cpuxram_adrcfg_rx.rx_base_addr = RX_BASE_ADDR; + ca_reg_write(&cpuxram_adrcfg_rx, (u64)priv->ni_hv_base_addr, + NI_HV_XRAM_CPUXRAM_ADRCFG_RX_OFFSET); + + /* TX XRAM ADDRESS CONFIG (start and end address) */ + memset(&cpuxram_adrcfg_tx, 0, sizeof(cpuxram_adrcfg_tx)); + cpuxram_adrcfg_tx.tx_top_addr = TX_TOP_ADDR; + cpuxram_adrcfg_tx.tx_base_addr = TX_BASE_ADDR; + ca_reg_write(&cpuxram_adrcfg_tx, (u64)priv->ni_hv_base_addr, + NI_HV_XRAM_CPUXRAM_ADRCFG_TX_0_OFFSET); + + /* + * Configuration for Management Ethernet Interface: + * - RGMII 1000 mode or RGMII 100 mode + * - MAC mode + */ + ca_reg_read(&port_static_cfg, (u64)priv->ni_hv_base_addr, + NI_HV_PT_PORT_STATIC_CFG_OFFSET + + (APB0_NI_HV_PT_STRIDE * priv->active_port)); + if (phydev->speed == SPEED_1000) { + /* port 4 connects to RGMII PHY */ + if (phydev->addr == 5) + port_static_cfg.int_cfg = GE_MAC_INTF_RGMII_1000; + else + port_static_cfg.int_cfg = GE_MAC_INTF_GMII; + } else { + /* port 4 connects to RGMII PHY */ + if (phydev->addr == 5) + port_static_cfg.int_cfg = GE_MAC_INTF_RGMII_100; + else + port_static_cfg.int_cfg = GE_MAC_INTF_MII; + } + + ca_reg_write(&port_static_cfg, (u64)priv->ni_hv_base_addr, + NI_HV_PT_PORT_STATIC_CFG_OFFSET + + (APB0_NI_HV_PT_STRIDE * priv->active_port)); + + ca_reg_read(&port_glb_cfg, (u64)priv->ni_hv_base_addr, + NI_HV_PT_PORT_GLB_CFG_OFFSET + + (APB0_NI_HV_PT_STRIDE * priv->active_port)); + port_glb_cfg.speed = phydev->speed == SPEED_10 ? 1 : 0; + port_glb_cfg.duplex = phydev->duplex == DUPLEX_HALF ? 1 : 0; + ca_reg_write(&port_glb_cfg, (u64)priv->ni_hv_base_addr, + NI_HV_PT_PORT_GLB_CFG_OFFSET + + (APB0_NI_HV_PT_STRIDE * priv->active_port)); + + /* Need to toggle the tx and rx cpu_pkt_dis bit */ + /* after changing Address config register. */ + ca_reg_read(&cpuxram_cfg, (u64)priv->ni_hv_base_addr, + NI_HV_XRAM_CPUXRAM_CFG_OFFSET); + cpuxram_cfg.rx_0_cpu_pkt_dis = 1; + cpuxram_cfg.tx_0_cpu_pkt_dis = 1; + ca_reg_write(&cpuxram_cfg, (u64)priv->ni_hv_base_addr, + NI_HV_XRAM_CPUXRAM_CFG_OFFSET); + + ca_reg_read(&cpuxram_cfg, (u64)priv->ni_hv_base_addr, + NI_HV_XRAM_CPUXRAM_CFG_OFFSET); + cpuxram_cfg.rx_0_cpu_pkt_dis = 0; + cpuxram_cfg.tx_0_cpu_pkt_dis = 0; + ca_reg_write(&cpuxram_cfg, (u64)priv->ni_hv_base_addr, + NI_HV_XRAM_CPUXRAM_CFG_OFFSET); + + ca_ni_enable_tx_rx(); + + return 0; +} + +/********************************************* + * Packet receive routine from Management FE + * Expects a previously allocated buffer and + * fills the length + * Retruns 0 on success -1 on failure + *******************************************/ +static int cortina_eth_recv(struct udevice *dev, int flags, uchar **packetp) +{ + u8 *ptr; + u32 next_link, pktlen = 0; + u32 sw_rx_rd_ptr, hw_rx_wr_ptr, *rx_xram_ptr, *data_ptr; + int loop, index = 0, blk_num; + struct cortina_ni_priv *priv = dev_get_priv(dev); + struct NI_HEADER_X_T header_x; + struct NI_PACKET_STATUS packet_status; + struct NI_HV_XRAM_CPUXRAM_CPU_STA_RX_0_t cpuxram_cpu_sta_rx; + struct NI_HV_XRAM_CPUXRAM_CPU_CFG_RX_0_t cpuxram_cpu_cfg_rx; + + /* get the hw write pointer */ + memset(&cpuxram_cpu_sta_rx, 0, sizeof(cpuxram_cpu_sta_rx)); + ca_reg_read(&cpuxram_cpu_sta_rx, (u64)priv->ni_hv_base_addr, + NI_HV_XRAM_CPUXRAM_CPU_STA_RX_0_OFFSET); + hw_rx_wr_ptr = cpuxram_cpu_sta_rx.pkt_wr_ptr; + + /* get the sw read pointer */ + memset(&cpuxram_cpu_cfg_rx, 0, sizeof(cpuxram_cpu_cfg_rx)); + ca_reg_read(&cpuxram_cpu_cfg_rx, (u64)priv->ni_hv_base_addr, + NI_HV_XRAM_CPUXRAM_CPU_CFG_RX_0_OFFSET); + sw_rx_rd_ptr = cpuxram_cpu_cfg_rx.pkt_rd_ptr; + + debug("%s: NI_HV_XRAM_CPUXRAM_CPU_STA_RX_0 = 0x%p, ", __func__, + priv->ni_hv_base_addr + NI_HV_XRAM_CPUXRAM_CPU_STA_RX_0_OFFSET); + debug("NI_HV_XRAM_CPUXRAM_CPU_CFG_RX_0 = 0x%p\n", + priv->ni_hv_base_addr + NI_HV_XRAM_CPUXRAM_CPU_CFG_RX_0_OFFSET); + debug("%s : RX hw_wr_ptr = %d, sw_rd_ptr = %d\n", + __func__, hw_rx_wr_ptr, sw_rx_rd_ptr); + + while (sw_rx_rd_ptr != hw_rx_wr_ptr) { + /* Point to the absolute memory address of XRAM + * where read pointer is + */ + rx_xram_ptr = (u32 *) + ((unsigned long)priv->ni_xram_base + + sw_rx_rd_ptr * 8); + + /* Wrap around if required */ + if (rx_xram_ptr >= (u32 *)(unsigned long)priv->rx_xram_end_adr) + rx_xram_ptr = (u32 *) + (unsigned long)priv->rx_xram_base_adr; + + /* Checking header XR. Do not update the read pointer yet */ + /* skip unused 32-bit in Header XR */ + rx_xram_ptr = ca_rdwrptr_adv_one(rx_xram_ptr, + priv->rx_xram_base_adr, + priv->rx_xram_end_adr); + + memcpy(&header_x, rx_xram_ptr, sizeof(header_x)); + next_link = header_x.next_link; + /* Header XR [31:0] */ + + if (*rx_xram_ptr == 0xffffffff) + printf("CA NI %s: XRAM Error !\n", __func__); + + debug("%s : RX next link 0x%x\n", __func__, next_link); + debug("%s : bytes_valid %x\n", __func__, header_x.bytes_valid); + + if (header_x.ownership == 0) { + /* point to Packet status [31:0] */ + rx_xram_ptr = ca_rdwrptr_adv_one(rx_xram_ptr, + priv->rx_xram_base_adr, + priv->rx_xram_end_adr); + + memcpy(&packet_status, rx_xram_ptr, + sizeof(rx_xram_ptr)); + if (packet_status.valid == 0) { + debug("%s: Invalid Packet !!, ", __func__); + debug("next_link=%d\n", next_link); + + /* Update the software read pointer */ + ca_reg_write(&next_link, + (u64)priv->ni_hv_base_addr, + NI_HV_XRAM_CPUXRAM_CPU_CFG_RX_0_OFFSET); + return 0; + } + + if (packet_status.drop || + packet_status.runt || + packet_status.oversize || + packet_status.jabber || + packet_status.crc_error || + packet_status.jumbo) { + debug("%s: Error Packet!!, ", __func__); + debug("next_link=%d\n", next_link); + + /* Update the software read pointer */ + ca_reg_write(&next_link, + (u64)priv->ni_hv_base_addr, + NI_HV_XRAM_CPUXRAM_CPU_CFG_RX_0_OFFSET); + return 0; + } + + /* check whether packet size is larger than 1514 */ + if (packet_status.packet_size > 1518) { + debug("%s: Error Packet !! Packet size=%d, ", + __func__, packet_status.packet_size); + debug("larger than 1518, next_link=%d\n", + next_link); + + /* Update the software read pointer */ + ca_reg_write(&next_link, + (u64)priv->ni_hv_base_addr, + NI_HV_XRAM_CPUXRAM_CPU_CFG_RX_0_OFFSET); + return 0; + } + + rx_xram_ptr = ca_rdwrptr_adv_one(rx_xram_ptr, + priv->rx_xram_base_adr, + priv->rx_xram_end_adr); + + pktlen = packet_status.packet_size; + + debug("%s : rx packet length = %d\n", + __func__, packet_status.packet_size); + + rx_xram_ptr = ca_rdwrptr_adv_one(rx_xram_ptr, + priv->rx_xram_base_adr, + priv->rx_xram_end_adr); + + data_ptr = (u32 *)net_rx_packets[index]; + + /* Read out the packet */ + /* Data is in little endian form in the XRAM */ + + /* Send the packet to upper layer */ + + debug("%s: packet data[]=", __func__); + + for (loop = 0; loop <= pktlen / 4; loop++) { + ptr = (u8 *)rx_xram_ptr; + if (loop < 10) + debug("[0x%x]-[0x%x]-[0x%x]-[0x%x]", + ptr[0], ptr[1], ptr[2], ptr[3]); + *data_ptr++ = *rx_xram_ptr++; + /* Wrap around if required */ + if (rx_xram_ptr >= (u32 *) + (unsigned long)priv->rx_xram_end_adr) { + rx_xram_ptr = (u32 *)(unsigned long) + (priv->rx_xram_base_adr); + } + } + + debug("\n"); + net_process_received_packet(net_rx_packets[index], + pktlen); + if (++index >= PKTBUFSRX) + index = 0; + blk_num = net_rx_packets[index][0x2c] * 255 + + net_rx_packets[index][0x2d]; + debug("%s: tftp block number=%d\n", __func__, blk_num); + + /* Update the software read pointer */ + ca_reg_write(&next_link, + (u64)priv->ni_hv_base_addr, + NI_HV_XRAM_CPUXRAM_CPU_CFG_RX_0_OFFSET); + } + + /* get the hw write pointer */ + ca_reg_read(&cpuxram_cpu_sta_rx, (u64)priv->ni_hv_base_addr, + NI_HV_XRAM_CPUXRAM_CPU_STA_RX_0_OFFSET); + hw_rx_wr_ptr = cpuxram_cpu_sta_rx.pkt_wr_ptr; + + /* get the sw read pointer */ + ca_reg_read(&sw_rx_rd_ptr, (u64)priv->ni_hv_base_addr, + NI_HV_XRAM_CPUXRAM_CPU_CFG_RX_0_OFFSET); + } + return 0; +} + +static int cortina_eth_send(struct udevice *dev, void *packet, int length) +{ + u32 hw_tx_rd_ptr = 0, sw_tx_wr_ptr = 0; + u32 loop, new_pkt_len, ca_crc32; + u32 *tx_xram_ptr, *data_ptr; + u16 next_link = 0; + u8 *ptr, *pkt_buf_ptr, valid_bytes = 0; + int pad = 0; + static u8 pkt_buf[2048]; + struct NI_HEADER_X_T hdr_xt; + struct NI_HV_XRAM_CPUXRAM_CPU_CFG_TX_0_t cpuxram_cpu_cfg_tx; + struct cortina_ni_priv *priv = dev_get_priv(dev); + + if (!packet || length > 2032) + return -1; + + /* Get the hardware read pointer */ + ca_reg_read(&hw_tx_rd_ptr, (u64)priv->ni_hv_base_addr, + NI_HV_XRAM_CPUXRAM_CPU_STAT_TX_0_OFFSET); + + /* Get the software write pointer */ + ca_reg_read(&sw_tx_wr_ptr, (u64)priv->ni_hv_base_addr, + NI_HV_XRAM_CPUXRAM_CPU_CFG_TX_0_OFFSET); + + debug("%s: NI_HV_XRAM_CPUXRAM_CPU_STAT_TX_0=0x%p, ", + __func__, + KSEG1_ATU_XLAT(priv->ni_hv_base_addr + + NI_HV_XRAM_CPUXRAM_CPU_STAT_TX_0_OFFSET)); + debug("NI_HV_XRAM_CPUXRAM_CPU_CFG_TX_0=0x%p\n", + KSEG1_ATU_XLAT(priv->ni_hv_base_addr + + NI_HV_XRAM_CPUXRAM_CPU_CFG_TX_0_OFFSET)); + debug("%s : hw_tx_rd_ptr = %d\n", __func__, hw_tx_rd_ptr); + debug("%s : sw_tx_wr_ptr = %d\n", __func__, sw_tx_wr_ptr); + + if (hw_tx_rd_ptr != sw_tx_wr_ptr) { + printf("CA NI %s: Tx FIFO is not available!\n", __func__); + return 1; + } + + /* a workaround on 2015/10/01 + * the packet size+CRC should be 8-byte alignment + */ + if (((length + 4) % 8) != 0) + length += (8 - ((length + 4) % 8)); + + memset(pkt_buf, 0x00, sizeof(pkt_buf)); + + /* add 8-byte header_A at the beginning of packet */ + memcpy(&pkt_buf[HEADER_A_SIZE], (const void *)packet, length); + + pad = 64 - (length + 4); /* if packet length < 60 */ + pad = (pad < 0) ? 0 : pad; + + debug("%s: length=%d, pad=%d\n", __func__, length, pad); + + new_pkt_len = length + pad; /* new packet length */ + + pkt_buf_ptr = (u8 *)pkt_buf; + + /* Calculate the CRC32, skip 8-byte header_A */ + ca_crc32 = crc32(0, (u8 *)(pkt_buf_ptr + HEADER_A_SIZE), new_pkt_len); + + debug("%s: crc32 is 0x%x\n", __func__, ca_crc32); + debug("%s: ~crc32 is 0x%x\n", __func__, ~ca_crc32); + debug("%s: pkt len %d\n", __func__, new_pkt_len); + /* should add 8-byte header_! */ + /* CRC will re-calculated by hardware */ + memcpy((pkt_buf_ptr + new_pkt_len + HEADER_A_SIZE), + (u8 *)(&ca_crc32), sizeof(ca_crc32)); + new_pkt_len = new_pkt_len + 4; /* add CRC */ + + valid_bytes = new_pkt_len % 8; + valid_bytes = valid_bytes ? valid_bytes : 0; + debug("%s: valid_bytes %d\n", __func__, valid_bytes); + + /* should add 8-byte headerA */ + next_link = sw_tx_wr_ptr + + (new_pkt_len + 7 + HEADER_A_SIZE) / 8; /* for headr XT */ + /* add header */ + next_link = next_link + 1; + /* Wrap around if required */ + if (next_link > priv->tx_xram_end) { + next_link = priv->tx_xram_start + + (next_link - (priv->tx_xram_end + 1)); + } + + debug("%s: TX next_link %x\n", __func__, next_link); + memset(&hdr_xt, 0, sizeof(hdr_xt)); + hdr_xt.ownership = 1; + hdr_xt.bytes_valid = valid_bytes; + hdr_xt.next_link = next_link; + + tx_xram_ptr = (u32 *)((unsigned long)priv->ni_xram_base + + sw_tx_wr_ptr * 8); + + /* Wrap around if required */ + if (tx_xram_ptr >= (u32 *)(unsigned long)priv->tx_xram_end_adr) + tx_xram_ptr = (u32 *)(unsigned long)priv->tx_xram_base_adr; + + tx_xram_ptr = ca_rdwrptr_adv_one(tx_xram_ptr, + priv->tx_xram_base_adr, + priv->tx_xram_end_adr); + + memcpy(tx_xram_ptr, &hdr_xt, sizeof(*tx_xram_ptr)); + + tx_xram_ptr = ca_rdwrptr_adv_one(tx_xram_ptr, + priv->tx_xram_base_adr, + priv->tx_xram_end_adr); + + /* Now to copy the data. The first byte on the line goes first */ + data_ptr = (u32 *)pkt_buf_ptr; + debug("%s: packet data[]=", __func__); + + /* copy header_A to XRAM */ + for (loop = 0; loop <= (new_pkt_len + HEADER_A_SIZE) / 4; loop++) { + ptr = (u8 *)data_ptr; + if ((loop % 4) == 0) + debug("\n"); + debug("[0x%x]-[0x%x]-[0x%x]-[0x%x]-", + ptr[0], ptr[1], ptr[2], ptr[3]); + + *tx_xram_ptr = *data_ptr++; + tx_xram_ptr = ca_rdwrptr_adv_one(tx_xram_ptr, + priv->tx_xram_base_adr, + priv->tx_xram_end_adr); + } + debug("\n"); + + /* Publish the software write pointer */ + cpuxram_cpu_cfg_tx.pkt_wr_ptr = next_link; + ca_reg_write(&cpuxram_cpu_cfg_tx, + (u64)priv->ni_hv_base_addr, + NI_HV_XRAM_CPUXRAM_CPU_CFG_TX_0_OFFSET); + + return 0; +} + +static void cortina_eth_stop(struct udevice *netdev) +{ + /* Nothing to do for now. */ +} + +static int cortina_eth_probe(struct udevice *dev) +{ + int ret, reg_value; + struct cortina_ni_priv *priv; + + priv = dev_get_priv(dev); + priv->rx_xram_base_adr = priv->ni_xram_base + (RX_BASE_ADDR * 8); + priv->rx_xram_end_adr = priv->ni_xram_base + ((RX_TOP_ADDR + 1) * 8); + priv->rx_xram_start = RX_BASE_ADDR; + priv->rx_xram_end = RX_TOP_ADDR; + priv->tx_xram_base_adr = priv->ni_xram_base + (TX_BASE_ADDR * 8); + priv->tx_xram_end_adr = priv->ni_xram_base + ((TX_TOP_ADDR + 1) * 8); + priv->tx_xram_start = TX_BASE_ADDR; + priv->tx_xram_end = TX_TOP_ADDR; + + curr_dev = dev; + debug("%s: rx_base_addr:%x\t rx_top_addr %x\n", + __func__, priv->rx_xram_start, priv->rx_xram_end); + debug("%s: tx_base_addr:%x\t tx_top_addr %x\n", + __func__, priv->tx_xram_start, priv->tx_xram_end); + debug("%s: rx physical start address = %x end address = %x\n", + __func__, priv->rx_xram_base_adr, priv->rx_xram_end_adr); + debug("%s: tx physical start address = %x end address = %x\n", + __func__, priv->tx_xram_base_adr, priv->tx_xram_end_adr); + + /* MDIO register */ + ret = ca_mdio_register(dev); + if (ret) + return ret; + + /* set MDIO pre-scale value */ + ca_reg_read(®_value, (u64)priv->per_mdio_base_addr, + PER_MDIO_CFG_OFFSET); + reg_value = reg_value | 0x00280000; + ca_reg_write(®_value, (u64)priv->per_mdio_base_addr, + PER_MDIO_CFG_OFFSET); + + ca_phy_probe(dev); + priv->phydev->addr = priv->port_map[priv->active_port].phy_addr; + + ca_ni_led(priv->active_port, CA_LED_ON); + + ca_ni_reset(); + + printf("CA NI %s: active_port=%d, phy_addr=%d\n", + __func__, priv->active_port, priv->phydev->addr); + printf("CA NI %s: phy_id=0x%x, phy_id & PHY_ID_MASK=0x%x\n", __func__, + priv->phydev->phy_id, priv->phydev->phy_id & 0xFFFFFFF0); + + /* parsing ethaddr and set to NI registers. */ + ca_ni_setup_mac_addr(); + +#ifdef MIIPHY_REGISTER + /* the phy_read and phy_write + * should meet the proto type of miiphy_register + */ + miiphy_register(dev->name, ca_miiphy_read, ca_miiphy_write); +#endif + + if (priv->init_rgmii) { + /* hardware settings for RGMII port */ + ca_rgmii_init(priv); + } + + if (priv->gphy_num > 0) { + /* do internal gphy calibration */ + ca_internal_gphy_cal(priv); + } + return 0; +} + +static int ca_ni_of_to_plat(struct udevice *dev) +{ + int i, ret; + struct cortina_ni_priv *priv = dev_get_priv(dev); + + memset(priv, 0, sizeof(struct cortina_ni_priv)); + priv->glb_base_addr = dev_remap_addr_index(dev, 0); + if (!priv->glb_base_addr) + return -ENOENT; + printf("CA NI %s: priv->glb_base_addr for index 0 is 0x%p\n", + __func__, priv->glb_base_addr); + + priv->per_mdio_base_addr = dev_remap_addr_index(dev, 1); + if (!priv->per_mdio_base_addr) + return -ENOENT; + printf("CA NI %s: priv->per_mdio_base_addr for index 1 is 0x%p\n", + __func__, priv->per_mdio_base_addr); + + priv->ni_hv_base_addr = dev_remap_addr_index(dev, 2); + if (!priv->ni_hv_base_addr) + return -ENOENT; + printf("CA NI %s: priv->ni_hv_base_addr for index 2 is 0x%p\n", + __func__, priv->ni_hv_base_addr); + + priv->valid_port_map = dev_read_u32_default(dev, "valid-port-map", 1); + priv->valid_port_num = dev_read_u32_default(dev, "valid-port-num", 1); + + for (i = 0; i < priv->valid_port_num; i++) { + ret = dev_read_u32_index(dev, "valid-ports", i * 2, + &priv->port_map[i].phy_addr); + ret = dev_read_u32_index(dev, "valid-ports", (i * 2) + 1, + &priv->port_map[i].port); + } + + priv->gphy_num = dev_read_u32_default(dev, "inter-gphy-num", 1); + for (i = 0; i < priv->gphy_num; i++) { + ret = dev_read_u32_index(dev, "inter-gphy-val", i * 2, + &priv->gphy_values[i].reg_off); + ret = dev_read_u32_index(dev, "inter-gphy-val", (i * 2) + 1, + &priv->gphy_values[i].value); + } + + priv->active_port = dev_read_u32_default(dev, "def-active-port", 1); + priv->init_rgmii = dev_read_u32_default(dev, "init-rgmii", 1); + priv->ni_xram_base = dev_read_u32_default(dev, "ni-xram-base", 1); + return 0; +} + +static const struct eth_ops cortina_eth_ops = { + .start = cortina_eth_start, + .send = cortina_eth_send, + .recv = cortina_eth_recv, + .stop = cortina_eth_stop, +}; + +static const struct udevice_id cortina_eth_ids[] = { + { .compatible = "eth_cortina" }, + { } +}; + +U_BOOT_DRIVER(eth_cortina) = { + .name = "eth_cortina", + .id = UCLASS_ETH, + .of_match = cortina_eth_ids, + .probe = cortina_eth_probe, + .ops = &cortina_eth_ops, + .priv_auto = sizeof(struct cortina_ni_priv), + .plat_auto = sizeof(struct eth_pdata), + .of_to_plat = ca_ni_of_to_plat, +}; diff --git a/drivers/net/cortina_ni.h b/drivers/net/cortina_ni.h new file mode 100644 index 00000000000..0ced4687cd0 --- /dev/null +++ b/drivers/net/cortina_ni.h @@ -0,0 +1,401 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +/* + * Copyright (C) 2020 Cortina Access Inc. + * Author: Aaron Tseng + * + * Ethernet MAC Driver for all supported CAxxxx SoCs + */ + +#ifndef __CORTINA_NI_H +#define __CORTINA_NI_H + +#include +#include +#include + +#define GE_MAC_INTF_GMII 0x0 +#define GE_MAC_INTF_MII 0x1 +#define GE_MAC_INTF_RGMII_1000 0x2 +#define GE_MAC_INTF_RGMII_100 0x3 + +/* Defines the base and top address in CPU XRA + * for packets to cpu instance 0 + * 0x300 * 8-byte = 6K-byte + */ +#define RX_TOP_ADDR 0x02FF +#define RX_BASE_ADDR 0x0000 + +/* Defines the base and top address in CPU XRAM + * for packets from cpu instance 0. + * 0x100 * 8-byte = 2K-byte + */ +#define TX_TOP_ADDR 0x03FF +#define TX_BASE_ADDR 0x0300 + +struct port_map_s { + u32 phy_addr; + u32 port; +}; + +struct gphy_cal_s { + u32 reg_off; + u32 value; +}; + +#if !defined(__ASSEMBLER__) && !defined(__ASSEMBLY__) +struct cortina_ni_priv { + u32 ni_xram_base; + u32 rx_xram_base_adr; + u32 rx_xram_end_adr; + u16 rx_xram_start; + u16 rx_xram_end; + u32 tx_xram_base_adr; + u32 tx_xram_end_adr; + u16 tx_xram_start; + u16 tx_xram_end; + u32 valid_port_map; + u32 valid_port_num; + u32 init_rgmii; + u32 gphy_num; + struct port_map_s port_map[5]; + struct gphy_cal_s gphy_values[10]; + void __iomem *glb_base_addr; + void __iomem *per_mdio_base_addr; + void __iomem *ni_hv_base_addr; + + struct mii_dev *mdio_bus; + struct phy_device *phydev; + int phy_interface; + int active_port; +}; + +struct NI_HEADER_X_T { + u32 next_link : 10; /* bits 9: 0 */ + u32 bytes_valid : 4; /* bits 13:10 */ + u32 reserved : 16; /* bits 29:14 */ + u32 hdr_a : 1; /* bits 30:30 */ + u32 ownership : 1; /* bits 31:31 */ +}; + +struct NI_PACKET_STATUS { + u32 packet_size : 14; /* bits 13:0 */ + u32 byte_valid : 4; /* bits 17:14 */ + u32 pfc : 1; /* bits 18:18 */ + u32 valid : 1; /* bits 19:19 */ + u32 drop : 1; /* bits 20:20 */ + u32 runt : 1; /* bits 21:21 */ + u32 oversize : 1; /* bits 22:22 */ + u32 jumbo : 1; /* bits 23:23 */ + u32 link_status : 1; /* bits 24:24 */ + u32 jabber : 1; /* bits 25:25 */ + u32 crc_error : 1; /* bits 26:26 */ + u32 pause : 1; /* bits 27:27 */ + u32 oam : 1; /* bits 28:28 */ + u32 unknown_opcode : 1; /* bits 29:29 */ + u32 multicast : 1; /* bits 30:30 */ + u32 broadcast : 1; /* bits 31:31 */ +}; + +struct NI_MDIO_OPER_T { + u32 reserved : 2; /* bits 1:0 */ + u32 reg_off : 5; /* bits 6:2 */ + u32 phy_addr : 5; /* bits 11:7 */ + u32 reg_base : 20; /* bits 31:12 */ +}; + +#define __MDIO_WR_FLAG (0) +#define __MDIO_RD_FLAG (1) +#define __MDIO_ACCESS_TIMEOUT (1000000) +#define CA_MDIO_ADDR_MIN (1) +#define CA_MDIO_ADDR_MAX (31) + +#endif /* !__ASSEMBLER__ */ + +/* HW REG */ +struct NI_HV_GLB_MAC_ADDR_CFG0_t { + u32 mac_addr0 : 32; /* bits 31:0 */ +}; + +struct NI_HV_GLB_MAC_ADDR_CFG1_t { + u32 mac_addr1 : 8; /* bits 7:0 */ + u32 rsrvd1 : 24; +}; + +struct NI_HV_PT_PORT_STATIC_CFG_t { + u32 int_cfg : 4; /* bits 3:0 */ + u32 phy_mode : 1; /* bits 4:4 */ + u32 rmii_clksrc : 1; /* bits 5:5 */ + u32 inv_clk_in : 1; /* bits 6:6 */ + u32 inv_clk_out : 1; /* bits 7:7 */ + u32 inv_rxclk_out : 1; /* bits 8:8 */ + u32 tx_use_gefifo : 1; /* bits 9:9 */ + u32 smii_tx_stat : 1; /* bits 10:10 */ + u32 crs_polarity : 1; /* bits 11:11 */ + u32 lpbk_mode : 2; /* bits 13:12 */ + u32 gmii_like_half_duplex_en : 1; /* bits 14:14 */ + u32 sup_tx_to_rx_lpbk_data : 1; /* bits 15:15 */ + u32 rsrvd1 : 8; + u32 mac_addr6 : 8; /* bits 31:24 */ +}; + +struct NI_HV_XRAM_CPUXRAM_CFG_t { + u32 rx_0_cpu_pkt_dis : 1; /* bits 0:0 */ + u32 rsrvd1 : 8; + u32 tx_0_cpu_pkt_dis : 1; /* bits 9:9 */ + u32 rsrvd2 : 1; + u32 rx_x_drop_err_pkt : 1; /* bits 11:11 */ + u32 xram_mgmt_dis_drop_ovsz_pkt : 1; /* bits 12:12 */ + u32 xram_mgmt_term_large_pkt : 1; /* bits 13:13 */ + u32 xram_mgmt_promisc_mode : 2; /* bits 15:14 */ + u32 xram_cntr_debug_mode : 1; /* bits 16:16 */ + u32 xram_cntr_op_code : 2; /* bits 18:17 */ + u32 rsrvd3 : 2; + u32 xram_rx_mgmtfifo_srst : 1; /* bits 21:21 */ + u32 xram_dma_fifo_srst : 1; /* bits 22:22 */ + u32 rsrvd4 : 9; +}; + +struct NI_HV_PT_RXMAC_CFG_t { + u32 rx_en : 1; /* bits 0:0 */ + u32 rsrvd1 : 7; + u32 rx_flow_disable : 1; /* bits 8:8 */ + u32 rsrvd2 : 3; + u32 rx_flow_to_tx_en : 1; /* bits 12:12 */ + u32 rx_pfc_disable : 1; /* bits 13:13 */ + u32 rsrvd3 : 15; + u32 send_pg_data : 1; /* bits 29:29 */ + u32 rsrvd4 : 2; +}; + +struct NI_HV_PT_TXMAC_CFG_t { + u32 tx_en : 1; /* bits 0:0 */ + u32 rsrvd1 : 7; + u32 mac_crc_calc_en : 1; /* bits 8:8 */ + u32 tx_ipg_sel : 3; /* bits 11:9 */ + u32 tx_flow_disable : 1; /* bits 12:12 */ + u32 tx_drain : 1; /* bits 13:13 */ + u32 tx_pfc_disable : 1; /* bits 14:14 */ + u32 tx_pau_sel : 2; /* bits 16:15 */ + u32 rsrvd2 : 9; + u32 tx_auto_xon : 1; /* bits 26:26 */ + u32 rsrvd3 : 1; + u32 pass_thru_hdr : 1; /* bits 28:28 */ + u32 rsrvd4 : 3; +}; + +struct NI_HV_GLB_INTF_RST_CONFIG_t { + u32 intf_rst_p0 : 1; /* bits 0:0 */ + u32 intf_rst_p1 : 1; /* bits 1:1 */ + u32 intf_rst_p2 : 1; /* bits 2:2 */ + u32 intf_rst_p3 : 1; /* bits 3:3 */ + u32 intf_rst_p4 : 1; /* bits 4:4 */ + u32 mac_rx_rst_p0 : 1; /* bits 5:5 */ + u32 mac_rx_rst_p1 : 1; /* bits 6:6 */ + u32 mac_rx_rst_p2 : 1; /* bits 7:7 */ + u32 mac_rx_rst_p3 : 1; /* bits 8:8 */ + u32 mac_rx_rst_p4 : 1; /* bits 9:9 */ + u32 mac_tx_rst_p0 : 1; /* bits 10:10 */ + u32 mac_tx_rst_p1 : 1; /* bits 11:11 */ + u32 mac_tx_rst_p2 : 1; /* bits 12:12 */ + u32 mac_tx_rst_p3 : 1; /* bits 13:13 */ + u32 mac_tx_rst_p4 : 1; /* bits 14:14 */ + u32 port_rst_p5 : 1; /* bits 15:15 */ + u32 pcs_rst_p6 : 1; /* bits 16:16 */ + u32 pcs_rst_p7 : 1; /* bits 17:17 */ + u32 mac_rst_p6 : 1; /* bits 18:18 */ + u32 mac_rst_p7 : 1; /* bits 19:19 */ + u32 rsrvd1 : 12; +}; + +struct NI_HV_GLB_STATIC_CFG_t { + u32 port_to_cpu : 4; /* bits 3:0 */ + u32 mgmt_pt_to_fe_also : 1; /* bits 4:4 */ + u32 txcrc_chk_en : 1; /* bits 5:5 */ + u32 p4_rgmii_tx_clk_phase : 2; /* bits 7:6 */ + u32 p4_rgmii_tx_data_order : 1; /* bits 8:8 */ + u32 rsrvd1 : 7; + u32 rxmib_mode : 1; /* bits 16:16 */ + u32 txmib_mode : 1; /* bits 17:17 */ + u32 eth_sch_rdy_pkt : 1; /* bits 18:18 */ + u32 rsrvd2 : 1; + u32 rxaui_mode : 2; /* bits 21:20 */ + u32 rxaui_sigdet : 2; /* bits 23:22 */ + u32 cnt_op_mode : 3; /* bits 26:24 */ + u32 rsrvd3 : 5; +}; + +struct GLOBAL_BLOCK_RESET_t { + u32 reset_ni : 1; /* bits 0:0 */ + u32 reset_l2fe : 1; /* bits 1:1 */ + u32 reset_l2tm : 1; /* bits 2:2 */ + u32 reset_l3fe : 1; /* bits 3:3 */ + u32 reset_sdram : 1; /* bits 4:4 */ + u32 reset_tqm : 1; /* bits 5:5 */ + u32 reset_pcie0 : 1; /* bits 6:6 */ + u32 reset_pcie1 : 1; /* bits 7:7 */ + u32 reset_pcie2 : 1; /* bits 8:8 */ + u32 reset_sata : 1; /* bits 9:9 */ + u32 reset_gic400 : 1; /* bits 10:10 */ + u32 rsrvd1 : 2; + u32 reset_usb : 1; /* bits 13:13 */ + u32 reset_flash : 1; /* bits 14:14 */ + u32 reset_per : 1; /* bits 15:15 */ + u32 reset_dma : 1; /* bits 16:16 */ + u32 reset_rtc : 1; /* bits 17:17 */ + u32 reset_pe0 : 1; /* bits 18:18 */ + u32 reset_pe1 : 1; /* bits 19:19 */ + u32 reset_rcpu0 : 1; /* bits 20:20 */ + u32 reset_rcpu1 : 1; /* bits 21:21 */ + u32 reset_sadb : 1; /* bits 22:22 */ + u32 rsrvd2 : 1; + u32 reset_rcrypto : 1; /* bits 24:24 */ + u32 reset_ldma : 1; /* bits 25:25 */ + u32 reset_fbm : 1; /* bits 26:26 */ + u32 reset_eaxi : 1; /* bits 27:27 */ + u32 reset_sd : 1; /* bits 28:28 */ + u32 reset_otprom : 1; /* bits 29:29 */ + u32 rsrvd3 : 2; +}; + +struct PER_MDIO_ADDR_t { + u32 mdio_addr : 5; /* bits 4:0 */ + u32 rsrvd1 : 3; + u32 mdio_offset : 5; /* bits 12:8 */ + u32 rsrvd2 : 2; + u32 mdio_rd_wr : 1; /* bits 15:15 */ + u32 mdio_st : 1; /* bits 16:16 */ + u32 rsrvd3 : 1; + u32 mdio_op : 2; /* bits 19:18 */ + u32 rsrvd4 : 12; +}; + +struct PER_MDIO_CTRL_t { + u32 mdiodone : 1; /* bits 0:0 */ + u32 rsrvd1 : 6; + u32 mdiostart : 1; /* bits 7:7 */ + u32 rsrvd2 : 24; +}; + +struct PER_MDIO_RDDATA_t { + u32 mdio_rddata : 16; /* bits 15:0 */ + u32 rsrvd1 : 16; +}; + +/* XRAM */ + +struct NI_HV_XRAM_CPUXRAM_ADRCFG_RX_t { + u32 rx_base_addr : 10; /* bits 9:0 */ + u32 rsrvd1 : 6; + u32 rx_top_addr : 10; /* bits 25:16 */ + u32 rsrvd2 : 6; +}; + +struct NI_HV_XRAM_CPUXRAM_ADRCFG_TX_0_t { + u32 tx_base_addr : 10; /* bits 9:0 */ + u32 rsrvd1 : 6; + u32 tx_top_addr : 10; /* bits 25:16 */ + u32 rsrvd2 : 6; +}; + +struct NI_HV_XRAM_CPUXRAM_CPU_STA_RX_0_t { + u32 pkt_wr_ptr : 10; /* bits 9:0 */ + u32 rsrvd1 : 5; + u32 int_colsc_thresh_reached : 1; /* bits 15:15 */ + u32 rsrvd2 : 16; +}; + +struct NI_HV_XRAM_CPUXRAM_CPU_CFG_RX_0_t { + u32 pkt_rd_ptr : 10; /* bits 9:0 */ + u32 rsrvd1 : 22; +}; + +struct NI_HV_XRAM_CPUXRAM_CPU_CFG_TX_0_t { + u32 pkt_wr_ptr : 10; /* bits 9:0 */ + u32 rsrvd1 : 22; +}; + +struct GLOBAL_GLOBAL_CONFIG_t { + u32 rsrvd1 : 4; + u32 wd_reset_subsys_enable : 1; /* bits 4:4 */ + u32 rsrvd2 : 1; + u32 wd_reset_all_blocks : 1; /* bits 6:6 */ + u32 wd_reset_remap : 1; /* bits 7:7 */ + u32 wd_reset_ext_reset : 1; /* bits 8:8 */ + u32 ext_reset : 1; /* bits 9:9 */ + u32 cfg_pcie_0_clken : 1; /* bits 10:10 */ + u32 cfg_sata_clken : 1; /* bits 11:11 */ + u32 cfg_pcie_1_clken : 1; /* bits 12:12 */ + u32 rsrvd3 : 1; + u32 cfg_pcie_2_clken : 1; /* bits 14:14 */ + u32 rsrvd4 : 2; + u32 ext_eth_refclk : 1; /* bits 17:17 */ + u32 refclk_sel : 2; /* bits 19:18 */ + u32 rsrvd5 : 7; + u32 l3fe_pd : 1; /* bits 27:27 */ + u32 offload0_pd : 1; /* bits 28:28 */ + u32 offload1_pd : 1; /* bits 29:29 */ + u32 crypto_pd : 1; /* bits 30:30 */ + u32 core_pd : 1; /* bits 31:31 */ +}; + +struct GLOBAL_IO_DRIVE_CONTROL_t { + u32 gmac_dp : 3; /* bits 2:0 */ + u32 gmac_dn : 3; /* bits 5:3 */ + u32 gmac_mode : 2; /* bits 7:6 */ + u32 gmac_ds : 1; /* bits 8:8 */ + u32 flash_ds : 1; /* bits 9:9 */ + u32 nu_ds : 1; /* bits 10:10 */ + u32 ssp_ds : 1; /* bits 11:11 */ + u32 spi_ds : 1; /* bits 12:12 */ + u32 gpio_ds : 1; /* bits 13:13 */ + u32 misc_ds : 1; /* bits 14:14 */ + u32 eaxi_ds : 1; /* bits 15:15 */ + u32 sd_ds : 8; /* bits 23:16 */ + u32 rsrvd1 : 8; +}; + +struct NI_HV_GLB_INIT_DONE_t { + u32 rsrvd1 : 1; + u32 ni_init_done : 1; /* bits 1:1 */ + u32 rsrvd2 : 30; +}; + +struct NI_HV_PT_PORT_GLB_CFG_t { + u32 speed : 1; /* bits 0:0 */ + u32 duplex : 1; /* bits 1:1 */ + u32 link_status : 1; /* bits 2:2 */ + u32 link_stat_mask : 1; /* bits 3:3 */ + u32 rsrvd1 : 7; + u32 power_dwn_rx : 1; /* bits 11:11 */ + u32 power_dwn_tx : 1; /* bits 12:12 */ + u32 tx_intf_lp_time : 1; /* bits 13:13 */ + u32 rsrvd2 : 18; +}; + +#define NI_HV_GLB_INIT_DONE_OFFSET 0x004 +#define NI_HV_GLB_INTF_RST_CONFIG_OFFSET 0x008 +#define NI_HV_GLB_STATIC_CFG_OFFSET 0x00c + +#define NI_HV_PT_PORT_STATIC_CFG_OFFSET NI_HV_PT_BASE +#define NI_HV_PT_PORT_GLB_CFG_OFFSET (0x4 + NI_HV_PT_BASE) +#define NI_HV_PT_RXMAC_CFG_OFFSET (0x8 + NI_HV_PT_BASE) +#define NI_HV_PT_TXMAC_CFG_OFFSET (0x14 + NI_HV_PT_BASE) + +#define NI_HV_XRAM_CPUXRAM_ADRCFG_RX_OFFSET NI_HV_XRAM_BASE +#define NI_HV_XRAM_CPUXRAM_ADRCFG_TX_0_OFFSET (0x4 + NI_HV_XRAM_BASE) +#define NI_HV_XRAM_CPUXRAM_CFG_OFFSET (0x8 + NI_HV_XRAM_BASE) +#define NI_HV_XRAM_CPUXRAM_CPU_CFG_RX_0_OFFSET (0xc + NI_HV_XRAM_BASE) +#define NI_HV_XRAM_CPUXRAM_CPU_STA_RX_0_OFFSET (0x10 + NI_HV_XRAM_BASE) +#define NI_HV_XRAM_CPUXRAM_CPU_CFG_TX_0_OFFSET (0x24 + NI_HV_XRAM_BASE) +#define NI_HV_XRAM_CPUXRAM_CPU_STAT_TX_0_OFFSET (0x28 + NI_HV_XRAM_BASE) + +#define PER_MDIO_CFG_OFFSET 0x00 +#define PER_MDIO_ADDR_OFFSET 0x04 +#define PER_MDIO_WRDATA_OFFSET 0x08 +#define PER_MDIO_RDDATA_OFFSET 0x0C +#define PER_MDIO_CTRL_OFFSET 0x10 + +#define APB0_NI_HV_PT_STRIDE 160 + +#endif /* __CORTINA_NI_H */ From a70d7b0192333133f8525305cce40f3d30e4281d Mon Sep 17 00:00:00 2001 From: Abbie Chang Date: Thu, 14 Jan 2021 13:34:12 -0800 Subject: [PATCH 6/9] net: phy: ca_phy: Add driver for CAxxxx SoCs Add phy driver support for MACs embedded inside Cortina Access SoCs Signed-off-by: Abbie Chang Signed-off-by: Alex Nemirovsky CC: Joe Hershberger CC: Tom Rini CC: Aaron Tseng Moved out PHY specific code out of Cortina NI Ethernet driver and into a Cortina Access PHY interface driver --- MAINTAINERS | 2 + drivers/net/phy/Kconfig | 7 +++ drivers/net/phy/Makefile | 1 + drivers/net/phy/ca_phy.c | 133 +++++++++++++++++++++++++++++++++++++++ drivers/net/phy/phy.c | 3 + include/phy.h | 1 + 6 files changed, 147 insertions(+) create mode 100644 drivers/net/phy/ca_phy.c diff --git a/MAINTAINERS b/MAINTAINERS index 1e5a361a87e..6d8c4674100 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -202,6 +202,7 @@ F: drivers/mtd/nand/raw/cortina_nand.c F: drivers/mtd/nand/raw/cortina_nand.h F: drivers/net/cortina_ni.c F: drivers/net/cortina_ni.h +F: drivers/net/phy/ca_phy.c F: configs/cortina_presidio-asic-pnand_defconfig ARM/CZ.NIC TURRIS MOX SUPPORT @@ -815,6 +816,7 @@ F: drivers/i2c/i2c-cortina.c F: drivers/i2c/i2c-cortina.h F: drivers/net/cortina_ni.c F: drivers/net/cortina_ni.h +F: drivers/net/phy/ca_phy.c MIPS MEDIATEK M: Weijie Gao diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 51733dd1237..d69503067d8 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -131,6 +131,13 @@ config SYS_CORTINA_FW_IN_SPIFLASH endchoice +config PHY_CORTINA_ACCESS + bool "Cortina Access Ethernet PHYs support" + default y + depends on CORTINA_NI_ENET + help + Cortina Access Ethernet PHYs init process + config PHY_DAVICOM bool "Davicom Ethernet PHYs support" diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 6e722331f1b..e967f822016 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_PHY_AQUANTIA) += aquantia.o obj-$(CONFIG_PHY_ATHEROS) += atheros.o obj-$(CONFIG_PHY_BROADCOM) += broadcom.o obj-$(CONFIG_PHY_CORTINA) += cortina.o +obj-$(CONFIG_PHY_CORTINA_ACCESS) += ca_phy.o obj-$(CONFIG_PHY_DAVICOM) += davicom.o obj-$(CONFIG_PHY_ET1011C) += et1011c.o obj-$(CONFIG_PHY_LXT) += lxt.o diff --git a/drivers/net/phy/ca_phy.c b/drivers/net/phy/ca_phy.c new file mode 100644 index 00000000000..16851a6820f --- /dev/null +++ b/drivers/net/phy/ca_phy.c @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Cortina CS4315/CS4340 10G PHY drivers + * + * Copyright 2014 Freescale Semiconductor, Inc. + * Copyright 2018 NXP + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PHY_ID_RTL8211_EXT 0x001cc910 +#define PHY_ID_RTL8211_INT 0x001cc980 +#define PHY_ID_MASK 0xFFFFFFF0 + +static void __internal_phy_init(struct phy_device *phydev, int reset_phy) +{ + u8 phy_addr; + u16 data; + + /* should initialize 4 GPHYs at once */ + for (phy_addr = 4; phy_addr > 0; phy_addr--) { + phydev->addr = phy_addr; + phy_write(phydev, MDIO_DEVAD_NONE, 31, 0x0BC6); + phy_write(phydev, MDIO_DEVAD_NONE, 16, 0x0053); + phy_write(phydev, MDIO_DEVAD_NONE, 18, 0x4003); + phy_write(phydev, MDIO_DEVAD_NONE, 22, 0x7e01); + phy_write(phydev, MDIO_DEVAD_NONE, 31, 0x0A42); + phy_write(phydev, MDIO_DEVAD_NONE, 31, 0x0A40); + phy_write(phydev, MDIO_DEVAD_NONE, 0, 0x1140); + } + + /* workaround to fix GPHY fail */ + for (phy_addr = 1; phy_addr < 5; phy_addr++) { + /* Clear clock fail interrupt */ + phydev->addr = phy_addr; + phy_write(phydev, MDIO_DEVAD_NONE, 31, 0xB90); + data = phy_read(phydev, MDIO_DEVAD_NONE, 19); + if (data == 0x10) { + phy_write(phydev, MDIO_DEVAD_NONE, 31, 0xB90); + data = phy_read(phydev, MDIO_DEVAD_NONE, 19); + printf("%s: read again.\n", __func__); + } + + printf("%s: phy_addr=%d, read register 19, value=0x%x\n", + __func__, phy_addr, data); + } +} + +static void __external_phy_init(struct phy_device *phydev, int reset_phy) +{ + u16 val; + + /* Disable response PHYAD=0 function of RTL8211 series PHY */ + /* REG31 write 0x0007, set to extension page */ + phy_write(phydev, MDIO_DEVAD_NONE, 31, 0x0007); + + /* REG30 write 0x002C, set to extension page 44 */ + phy_write(phydev, MDIO_DEVAD_NONE, 30, 0x002C); + + /* + * REG27 write bit[2] = 0 disable response PHYAD = 0 function. + * we should read REG27 and clear bit[2], and write back + */ + val = phy_read(phydev, MDIO_DEVAD_NONE, 27); + val &= ~(1 << 2); + phy_write(phydev, MDIO_DEVAD_NONE, 27, val); + + /* REG31 write 0X0000, back to page0 */ + phy_write(phydev, MDIO_DEVAD_NONE, 31, 0x0000); +} + +static int rtl8211_external_config(struct phy_device *phydev) +{ + __external_phy_init(phydev, 0); + printf("%s: initialize RTL8211 external done.\n", __func__); + return 0; +} + +static int rtl8211_internal_config(struct phy_device *phydev) +{ + struct phy_device phydev_init; + + memcpy(&phydev_init, phydev, sizeof(struct phy_device)); + /* should initialize 4 GPHYs at once */ + __internal_phy_init(&phydev_init, 0); + printf("%s: initialize RTL8211 internal done.\n", __func__); + return 0; +} + +static int rtl8211_probe(struct phy_device *phydev) +{ + /* disable reset behavior */ + phydev->flags = PHY_FLAG_BROKEN_RESET; + return 0; +} + +/* Support for RTL8211 External PHY */ +struct phy_driver rtl8211_external_driver = { + .name = "Cortina RTL8211 External", + .uid = PHY_ID_RTL8211_EXT, + .mask = PHY_ID_MASK, + .features = PHY_GBIT_FEATURES, + .config = &rtl8211_external_config, + .probe = &rtl8211_probe, + .startup = &genphy_startup, +}; + +/* Support for RTL8211 Internal PHY */ +struct phy_driver rtl8211_internal_driver = { + .name = "Cortina RTL8211 Inrernal", + .uid = PHY_ID_RTL8211_INT, + .mask = PHY_ID_MASK, + .features = PHY_GBIT_FEATURES, + .config = &rtl8211_internal_config, + .probe = &rtl8211_probe, + .startup = &genphy_startup, +}; + +int phy_cortina_access_init(void) +{ + phy_register(&rtl8211_external_driver); + phy_register(&rtl8211_internal_driver); + return 0; +} diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 9587e6b9fae..662ea2b98cc 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -500,6 +500,9 @@ int phy_init(void) #ifdef CONFIG_PHY_CORTINA phy_cortina_init(); #endif +#ifdef CONFIG_PHY_CORTINA_ACCESS + phy_cortina_access_init(); +#endif #ifdef CONFIG_PHY_DAVICOM phy_davicom_init(); #endif diff --git a/include/phy.h b/include/phy.h index cbdb10d6fce..7750efd8bb5 100644 --- a/include/phy.h +++ b/include/phy.h @@ -493,6 +493,7 @@ int phy_aquantia_init(void); int phy_atheros_init(void); int phy_broadcom_init(void); int phy_cortina_init(void); +int phy_cortina_access_init(void); int phy_davicom_init(void); int phy_et1011c_init(void); int phy_lxt_init(void); From c34a927566d553a799b4811456b49c88b6e5e30c Mon Sep 17 00:00:00 2001 From: Alex Nemirovsky Date: Thu, 14 Jan 2021 13:34:13 -0800 Subject: [PATCH 7/9] board: presidio-asic: Add CAxxxx Ethernet support Add CAxxxx Ethernet support for the Cortina Access Presidio Engineering Board Signed-off-by: Alex Nemirovsky CC: Tom Rini --- arch/arm/dts/ca-presidio-engboard.dts | 31 ++++++++++++++++++++ configs/cortina_presidio-asic-emmc_defconfig | 4 ++- include/configs/presidio_asic.h | 15 +++++++++- 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/arch/arm/dts/ca-presidio-engboard.dts b/arch/arm/dts/ca-presidio-engboard.dts index 0ab52fdfda8..8c1e3797d75 100644 --- a/arch/arm/dts/ca-presidio-engboard.dts +++ b/arch/arm/dts/ca-presidio-engboard.dts @@ -109,4 +109,35 @@ }; }; + + eth: ethnet@0xf4300000 { + compatible = "eth_cortina"; + reg = <0x0 0xf4320000 0x34>, + <0x0 0xf43290d8 0x04>, + <0x0 0xf4304000 0x04>; + + /* port0: phy address 1 - GMAC0: port 0 + * port1: phy address 2 - GMAC1: port 1 + * port2: phy address 3 - GMAC2: port 2 + * port3: phy address 4 - GMAC3: port 3 + * port4: phy address 5 - RGMII: port 4 + */ + valid-port-map = <0x1f>; + valid-port-num = <5>; + valid-ports = <0x1 0x0>, + <0x2 0x1>, + <0x3 0x2>, + <0x4 0x3>, + <0x5 0x4>; + def-active-port = <0x3>; + inter-gphy-num = <6>; + inter-gphy-val = <0xf43380fc 0xbcd>, + <0xf43380dc 0xeeee>, + <0xf43380d8 0xeeee>, + <0xf43380fc 0xbce>, + <0xf43380c0 0x7777>, + <0xf43380c4 0x7777>; + init-rgmii = <1>; + ni-xram-base = <0xF4500000>; + }; }; diff --git a/configs/cortina_presidio-asic-emmc_defconfig b/configs/cortina_presidio-asic-emmc_defconfig index faffdd41479..eec958423b8 100644 --- a/configs/cortina_presidio-asic-emmc_defconfig +++ b/configs/cortina_presidio-asic-emmc_defconfig @@ -22,7 +22,6 @@ CONFIG_CMD_EXT2=y CONFIG_CMD_EXT4=y CONFIG_OF_CONTROL=y CONFIG_OF_LIVE=y -# CONFIG_NET is not set CONFIG_DM=y CONFIG_CORTINA_GPIO=y CONFIG_DM_I2C=y @@ -30,6 +29,9 @@ CONFIG_SYS_I2C_CA=y CONFIG_DM_MMC=y CONFIG_MMC_DW=y CONFIG_MMC_DW_CORTINA=y +CONFIG_PHYLIB=y +CONFIG_DM_ETH=y +CONFIG_CORTINA_NI_ENET=y CONFIG_DM_SERIAL=y CONFIG_CORTINA_UART=y CONFIG_WDT=y diff --git a/include/configs/presidio_asic.h b/include/configs/presidio_asic.h index 710731efd56..3f926212820 100644 --- a/include/configs/presidio_asic.h +++ b/include/configs/presidio_asic.h @@ -2,7 +2,7 @@ /* * Copyright (C) 2020 Cortina Access Inc. * - * Configuration for Cortina-Access Presidio board. + * Configuration for Cortina-Access Presidio board */ #ifndef __PRESIDIO_ASIC_H @@ -63,6 +63,19 @@ sizeof(CONFIG_SYS_PROMPT) + 16) #define CONFIG_SYS_BARGSIZE CONFIG_SYS_CBSIZE +#define KSEG1_ATU_XLAT(x) (x) + +/* HW REG ADDR */ +#define NI_READ_POLL_COUNT 1000 +#define CA_NI_MDIO_REG_BASE 0xF4338 +#define NI_HV_GLB_MAC_ADDR_CFG0_OFFSET 0x010 +#define NI_HV_GLB_MAC_ADDR_CFG1_OFFSET 0x014 +#define NI_HV_PT_BASE 0x400 +#define NI_HV_XRAM_BASE 0x820 +#define GLOBAL_BLOCK_RESET_OFFSET 0x04 +#define GLOBAL_GLOBAL_CONFIG_OFFSET 0x20 +#define GLOBAL_IO_DRIVE_CONTROL_OFFSET 0x4c + /* max command args */ #define CONFIG_SYS_MAXARGS 64 #define CONFIG_EXTRA_ENV_SETTINGS "silent=y\0" From b5f09df246a8a24a3b4acb669cbf95d2a063176b Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 17 Jan 2021 00:16:16 +0100 Subject: [PATCH 8/9] net: phy: micrel: Try default PHY ofnode first The phydev structure has a PHY OF node pointer in it, use that OF node first when looking up PHY OF node properties, since that is likely the correct PHY OF node pointer. If the pointer is not valid, which is the case e.g. on legacy DTs, fall back to parsing MAC ethernet-phy subnode. Signed-off-by: Marek Vasut Cc: Joe Hershberger Cc: Ramon Fried --- drivers/net/phy/micrel_ksz90x1.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/net/phy/micrel_ksz90x1.c b/drivers/net/phy/micrel_ksz90x1.c index 77fad4a8fc9..e5f578201f3 100644 --- a/drivers/net/phy/micrel_ksz90x1.c +++ b/drivers/net/phy/micrel_ksz90x1.c @@ -120,8 +120,13 @@ static int ksz90x1_of_config_group(struct phy_device *phydev, if (!drv || !drv->writeext) return -EOPNOTSUPP; - /* Look for a PHY node under the Ethernet node */ - node = dev_read_subnode(dev, "ethernet-phy"); + node = phydev->node; + + if (!ofnode_valid(node)) { + /* Look for a PHY node under the Ethernet node */ + node = dev_read_subnode(dev, "ethernet-phy"); + } + if (!ofnode_valid(node)) { /* No node found, look in the Ethernet node */ node = dev_ofnode(dev); From 69076dff2284ed099cc0583e5e64bd8012d1ab5c Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Wed, 20 Jan 2021 09:54:53 +0100 Subject: [PATCH 9/9] cmd: pxe: add support for FDT overlays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds support for specifying FDT overlays in an extlinux/pxelinux configuration file. Without this, there is no simple way to apply overlays when the kernel and fdt is loaded by the pxe command. This change adds the 'fdtoverlays' keyword for a label, supporting multiple overlay files to be applied on top of the fdt specified in the 'fdt' or 'devicetree' keyword. Example: label linux kernel /Image fdt /soc-board.dtb fdtoverlays /soc-board-function.dtbo append console=ttyS0,115200 root=/dev/mmcblk0p2 rootwait This code makes usage of a new variable called fdtoverlay_addr_r used to load the overlay files without overwritting anything important. Cc: Tom Rini Cc: Andre Heider Cc: Jernej Škrabec Cc: Jonas Karlman Tested-by: Jernej Škrabec Reviewed-by: Jernej Škrabec Signed-off-by: Neil Armstrong --- cmd/pxe_utils.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++ cmd/pxe_utils.h | 1 + doc/README.pxe | 9 +++++ 3 files changed, 113 insertions(+) diff --git a/cmd/pxe_utils.c b/cmd/pxe_utils.c index b9d9a5786c2..3526a651d79 100644 --- a/cmd/pxe_utils.c +++ b/cmd/pxe_utils.c @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include #include #include @@ -284,6 +286,9 @@ static void label_destroy(struct pxe_label *label) if (label->fdtdir) free(label->fdtdir); + if (label->fdtoverlays) + free(label->fdtoverlays); + free(label); } @@ -332,6 +337,92 @@ static int label_localboot(struct pxe_label *label) return run_command_list(localcmd, strlen(localcmd), 0); } +/* + * Loads fdt overlays specified in 'fdtoverlays'. + */ +#ifdef CONFIG_OF_LIBFDT_OVERLAY +static void label_boot_fdtoverlay(struct cmd_tbl *cmdtp, struct pxe_label *label) +{ + char *fdtoverlay = label->fdtoverlays; + struct fdt_header *working_fdt; + char *fdtoverlay_addr_env; + ulong fdtoverlay_addr; + ulong fdt_addr; + int err; + + /* Get the main fdt and map it */ + fdt_addr = simple_strtoul(env_get("fdt_addr_r"), NULL, 16); + working_fdt = map_sysmem(fdt_addr, 0); + err = fdt_check_header(working_fdt); + if (err) + return; + + /* Get the specific overlay loading address */ + fdtoverlay_addr_env = env_get("fdtoverlay_addr_r"); + if (!fdtoverlay_addr_env) { + printf("Invalid fdtoverlay_addr_r for loading overlays\n"); + return; + } + + fdtoverlay_addr = simple_strtoul(fdtoverlay_addr_env, NULL, 16); + + /* Cycle over the overlay files and apply them in order */ + do { + struct fdt_header *blob; + char *overlayfile; + char *end; + int len; + + /* Drop leading spaces */ + while (*fdtoverlay == ' ') + ++fdtoverlay; + + /* Copy a single filename if multiple provided */ + end = strstr(fdtoverlay, " "); + if (end) { + len = (int)(end - fdtoverlay); + overlayfile = malloc(len + 1); + strncpy(overlayfile, fdtoverlay, len); + overlayfile[len] = '\0'; + } else + overlayfile = fdtoverlay; + + if (!strlen(overlayfile)) + goto skip_overlay; + + /* Load overlay file */ + err = get_relfile_envaddr(cmdtp, overlayfile, + "fdtoverlay_addr_r"); + if (err < 0) { + printf("Failed loading overlay %s\n", overlayfile); + goto skip_overlay; + } + + /* Resize main fdt */ + fdt_shrink_to_minimum(working_fdt, 8192); + + blob = map_sysmem(fdtoverlay_addr, 0); + err = fdt_check_header(blob); + if (err) { + printf("Invalid overlay %s, skipping\n", + overlayfile); + goto skip_overlay; + } + + err = fdt_overlay_apply_verbose(working_fdt, blob); + if (err) { + printf("Failed to apply overlay %s, skipping\n", + overlayfile); + goto skip_overlay; + } + +skip_overlay: + if (end) + free(overlayfile); + } while ((fdtoverlay = strstr(fdtoverlay, " "))); +} +#endif + /* * Boot according to the contents of a pxe_label. * @@ -534,6 +625,11 @@ static int label_boot(struct cmd_tbl *cmdtp, struct pxe_label *label) goto cleanup; } } + +#ifdef CONFIG_OF_LIBFDT_OVERLAY + if (label->fdtoverlays) + label_boot_fdtoverlay(cmdtp, label); +#endif } else { bootm_argv[3] = NULL; } @@ -591,6 +687,7 @@ enum token_type { T_INCLUDE, T_FDT, T_FDTDIR, + T_FDTOVERLAYS, T_ONTIMEOUT, T_IPAPPEND, T_BACKGROUND, @@ -625,6 +722,7 @@ static const struct token keywords[] = { {"fdt", T_FDT}, {"devicetreedir", T_FDTDIR}, {"fdtdir", T_FDTDIR}, + {"fdtoverlays", T_FDTOVERLAYS}, {"ontimeout", T_ONTIMEOUT,}, {"ipappend", T_IPAPPEND,}, {"background", T_BACKGROUND,}, @@ -1057,6 +1155,11 @@ static int parse_label(char **c, struct pxe_menu *cfg) err = parse_sliteral(c, &label->fdtdir); break; + case T_FDTOVERLAYS: + if (!label->fdtoverlays) + err = parse_sliteral(c, &label->fdtoverlays); + break; + case T_LOCALBOOT: label->localboot = 1; err = parse_integer(c, &label->localboot_val); diff --git a/cmd/pxe_utils.h b/cmd/pxe_utils.h index 77d25888758..6af95237343 100644 --- a/cmd/pxe_utils.h +++ b/cmd/pxe_utils.h @@ -43,6 +43,7 @@ struct pxe_label { char *initrd; char *fdt; char *fdtdir; + char *fdtoverlays; int ipappend; int attempted; int localboot; diff --git a/doc/README.pxe b/doc/README.pxe index 42f913c61fe..b67151ca510 100644 --- a/doc/README.pxe +++ b/doc/README.pxe @@ -89,6 +89,9 @@ pxe boot fdt_addr - the location of a fdt blob. 'fdt_addr' will be passed to bootm command if it is set and 'fdt_addr_r' is not passed to bootm command. + fdtoverlay_addr_r - location in RAM at which 'pxe boot' will temporarily store + fdt overlay(s) before applying them to the fdt blob stored at 'fdt_addr_r'. + pxe file format =============== The pxe file format is nearly a subset of the PXELINUX file format; see @@ -148,6 +151,12 @@ kernel - if this label is chosen, use tftp to retrieve the kernel It useful for overlay selection in pxe file (see: doc/uImage.FIT/overlay-fdt-boot.txt) +fdtoverlays [...] - if this label is chosen, use tftp to retrieve the DT + overlay(s) at . it will be temporarily stored at the + address indicated in the fdtoverlay_addr_r environment variable, + and then applied in the load order to the fdt blob stored at the + address indicated in the fdt_addr_r environment variable. + append - use as the kernel command line when booting this label.