Hi Team,
I want to implement cr52 usxgmii (Marvell 88Q2112),
I've modified
whitebox-sdk/realtime_cpu/trampoline/libraries/drivers/ethernet/renesas/eth_serdes.c
whitebox-sdk/realtime_cpu/trampoline/libraries/drivers/ethernet/renesas/rswitch.c
CR52 network does not work.
The experimental results and steps are as follows
How can I modify the code to make CR52 network work properly?
Experimenting with Linux
The two boards phy: usxgmii 2.5G are communicable
A:B uboot
setenv rswitch.parallel_mode 0
saveenv
A Linux_TSN0 :Master
echo m > /proc/mvq3244/etha0\:02_link
ifconfig tsn1 192.168.1.22
B: Linux_TSN1 :Slave
echo s > /proc/mvq3244/etha1\:04_link
ifconfig tsn1 192.168.1.21
We want to do the network under CR52
A:CR52 Master
Change the following two files. Search for "__Drogon__" "__Drogon__0".
Use the example
/whitebox-sdk/realtime_cpu/trampoline/examples/cortex-a-r/armv8/spider/ethernet
AKA cr52_eth.srec
In uboot
setenv rswitch.parallel_mode 1
(reboot into uboot)
B ping A: no response
Fullscreen eth_serdes.c Download #include "tpl_os.h" #include "eth_serdes.h" #include "err_codes.h" #include "utils.h" //#define ETH_SERDES_AN_ENABLED #define __Drogon__ #ifdef __Drogon__ //Max_Add #define BANK_180 0x0180 #define VR_XS_PMA_MP_12G_16G_25G_REF_CLK_CTRL 0x0244 #define VR_XS_PMA_MP_10G_MPLLA_CTRL2 0x01cc #define VR_XS_PMA_MP_12G_16G_MPLLA_CTRL0 0x01c4 #define VR_XS_PMA_MP_12G_MPLLA_CTRL1 0x01c8 #define VR_XS_PMA_MP_12G_MPLLA_CTRL3 0x01dc #define BANK_300 0x0300 #define BANK_380 0x0380 #define SR_XS_PCS_CTRL2 0x001c #define VR_XS_PCS_DEBUG_CTRL 0x0014 #define VR_XS_PCS_DIG_CTRL1 0x0000 #define VR_XS_PCS_KR_CTRL 0x001c #define VR_XS_PMA_MP_12G_16G_25G_MPLL_CMN_CTRL 0x01c0 #define VR_XS_PMA_MP_12G_16G_25G_VCO_CAL_LD0 0x0248 #define VR_XS_PMA_MP_12G_VCO_CAL_REF0 0x0258 #define VR_XS_PMA_MP_12G_16G_25G_RX_GENCTRL1 0x0144 #define VR_XS_PMA_CONSUMER_10G_RX_GENCTRL4 0x01a0 #define VR_XS_PMA_MP_12G_16G_25G_TX_RATE_CTRL 0x00d0 #define VR_XS_PMA_MP_12G_16G_25G_RX_RATE_CTRL 0x0150 #define VR_XS_PMA_MP_12G_16G_TX_GENCTRL2 0x00c8 #define VR_XS_PMA_MP_12G_16G_RX_GENCTRL2 0x0148 #define VR_XS_PMA_MP_12G_AFE_DFE_EN_CTRL 0x0174 #define VR_XS_PMA_MP_12G_RX_EQ_CTRL0 0x0160 #define VR_XS_PMA_MP_10G_RX_IQ_CTRL0 0x01ac #define VR_XS_PMA_MP_12G_16G_25G_TX_GENCTRL1 0x00c4 #define VR_XS_PMA_MP_12G_16G_25G_TX_EQ_CTRL0 0x00d8 #define VR_XS_PMA_MP_12G_16G_25G_TX_EQ_CTRL1 0x00dc #define SR_MII_CTRL 0x0000 #endif #define ETH_SERDES_CH_NUM 3 #define ETH_SERDES_XPCS_CH0 0UL #define ETH_SERDES_XPCS_CH1 1UL #define ETH_SERDES_XPCS_CH2 2UL /* Maximum Ethernet Timeout Count */ #define ETH_TIMEOUT_COUNT 63158UL /* Ethernet SERDES registers Base Address */ #define ETH_SERDES_XPCS0_BASE 0xE6444000UL #define ETH_SERDES_XPCS1_BASE 0xE6444400UL #define ETH_SERDES_XPCS2_BASE 0xE6444800UL /* Ehternet SERDES Bank Register Address */ #define ETH_SERDES_XPCS0_BANK 0xE64443FCUL #define ETH_SERDES_XPCS1_BANK 0xE64447FCUL #define ETH_SERDES_XPCS2_BANK 0xE6444BFCUL uint32 eth_serdes_base_addr[ETH_SERDES_CH_NUM] = { ETH_SERDES_XPCS0_BASE, ETH_SERDES_XPCS1_BASE, ETH_SERDES_XPCS2_BASE }; uint32 eth_serdes_bank_addr[ETH_SERDES_CH_NUM] = { ETH_SERDES_XPCS0_BANK, ETH_SERDES_XPCS1_BANK, ETH_SERDES_XPCS2_BANK }; typedef enum { ETH_MAC_LAYER_SPEED_10M = 0, ETH_MAC_LAYER_SPEED_100M, ETH_MAC_LAYER_SPEED_1G, ETH_MAC_LAYER_SPEED_2500M, ETH_MAC_LAYER_SPEED_10G } eth_serdes_speed_t; #ifdef __Drogon__ #define ETH_SERDES_BPS ETH_MAC_LAYER_SPEED_2500M #else #define ETH_SERDES_BPS ETH_MAC_LAYER_SPEED_1G #endif #define ETH_SERDES_SEL_BANK(ch, value) \ {*(volatile uint32*)eth_serdes_bank_addr[ch] = (uint32)value;} #define ETH_SERDES_REG_WRITE(ch, offset, value) \ {*(volatile uint32*)(eth_serdes_base_addr[ch] + (uint16)offset) = (uint32)value;} #define ETH_SERDES_REG_READ(ch, offset) \ (*(volatile uint32*)(eth_serdes_base_addr[ch] + (uint16)offset)) void eth_disable_fuse_ovr(void) { /* Disable Fuse_override_en */ uint32 address; volatile uint32 val; address = 0xE6446600; val = *((volatile uint32*)address); *((volatile uint32*)address) = 0x00000000U; val = *((volatile uint32*)address); (void) val; } static int eth_serdes_wait_for_update(uint32 ch, uint16 offset, uint32 mask, uint32 exp_val, uint32 timeout) { uint32 start_time; uint32 reg_val; start_time = get_time(); do { reg_val = ETH_SERDES_REG_READ(ch, offset); if ((reg_val & mask) == exp_val) { return 0; } } while (get_elapsed_time(start_time) < timeout); return ERR_TIMEOUT; } static int eth_serdes_wait_reset(void) { int ch, ret; for (ch = ETH_SERDES_XPCS_CH0; ch < ETH_SERDES_CH_NUM; ch++) { ETH_SERDES_SEL_BANK(ch, 0x0180U); ret = eth_serdes_wait_for_update(ch, 0x026C, BIT(0), BIT(0), ETH_TIMEOUT_COUNT); if (ret != 0) { break; } } return ret; } static int eth_serdes_initialize_SRAM(void) { int ch, ret; ret = eth_serdes_wait_reset(); if (ret != 0) { return ret; } for (ch = ETH_SERDES_XPCS_CH0; ch < ETH_SERDES_CH_NUM; ch++) { ETH_SERDES_SEL_BANK(ch, 0x0180); ETH_SERDES_REG_WRITE(ch, 0x026C, 0x3); } for (ch = ETH_SERDES_XPCS_CH0; ch < ETH_SERDES_CH_NUM; ch++) { ETH_SERDES_SEL_BANK(ch, 0x0300U); ret = eth_serdes_wait_for_update(ch, 0x0000U, BIT(15), 0UL, ETH_TIMEOUT_COUNT); if (ret != 0) { break; } } return ret; } #ifdef __Drogon__ static void eth_serdes_set_USXGMII_common_settings(void) { int ch; /* Steps U.4.1 to U.4.5 */ for (ch = ETH_SERDES_XPCS_CH0; ch < ETH_SERDES_CH_NUM; ch++) { ETH_SERDES_SEL_BANK(ch, BANK_180); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_16G_25G_REF_CLK_CTRL, 0x57); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_10G_MPLLA_CTRL2, 0xc200); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_16G_MPLLA_CTRL0, 0x42); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_MPLLA_CTRL1, 0x0); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_MPLLA_CTRL3, 0x2f); } } #else static void eth_serdes_set_SGMII_common_settings(void) { int ch; /* Steps S.4.1 to S.4.5 */ for (ch = ETH_SERDES_XPCS_CH0; ch < ETH_SERDES_CH_NUM; ch++) { ETH_SERDES_SEL_BANK(ch, 0x0180); ETH_SERDES_REG_WRITE(ch, 0x0244, 0x97); ETH_SERDES_REG_WRITE(ch, 0x01D0, 0x60); ETH_SERDES_REG_WRITE(ch, 0x01D8, 0x2200); ETH_SERDES_REG_WRITE(ch, 0x01D4, 0x0); ETH_SERDES_REG_WRITE(ch, 0x01E0, 0x3D); } } #endif static int eth_serdes_PHY_soft_reset(void) { int ch, ret; /* Step:5 */ for (ch = ETH_SERDES_XPCS_CH0; ch < ETH_SERDES_CH_NUM; ch++) { ETH_SERDES_SEL_BANK(ch, 0x0380); ETH_SERDES_REG_WRITE(ch, 0x0000, 0x8000); } /* Step:6 */ ret = eth_serdes_wait_reset(); if (ret != 0) { return ret; } /* Step:7 */ for (ch = ETH_SERDES_XPCS_CH0; ch < ETH_SERDES_CH_NUM; ch++) { ETH_SERDES_SEL_BANK(ch, 0x0180U); ETH_SERDES_REG_WRITE(ch, 0x026CU, 0x00000003UL); } /* Step:8 */ for (ch = ETH_SERDES_XPCS_CH0; ch < ETH_SERDES_CH_NUM; ch++) { ETH_SERDES_SEL_BANK(ch, 0x0380U); ret = eth_serdes_wait_for_update(ch, 0x0000U, BIT(15), 0UL, ETH_TIMEOUT_COUNT); if (ret != 0) { return ret; } } return 0; } #ifdef __Drogon__ static int eth_serdes_channel_USXGMII_common_configuration(uint32 ch){ int ret; ETH_SERDES_SEL_BANK(ch, BANK_300); ETH_SERDES_REG_WRITE(ch, VR_XS_PCS_KR_CTRL, 0x0); ETH_SERDES_SEL_BANK(ch, BANK_380); ETH_SERDES_REG_WRITE(ch, VR_XS_PCS_DEBUG_CTRL, 0x50); ETH_SERDES_REG_WRITE(ch, VR_XS_PCS_DIG_CTRL1, 0x2200); ETH_SERDES_REG_WRITE(ch, VR_XS_PCS_KR_CTRL, 0x400); ETH_SERDES_SEL_BANK(ch, BANK_180); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_16G_25G_MPLL_CMN_CTRL, 0x01); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_16G_25G_VCO_CAL_LD0, 0x56a); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_VCO_CAL_REF0, 0x15); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_16G_25G_RX_GENCTRL1, 0x1100); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_CONSUMER_10G_RX_GENCTRL4, 0x1); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_16G_25G_TX_RATE_CTRL, 0x1); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_16G_25G_RX_RATE_CTRL, 0x1); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_16G_TX_GENCTRL2, 0x300); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_16G_RX_GENCTRL2, 0x300); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_AFE_DFE_EN_CTRL, 0x0); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_RX_EQ_CTRL0, 0x04); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_10G_RX_IQ_CTRL0, 0x0); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_16G_25G_TX_GENCTRL1, 0x310); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_16G_TX_GENCTRL2, 0x0301); ret = eth_serdes_wait_for_update(ch, VR_XS_PMA_MP_12G_16G_TX_GENCTRL2, BIT(0), 0x0, ETH_TIMEOUT_COUNT); if (ret != 0) { return ret; } ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_16G_RX_GENCTRL2, 0x0301); ret = eth_serdes_wait_for_update(ch, VR_XS_PMA_MP_12G_16G_RX_GENCTRL2, BIT(0), 0x0, ETH_TIMEOUT_COUNT); if (ret != 0) { return ret; } ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_16G_25G_TX_GENCTRL1, 0x1310); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_16G_25G_TX_EQ_CTRL0, 0x1800); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_16G_25G_TX_EQ_CTRL1, 0x0); ETH_SERDES_SEL_BANK(ch, BANK_380); ETH_SERDES_REG_WRITE(ch, VR_XS_PCS_DIG_CTRL1, 0x2300); ret = eth_serdes_wait_for_update(ch, VR_XS_PCS_DIG_CTRL1, BIT(8), 0x0, ETH_TIMEOUT_COUNT); return ret; } #else static int eth_serdes_channel_SGMII_common_configuration(uint32 ch) { int ret; /* Steps S/SA.9.1 to S/SA.9.14 */ ETH_SERDES_SEL_BANK(ch, 0x0380); ETH_SERDES_REG_WRITE(ch, 0x0000, 0x2000); ETH_SERDES_SEL_BANK(ch, 0x0180); ETH_SERDES_REG_WRITE(ch, 0x01C0, 0x11); ETH_SERDES_REG_WRITE(ch, 0x0248, 0x540); ETH_SERDES_REG_WRITE(ch, 0x0258, 0x15); ETH_SERDES_REG_WRITE(ch, 0x0144, 0x100); ETH_SERDES_REG_WRITE(ch, 0x01A0, 0x0); ETH_SERDES_REG_WRITE(ch, 0x00D0, 0x2); ETH_SERDES_REG_WRITE(ch, 0x0150, 0x3); ETH_SERDES_REG_WRITE(ch, 0x00C8, 0x100); ETH_SERDES_REG_WRITE(ch, 0x0148, 0x100); ETH_SERDES_REG_WRITE(ch, 0x0174, 0x0); ETH_SERDES_REG_WRITE(ch, 0x0160, 0x7); ETH_SERDES_REG_WRITE(ch, 0x01AC, 0x0); ETH_SERDES_REG_WRITE(ch, 0x00C4, 0x310); /* Step: S/SA.9.15 */ ETH_SERDES_REG_WRITE(ch, 0x00C8, 0x101); /* Step: S/SA.9.16 */ ret = eth_serdes_wait_for_update(ch, 0x00C8, BIT(0), 0x0, ETH_TIMEOUT_COUNT); if (ret != 0) { return ret; } /* Step: S/SA.9.17 */ ETH_SERDES_REG_WRITE(ch, 0x0148, 0x101); /* Step: S/SA.9.18 */ ret = eth_serdes_wait_for_update(ch, 0x0148, BIT(0), 0x0, ETH_TIMEOUT_COUNT); if (ret != 0) { return ret; } /* Steps S/SA.9.19 to S/SA.9.24 */ ETH_SERDES_REG_WRITE(ch, 0x00C4, 0x1310); ETH_SERDES_REG_WRITE(ch, 0x00D8, 0x1800); ETH_SERDES_REG_WRITE(ch, 0x00DC, 0x0); ETH_SERDES_SEL_BANK(ch, 0x0300); ETH_SERDES_REG_WRITE(ch, 0x001C, 0x1); ETH_SERDES_SEL_BANK(ch, 0x0380); ETH_SERDES_REG_WRITE(ch, 0x0000, 0x2100); ret = eth_serdes_wait_for_update(ch, 0x0000, BIT(8), 0x0, ETH_TIMEOUT_COUNT); return ret; } #endif #if defined(ETH_SERDES_AN_ENABLED) static void eth_serdes_channel_SGMII_enable_AN(uint32 ch, eth_serdes_speed_t LenSpeed) { /* Step: SA.9.25 */ ETH_SERDES_SEL_BANK(ch, 0x1F00); if (ETH_MAC_LAYER_SPEED_1G == ch) { /* select when 1Gbps */ ETH_SERDES_REG_WRITE(ch, 0x0000, 0x140); } else { /* select when 100Mbps */ ETH_SERDES_REG_WRITE(ch, 0x0000, 0x2100); } /* Steps SA.9.26 to SA.9.28 */ ETH_SERDES_SEL_BANK(ch, 0x1F80); ETH_SERDES_REG_WRITE(ch, 0x0004, 0x005); ETH_SERDES_REG_WRITE(ch, 0x0028, 0x7A1); ETH_SERDES_REG_WRITE(ch, 0x0000, 0x208); } #endif #if defined(ETH_SERDES_AN_ENABLED) static int eth_serdes_channel_set_bps_SGMII_ANON(uint32 ch, eth_serdes_speed_t LenSpeed) { int ret; /* Step: SA.10.1 */ ETH_SERDES_SEL_BANK(ch, 0x1F00); /* Mode Selection Method is provisional */ if (ETH_MAC_LAYER_SPEED_1G == LenSpeed) { /* select 1Gbps */ ETH_SERDES_REG_WRITE(ch, 0x0000, 0x1140); } else { /* select 100Mbps */ ETH_SERDES_REG_WRITE(ch, 0x0000, 0x3100); } /* Step: SA.10.2 */ ETH_SERDES_SEL_BANK(ch, 0x1F80); ret = eth_serdes_wait_for_update(ch, 0x0008, BIT(0), BIT(0), ETH_TIMEOUT_COUNT); if (ret != 0) { return ret; } ETH_SERDES_REG_WRITE(ch, 0x0008, 0x0); return ret; } #endif #ifdef __Drogon__ //u10.1 static void eth_serdes_channel_set_bps_USXGMII_ANOFF(uint32 ch, eth_serdes_speed_t LenSpeed) { if(ETH_MAC_LAYER_SPEED_2500M==LenSpeed){ /* Step:U.10.1 USXGMII - 2.5Gbps */ ETH_SERDES_SEL_BANK(ch, 0x1F00); ETH_SERDES_REG_WRITE(ch, SR_MII_CTRL, 0x120); } /* Step:U.10.2 Wait for the required amount of time (>10us).*/ ms_delay(10); /* Step:U.10.3*/ ETH_SERDES_SEL_BANK(ch, 0x0380); ETH_SERDES_REG_WRITE(ch, SR_MII_CTRL, 0x2600); eth_serdes_wait_for_update(ch, 0x0000, BIT(10), 0x0, ETH_TIMEOUT_COUNT); } #else static void eth_serdes_channel_set_bps_SGMII_ANOFF(uint32 ch, eth_serdes_speed_t LenSpeed) { /* Step:S.10.1 */ ETH_SERDES_SEL_BANK(ch, 0x1F00); /* Mode Selection Method is provisional */ if (ETH_MAC_LAYER_SPEED_1G == LenSpeed) { /* select 1Gbps */ ETH_SERDES_REG_WRITE(ch, 0x0000, 0x140); } else { /* select 100Mbps */ ETH_SERDES_REG_WRITE(ch, 0x0000, 0x2100); } } #endif /* Following User's Manual at section 100.6.3 */ int eth_serdes_initialize(void) { int ret, ch; /* Step 1: Initialize SRAM */ ret = eth_serdes_initialize_SRAM(); if (ret != 0) { return ret; } /* Step 2 */ for (ch = ETH_SERDES_XPCS_CH0; ch < ETH_SERDES_CH_NUM; ch++) { ETH_SERDES_SEL_BANK(ch, 0x0380); ETH_SERDES_REG_WRITE(ch, 0x03D4, 0x443); } /* Step 3 */ #ifdef __Drogon__ eth_serdes_set_USXGMII_common_settings(); #else eth_serdes_set_SGMII_common_settings(); #endif /* Step 4 */ for (ch = ETH_SERDES_XPCS_CH0; ch < ETH_SERDES_CH_NUM; ch++) { ETH_SERDES_SEL_BANK(ch, 0x0380); ETH_SERDES_REG_WRITE(ch, 0X03D0, 0x1); } /* Steps 5 to 8 */ ret = eth_serdes_PHY_soft_reset(); if (ret != 0) { return ret; } /* Step 9 */ for (ch = ETH_SERDES_XPCS_CH0; ch < ETH_SERDES_CH_NUM; ch++) { #ifdef __Drogon__ /* Steps U.9.1 to U.9.26 */ ret = eth_serdes_channel_USXGMII_common_configuration(ch); #else /* Steps S/SA.9.1 to S/SA.9.24 */ ret = eth_serdes_channel_SGMII_common_configuration(ch); #endif if (ret != 0) { return ret; } #if defined(ETH_SERDES_AN_ENABLED) /* Steps SA.9.25 to SA.9.28 */ eth_serdes_channel_SGMII_enable_AN(ch, ETH_SERDES_BPS); #endif } /* Step 10: */ for (ch = ETH_SERDES_XPCS_CH0; ch < ETH_SERDES_CH_NUM; ch++) { #if defined(ETH_SERDES_AN_ENABLED) /* Steps SA.10.1 to SA.10.3 */ ret = eth_serdes_channel_set_bps_SGMII_ANON(ch, ETH_SERDES_BPS); if (ret != 0) { return ret; } #else #ifdef __Drogon__ /*Step: U10.1 to U10.4*/ eth_serdes_channel_set_bps_USXGMII_ANOFF(ch, ETH_SERDES_BPS); #else /* Step: S.10.1 */ eth_serdes_channel_set_bps_SGMII_ANOFF(ch, ETH_SERDES_BPS); #endif #endif } /* Step 11 */ for (ch = ETH_SERDES_XPCS_CH0; ch < ETH_SERDES_CH_NUM; ch++) { ETH_SERDES_SEL_BANK(ch, 0x0380); ETH_SERDES_REG_WRITE(ch, 0x03C0, 0x0); } /* Step 12 */ for (ch = ETH_SERDES_XPCS_CH0; ch < ETH_SERDES_CH_NUM; ch++) { ETH_SERDES_SEL_BANK(ch, 0x0380); ETH_SERDES_REG_WRITE(ch, 0x03D0, 0x0); } return 0; } Fullscreen rswitch.c Download #include "tpl_os.h" #include "utils.h" #include "rswitch.h" #include "rswitch_regs.h" #include "err_codes.h" #include "spider_utils.h" #include <string.h> #define __Drogon__ #ifdef __Drogon__ #define MPIC_MDC_CLK_SET (0x063f0000) #define MDIO_DEVAD_AN 7 #endif #define PORT_TSNA_N 3 #define PORT_GWCA_N 2 #define TOT_PORT_NUM (PORT_TSNA_N + PORT_GWCA_N) #define RSWITCH_GWCA_IDX_TO_HW_NUM(i) ((i) + PORT_TSNA_N) #define RSWITCH_HW_NUM_TO_GWCA_IDX(i) ((i) - PORT_TSNA_N) #define TSNA0_PORT_NUM 0 #define TSNA1_PORT_NUM 1 #define TSNA2_PORT_NUM 2 #define GWCA0_PORT_NUM 3 #define GWCA1_PORT_NUM 4 #define TSN_PORT_IN_USE TSNA0_PORT_NUM #define GWCA_PORT_IN_USE (GWCA1_PORT_NUM - PORT_TSNA_N) /* GWCA */ enum rswitch_gwca_mode { GWMC_OPC_RESET, GWMC_OPC_DISABLE, GWMC_OPC_CONFIG, GWMC_OPC_OPERATION, }; #define GWMS_OPS_MASK GWMC_OPC_OPERATION #define GWVCC_VEM_SC_TAG (0x3 << 16) #define GWMTIRM_MTIOG BIT(0) #define GWMTIRM_MTR BIT(1) #define GWARIRM_ARIOG BIT(0) #define GWARIRM_ARR BIT(1) #define GWDCC_BALR BIT(24) #define GWDCC_DCP(q, idx) ((q + (idx * 2)) << 16) #define GWDCC_DQT BIT(11) #define GWDCC_ETS BIT(9) #define GWDCC_EDE BIT(8) #define GWDCC_OFFS(chain) (GWDCC0 + (chain) * 4) enum DIE_DT { /* Frame data */ DT_FSINGLE = 0x80, DT_FSTART = 0x90, DT_FMID = 0xA0, DT_FEND = 0xB8, /* Chain control */ DT_LEMPTY = 0xC0, DT_EEMPTY = 0xD0, DT_LINKFIX = 0x00, DT_LINK = 0xE0, DT_EOS = 0xF0, /* HW/SW arbitration */ DT_FEMPTY = 0x40, DT_FEMPTY_IS = 0x10, DT_FEMPTY_IC = 0x20, DT_FEMPTY_ND = 0x38, DT_FEMPTY_START = 0x50, DT_FEMPTY_MID = 0x60, DT_FEMPTY_END = 0x70, DT_MASK = 0xF0, DIE = 0x08, /* Descriptor Interrupt Enable */ }; struct rswitch_desc { uint16 info_ds; /* Descriptor size */ uint8 die_dt; /* Descriptor interrupt enable and type */ uint8 dptrh; /* Descriptor pointer MSB */ uint32 dptrl; /* Descriptor pointer LSW */ } __attribute__((packed)); struct rswitch_ts_desc { uint16 info_ds; /* Descriptor size */ uint8 die_dt; /* Descriptor interrupt enable and type */ uint8 dptrh; /* Descriptor pointer MSB */ uint32 dptrl; /* Descriptor pointer LSW */ uint32 ts_nsec; uint32 ts_sec; } __attribute__((packed)); struct rswitch_ext_desc { uint16 info_ds; /* Descriptor size */ uint8 die_dt; /* Descriptor interrupt enable and type */ uint8 dptrh; /* Descriptor pointer MSB */ uint32 dptrl; /* Descriptor pointer LSW */ uint64 info1; } __attribute__((packed)); struct rswitch_ext_ts_desc { uint16 info_ds; /* Descriptor size */ uint8 die_dt; /* Descriptor interrupt enable and type */ uint8 dptrh; /* Descriptor pointer MSB */ uint32 dptrl; /* Descriptor pointer LSW */ uint64 info1; uint32 ts_nsec; uint32 ts_sec; } __attribute__((packed)); #define NUM_DEVICES 3 #define NUM_CHAINS_PER_NDEV 2 #define RSWITCH_MAX_NUM_CHAINS 128 #define RSWITCH_NUM_IRQ_REGS (RSWITCH_MAX_NUM_CHAINS / BITS_PER_TYPE(uint32)) #define MAC_ADDRESS_LEN 6 struct rswitch_etha { uint8 port_num; uint32 base_addr; uint8 mac_addr[MAC_ADDRESS_LEN]; int speed; } etha_props[PORT_TSNA_N] = { { .port_num = TSNA0_PORT_NUM, .base_addr = RSWITCH_ETHA0_ADDR }, { .port_num = TSNA1_PORT_NUM, .base_addr = RSWITCH_ETHA1_ADDR }, { .port_num = TSNA2_PORT_NUM, .base_addr = RSWITCH_ETHA2_ADDR }, }; enum rswitch_etha_mode { EAMC_OPC_RESET, EAMC_OPC_DISABLE, EAMC_OPC_CONFIG, EAMC_OPC_OPERATION, }; #define EAMS_OPS_MASK EAMC_OPC_OPERATION #define EAVCC_VEM_SC_TAG (0x3 << 16) #define MPIC_PIS_MII 0x00 #define MPIC_PIS_GMII 0x02 #define MPIC_PIS_XGMII 0x04 #define MPIC_LSC_SHIFT 3 #define MPIC_LSC_10M (0 << MPIC_LSC_SHIFT) #define MPIC_LSC_100M (1 << MPIC_LSC_SHIFT) #define MPIC_LSC_1G (2 << MPIC_LSC_SHIFT) #define MPIC_LSC_2_5G (3 << MPIC_LSC_SHIFT) #define MPIC_LSC_5G (4 << MPIC_LSC_SHIFT) #define MPIC_LSC_10G (5 << MPIC_LSC_SHIFT) #define MPIC_PSMCS_SHIFT 16 #define MPIC_PSMCS_MASK GENMASK(22, MPIC_PSMCS_SHIFT) #define MPIC_PSMCS(val) ((val) << MPIC_PSMCS_SHIFT) #define MPIC_PSMHT_SHIFT 24 #define MPIC_PSMHT_MASK GENMASK(26, MPIC_PSMHT_SHIFT) #define MPIC_PSMHT(val) ((val) << MPIC_PSMHT_SHIFT) #define MPSM_MFF_C45 BIT(2) #define MLVC_PLV BIT(16) #define MDIO_READ_C45 0x03 #define MDIO_WRITE_C45 0x01 #define MII_ADDR_C45 (1<<30) #define MII_DEVADDR_C45_SHIFT 16 #define MII_REGADDR_C45_MASK GENMASK(15, 0) #define MMIS1_PAACS BIT(2) /* Address */ #define MMIS1_PWACS BIT(1) /* Write */ #define MMIS1_PRACS BIT(0) /* Read */ #define MMIS1_CLEAR_FLAGS 0xf #define MPSM_PSME BIT(0) #define MPSM_MFF_C45 BIT(2) #define MPSM_PDA_SHIFT 3 #define MPSM_PDA_MASK GENMASK(7, MPSM_PDA_SHIFT) #define MPSM_PDA(val) ((val) << MPSM_PDA_SHIFT) #define MPSM_PRA_SHIFT 8 #define MPSM_PRA_MASK GENMASK(12, MPSM_PRA_SHIFT) #define MPSM_PRA(val) ((val) << MPSM_PRA_SHIFT) #define MPSM_POP_SHIFT 13 #define MPSM_POP_MASK GENMASK(14, MPSM_POP_SHIFT) #define MPSM_POP(val) ((val) << MPSM_POP_SHIFT) #define MPSM_PRD_SHIFT 16 #define MPSM_PRD_MASK GENMASK(31, MPSM_PRD_SHIFT) #define MPSM_PRD_WRITE(val) ((val) << MPSM_PRD_SHIFT) #define MPSM_PRD_READ(val) ((val) & MPSM_PRD_MASK >> MPSM_PRD_SHIFT) #define TX_RX_RING_SIZE 32 #define PKT_BUF_SZ 1584 #define PKT_BUF_SZ_ALIGN 1792 /* 0x700 to keep elements aligned to the value specified by RSWITCH_ALIGN */ #define RSWITCH_ALIGN 1 /* kenel uses 128, so 7 bits (=log_2(128)) are enough * to represent this value, so we round up to 1 byte. */ /* These elements should either be rswitch_ext_desc or rswitch_ext_ts_desc * depending if gptp is used or not. */ struct rswitch_ext_ts_desc rx_ring[TX_RX_RING_SIZE]; struct rswitch_ext_desc tx_ring[TX_RX_RING_SIZE]; uint8 rx_data_buff[TX_RX_RING_SIZE][PKT_BUF_SZ_ALIGN] __attribute__((aligned(RSWITCH_ALIGN))); uint8 tx_data_buff[TX_RX_RING_SIZE][PKT_BUF_SZ_ALIGN] __attribute__((aligned(RSWITCH_ALIGN))); struct rswitch_gwca_chain { uint8 chain_index; uint8 dir_tx; // bool gptp; union { struct rswitch_ext_desc *ring; struct rswitch_ext_ts_desc *ts_ring; }; uint32 num_ring; uint32 next_index; // next descriptor (as index) to be processed uint8 *data_buffers[TX_RX_RING_SIZE]; uint8 irq_triggered; }; struct rswitch_gwca { uint8 port_num; uint32 base_addr; struct rswitch_gwca_chain rx_chain; struct rswitch_gwca_chain tx_chain; int speed; } gwca_props[PORT_GWCA_N] = { { .port_num = GWCA0_PORT_NUM, .base_addr = RSWITCH_GWCA0_ADDR }, { .port_num = GWCA1_PORT_NUM, .base_addr = RSWITCH_GWCA1_ADDR }, }; struct rswitch_device { struct rswitch_desc base_descriptors[NUM_CHAINS_PER_NDEV]; struct rswitch_etha *etha; struct rswitch_gwca *gwca; uint32 fwd_base_addr; uint32 coma_base_addr; } rsw_dev = { .fwd_base_addr = RSWITCH_FWD_ADDR, .coma_base_addr = RSWITCH_COMA_ADDR, .etha = ða_props[TSN_PORT_IN_USE], .gwca = &gwca_props[GWCA_PORT_IN_USE], }; /* MFWD */ #define FWPC0_LTHTA BIT(0) #define FWPC0_IP4UE BIT(3) #define FWPC0_IP4TE BIT(4) #define FWPC0_IP4OE BIT(5) #define FWPC0_L2SE BIT(9) #define FWPC0_IP4EA BIT(10) #define FWPC0_IPDSA BIT(12) #define FWPC0_IPHLA BIT(18) #define FWPC0_MACSDA BIT(20) #define FWPC0_MACHLA BIT(26) #define FWPC0_MACHMA BIT(27) #define FWPC0_VLANSA BIT(28) #define FWPC0(i) (FWPC00 + (i) * 0x10) #define FWPC1(i) (FWPC10 + (i) * 0x10) #define FWPC0_DEFAULT (FWPC0_LTHTA | FWPC0_IP4UE | FWPC0_IP4TE | \ FWPC0_IP4OE | FWPC0_L2SE | FWPC0_IP4EA | \ FWPC0_IPDSA | FWPC0_IPHLA | FWPC0_MACSDA | \ FWPC0_MACHLA | FWPC0_MACHMA | FWPC0_VLANSA) #define FWPC1_DDE BIT(0) #define FWPBFC(i) (FWPBFCi + (i) * 0x10) #define FWPBFCSDC(j, i) (FWPBFCSDC00 + (i) * 0x10 + (j) * 0x04) /* Interrupts */ #define GWCA0_RX_TX_IRQ 312 #define GWCA1_RX_TX_IRQ 320 #define GWCA0_RX_TS_IRQ 328 #define GWCA1_RX_TS_IRQ 330 #define COMA_ERR_IRQ 290 #define GWCA0_ERR_IRQ 291 #define GWCA1_ERR_IRQ 292 #define ETHA0_ERR_IRQ 293 #define ETHA1_ERR_IRQ 294 #define ETHA2_ERR_IRQ 295 /* GWCA data interrupt status */ #define GWDIS0i_OFFSET(index) (GWDIS0 + (index/32) * 0x10) #define GWDIS0i_BIT(index) (BIT(index % 32)) /* Well, this shouldn't be here as it should be included from "tpl_os.h", but * that doesn't work, so we will redeclare it here... */ extern FUNC(void, OS_CODE) CallTerminateISR2(void); /* This is the callback function that is invoked every time some data is received */ rx_callback_fn rx_cb = NULL; #define RSWITCH_TIMEOUT_MS 10000 static int rswitch_reg_wait(uint32 addr, uint32 mask, uint32 expected) { int i; for (i = 0; i < RSWITCH_TIMEOUT_MS; i++) { if ((reg_read32(addr) & mask) == expected) return 0; ms_delay(1); } return ERR_TIMEOUT; } static void rswitch_modify(uint32 addr, uint32 clear, uint32 set) { uint32 val = reg_read32(addr) & ~clear; reg_write32(val | set, addr); } static int rswitch_mii_set_access(struct rswitch_etha *etha, uint8 read, uint32 phyad, uint32 devad, uint32 regad, uint16 *data) { int pop = read ? MDIO_READ_C45 : MDIO_WRITE_C45; uint32 val; int ret; /* Clear completion flags */ reg_write32(MMIS1_CLEAR_FLAGS, etha->base_addr + MMIS1); /* Submit address to PHY (MDIO_ADDR_C45 << 13) */ val = MPSM_PSME | MPSM_MFF_C45; reg_write32((regad << 16) | (devad << 8) | (phyad << 3) | val, etha->base_addr + MPSM); CHECK_RET(rswitch_reg_wait(etha->base_addr + MMIS1, MMIS1_PAACS, MMIS1_PAACS)); /* Clear address completion flag */ rswitch_modify(etha->base_addr + MMIS1, MMIS1_PAACS, MMIS1_PAACS); /* Read/Write PHY register */ if (read) { reg_write32((pop << 13) | (devad << 8) | (phyad << 3) | val, etha->base_addr + MPSM); CHECK_RET(rswitch_reg_wait(etha->base_addr + MMIS1, MMIS1_PRACS, MMIS1_PRACS)); /* Read data */ ret = (reg_read32(etha->base_addr + MPSM) & MPSM_PRD_MASK) >> 16; *data = ret; /* Clear read completion flag */ rswitch_modify(etha->base_addr + MMIS1, MMIS1_PRACS, MMIS1_PRACS); } else { reg_write32((*data << 16) | (pop << 13) | (devad << 8) | (phyad << 3) | val, etha->base_addr + MPSM); ret = rswitch_reg_wait(etha->base_addr + MMIS1, MMIS1_PWACS, MMIS1_PWACS); } return 0; } static int rswitch_mii_read(struct rswitch_etha *etha, uint32 phyaddr, uint32 devad, uint32 regad, uint32 *data) { uint16 tmp; int ret; ret = rswitch_mii_set_access(etha, TRUE, phyaddr, devad, regad, &tmp); *data = tmp; return ret; } static int rswitch_mii_write(struct rswitch_etha *etha, uint32 phyaddr, uint32 devad, uint32 regad, uint32 *data) { uint16 tmp = (uint16) *data; return rswitch_mii_set_access(etha, FALSE, phyaddr, devad, regad, &tmp); } static int rswitch_agent_clock_is_enabled(int port) { uint32 val = reg_read32(rsw_dev.coma_base_addr + RCEC); if (val & RCEC_RCE) return (val & BIT(port)) ? TRUE : FALSE; else return FALSE; } static void rswitch_agent_clock_ctrl(int port, int enable) { uint32 val; if (enable) { val = reg_read32(rsw_dev.coma_base_addr + RCEC); reg_write32(val | RCEC_RCE | BIT(port), rsw_dev.coma_base_addr + RCEC); } else { val = reg_read32(rsw_dev.coma_base_addr + RCDC); reg_write32(val | BIT(port), rsw_dev.coma_base_addr + RCDC); } } /* mac is supposed to be an array of 6 bytes */ static void __unused rswitch_etha_read_mac_address(struct rswitch_etha *eth_dev) { uint32 mrmac0 = reg_read32(eth_dev->base_addr + MRMAC0); uint32 mrmac1 = reg_read32(eth_dev->base_addr + MRMAC1); eth_dev->mac_addr[0] = (mrmac0 >> 8) & 0xFF; eth_dev->mac_addr[1] = (mrmac0 >> 0) & 0xFF; eth_dev->mac_addr[2] = (mrmac1 >> 24) & 0xFF; eth_dev->mac_addr[3] = (mrmac1 >> 16) & 0xFF; eth_dev->mac_addr[4] = (mrmac1 >> 8) & 0xFF; eth_dev->mac_addr[5] = (mrmac1 >> 0) & 0xFF; } /* This function sets only data in the global strucure, not on the hardware */ static void rswitch_etha_set_mac_address(struct rswitch_etha *eth_dev) { int i; uint32 tmp = 0; // Values from MCAL static const uint8 predefined_mac_address[PORT_TSNA_N][6] = { {0x74U, 0x90U, 0x50U, 0x00U, 0x00U, 0x00U}, {0x74U, 0x90U, 0x50U, 0x00U, 0x00U, 0x01U}, {0x74U, 0x90U, 0x50U, 0x00U, 0x00U, 0x02U}, }; /* The following works only because TSN port's numbers starts from 0 as * if they were indexes. */ for (i = 0; i < MAC_ADDRESS_LEN; i++) { eth_dev->mac_addr[i] = predefined_mac_address[eth_dev->port_num][i]; } tmp = (eth_dev->mac_addr[0] << 8) + (eth_dev->mac_addr[1]); reg_write32(tmp, eth_dev->base_addr + MRMAC0); tmp = (eth_dev->mac_addr[2] << 24) + (eth_dev->mac_addr[3] << 16) + (eth_dev->mac_addr[4] << 8) + (eth_dev->mac_addr[5]); reg_write32(tmp, eth_dev->base_addr + MRMAC1); } static void rswitch_soft_reset(void) { reg_write32(RRC_RR, rsw_dev.coma_base_addr + RRC); reg_write32(RRC_RR_CLR, rsw_dev.coma_base_addr + RRC); /* Clock initialization and module reset have already been done before * in utils.c */ } static int rswitch_gwca_change_mode(struct rswitch_gwca *gwca, enum rswitch_gwca_mode mode) { int ret; /* Enable clock */ if (!rswitch_agent_clock_is_enabled(gwca->port_num)) rswitch_agent_clock_ctrl(gwca->port_num, 1); reg_write32(mode, gwca->base_addr + GWMC); ret = rswitch_reg_wait(gwca->base_addr + GWMS, GWMS_OPS_MASK, mode); /* Disable clock */ if (mode == GWMC_OPC_DISABLE) rswitch_agent_clock_ctrl(gwca->port_num, 0); return ret; } static int rswitch_gwca_mcast_table_reset(struct rswitch_gwca *gwca) { reg_write32(GWMTIRM_MTIOG, gwca->base_addr + GWMTIRM); return rswitch_reg_wait(gwca->base_addr + GWMTIRM, GWMTIRM_MTR, GWMTIRM_MTR); } static int rswitch_gwca_axi_ram_reset(struct rswitch_gwca *gwca) { reg_write32(GWARIRM_ARIOG, gwca->base_addr + GWARIRM); return rswitch_reg_wait(gwca->base_addr + GWARIRM, GWARIRM_ARR, GWARIRM_ARR); } static void rswitch_gwca_set_rate_limit(struct rswitch_gwca *gwca) { uint32 gwgrlulc, gwgrlc; switch (gwca->speed) { case 1000: gwgrlulc = 0x0000005f; gwgrlc = 0x00010260; break; default: debug_msg("%s: This rate is not supported (%d)\n", __func__, gwca->speed); return; } //100_4_ASOP-C21-042_GWCA_IPSpec_UM_1_01. //GWCA Global Rate Limiter Upper Limit Configuration reg_write32(gwgrlulc, gwca->base_addr + GWGRLULC); //GWCA Global Rate Limiter Configuration reg_write32(gwgrlc, gwca->base_addr + GWGRLC); } static int rswitch_gwca_hw_init(struct rswitch_device *rswitch) { int ret, i; uint32 descriptors_addr = (uint32) rswitch->base_descriptors; struct rswitch_gwca *gwca = rswitch->gwca; for (i = 0; i < NUM_CHAINS_PER_NDEV; i++) { rswitch->base_descriptors[i].die_dt = DT_EOS; } CHECK_RET(rswitch_gwca_change_mode(gwca, GWMC_OPC_DISABLE)); CHECK_RET(rswitch_gwca_change_mode(gwca, GWMC_OPC_CONFIG)); CHECK_RET(rswitch_gwca_mcast_table_reset(gwca)); CHECK_RET(rswitch_gwca_axi_ram_reset(gwca)); /* Full setting flow */ reg_write32(GWVCC_VEM_SC_TAG, gwca->base_addr + GWVCC); reg_write32(0, gwca->base_addr + GWTTFC); reg_write32(descriptors_addr, gwca->base_addr + GWDCBAC1); reg_write32(0x00, gwca->base_addr + GWDCBAC0); #ifdef __Drogon__ gwca->speed = 2500; /*Disable Global rate limit*/ #else gwca->speed = 1000; rswitch_gwca_set_rate_limit(gwca); #endif CHECK_RET(rswitch_gwca_change_mode(gwca, GWMC_OPC_DISABLE)); CHECK_RET(rswitch_gwca_change_mode(gwca, GWMC_OPC_OPERATION)); return 0; } static void rswitch_gwca_chain_format(struct rswitch_device *rswitch, struct rswitch_gwca_chain *c) { struct rswitch_ext_desc *ring; struct rswitch_desc *base_desc; int ring_size = sizeof(*ring) * c->num_ring; int i; memset(c->ring, 0, ring_size); for (i = 0, ring = c->ring; i < c->num_ring - 1; i++, ring++) { if (!c->dir_tx) { ring->info_ds = PKT_BUF_SZ; ring->dptrl = (uint32) c->data_buffers[i]; ring->dptrh = 0x00; ring->die_dt = DT_FEMPTY | DIE; } else { ring->info_ds = 0; ring->dptrl = (uint32) c->data_buffers[i]; ring->dptrh = 0x00; ring->die_dt = DT_EEMPTY | DIE; } } /* Close the loop */ ring->dptrl = (uint32) c->ring; ring->dptrh = 0x00; ring->die_dt = DT_LINKFIX; /* Configure the base descriptor */ base_desc = &rswitch->base_descriptors[c->chain_index]; base_desc->die_dt = DT_LINKFIX; base_desc->dptrl = (uint32) c->ring; base_desc->dptrh = 0x00; /* FIXME: GWDCC_DCP */ reg_write32(GWDCC_BALR | (c->dir_tx ? GWDCC_DQT : 0) | GWDCC_EDE, rswitch->gwca->base_addr + GWDCC_OFFS(c->chain_index)); } static void rswitch_gwca_chain_ts_format(struct rswitch_device *rswitch, struct rswitch_gwca_chain *c) { struct rswitch_ext_ts_desc *ring; struct rswitch_desc *base_desc; int ring_size = sizeof(*ring) * c->num_ring; int i; memset(c->ring, 0, ring_size); for (i = 0, ring = c->ts_ring; i < c->num_ring - 1; i++, ring++) { if (!c->dir_tx) { ring->info_ds = PKT_BUF_SZ; ring->dptrl = (uint32) c->data_buffers[i]; ring->dptrh = 0x00; ring->die_dt = DT_FEMPTY | DIE; } else { ring->info_ds = 0; ring->dptrl = (uint32) c->data_buffers[i]; ring->dptrh = 0x00; ring->die_dt = DT_EEMPTY | DIE; } } /* Close the loop */ ring->dptrl = (uint32) c->ts_ring; ring->dptrh = 0x00; ring->die_dt = DT_LINKFIX; /* Configure the base descriptor */ base_desc = &rswitch->base_descriptors[c->chain_index]; base_desc->die_dt = DT_LINKFIX; base_desc->dptrl = (uint32) c->ts_ring; base_desc->dptrh = 0x00; /* FIXME: GWDCC_DCP */ reg_write32(GWDCC_BALR | (c->dir_tx ? GWDCC_DQT : 0) | GWDCC_ETS | GWDCC_EDE, rswitch->gwca->base_addr + GWDCC_OFFS(c->chain_index)); } static int rswitch_bpool_config(struct rswitch_device *rswitch) { uint32 val; val = reg_read32(rswitch->coma_base_addr + CABPIRM); if (val & CABPIRM_BPR) return 0; reg_write32(CABPIRM_BPIOG, rswitch->coma_base_addr + CABPIRM); return rswitch_reg_wait(rswitch->coma_base_addr + CABPIRM, CABPIRM_BPR, CABPIRM_BPR); } static void rswitch_fwd_init(struct rswitch_device *rswitch) { int eth_port_num = rswitch->etha->port_num; int gwca_port_num = rswitch->gwca->port_num; int gwca_idx = RSWITCH_HW_NUM_TO_GWCA_IDX(gwca_port_num); /* Set RX chain as destination for port based forwarding from the selected * ethernet port. */ reg_write32(rswitch->gwca->rx_chain.chain_index, rswitch->fwd_base_addr + FWPBFCSDC(gwca_idx, eth_port_num)); /* Static routing ETHA --> GWCA. */ reg_write32(BIT(rswitch->gwca->port_num), rswitch->fwd_base_addr + FWPBFC(eth_port_num)); /* Static routing GWCA --> ETHA. */ reg_write32(0, rswitch->fwd_base_addr + FWPBFC(gwca_port_num)); reg_write32(BIT(eth_port_num), rswitch->fwd_base_addr + FWPBFC(gwca_port_num)); } static __unused void rswitch_get_data_irq_status(struct rswitch_device *rswitch, uint32 *dis) { int i; for (i = 0; i < RSWITCH_NUM_IRQ_REGS; i++) { dis[i] = reg_read32(rswitch->gwca->base_addr + GWDIS0 + i * 0x10); } } static void rswitch_enadis_data_irq(struct rswitch_gwca *gwca, int index, int enable) { uint32 offs = (enable ? GWDIE0 : GWDID0) + (index / 32) * 0x10; uint32 tmp = 0; /* For VPF? */ if (enable) tmp = reg_read32(gwca->base_addr + offs); reg_write32(BIT(index % 32) | tmp, gwca->base_addr + offs); } static void rswitch_enable_irqs(void) { /* Enables IRQs on the core side */ enable_int(GWCA1_RX_TX_IRQ); enable_int(GWCA1_RX_TS_IRQ); enable_int(GWCA1_ERR_IRQ); enable_int(ETHA0_ERR_IRQ); enable_int(COMA_ERR_IRQ); } int rswitch_init(void) { int i, ret; memset(rx_data_buff, 0, sizeof(rx_data_buff)); memset(tx_data_buff, 0, sizeof(tx_data_buff)); // Enable clocks to all the agents for (i = 0; i < TOT_PORT_NUM; i++) { rswitch_agent_clock_ctrl(i, 1); } rswitch_soft_reset(); CHECK_RET(rswitch_gwca_hw_init(&rsw_dev)); /* Copy speed property from GWCA */ rsw_dev.etha->speed = rsw_dev.gwca->speed; rsw_dev.gwca->rx_chain.chain_index = 0; rsw_dev.gwca->rx_chain.ts_ring = rx_ring; for (i = 0; i < TX_RX_RING_SIZE; i++) { rsw_dev.gwca->rx_chain.data_buffers[i] = rx_data_buff[i]; } rsw_dev.gwca->rx_chain.num_ring = TX_RX_RING_SIZE; rsw_dev.gwca->rx_chain.dir_tx = 0; rswitch_gwca_chain_ts_format(&rsw_dev, &(rsw_dev.gwca->rx_chain)); rsw_dev.gwca->tx_chain.chain_index = 1; rsw_dev.gwca->tx_chain.ring = tx_ring; for (i = 0; i < TX_RX_RING_SIZE; i++) { rsw_dev.gwca->tx_chain.data_buffers[i] = tx_data_buff[i]; } rsw_dev.gwca->tx_chain.num_ring = TX_RX_RING_SIZE; rsw_dev.gwca->tx_chain.dir_tx = 1; rswitch_gwca_chain_format(&rsw_dev, &(rsw_dev.gwca->tx_chain)); CHECK_RET(rswitch_bpool_config(&rsw_dev)); rswitch_fwd_init(&rsw_dev); rswitch_enable_irqs(); return 0; } static int rswitch_etha_change_mode(struct rswitch_etha *etha, enum rswitch_etha_mode mode) { int ret; /* Enable clock */ if (!rswitch_agent_clock_is_enabled(etha->port_num)) rswitch_agent_clock_ctrl(etha->port_num, 1); reg_write32(mode, etha->base_addr + EAMC); ret = rswitch_reg_wait(etha->base_addr + EAMS, EAMS_OPS_MASK, mode); /* Disable clock */ if (mode == EAMC_OPC_DISABLE) rswitch_agent_clock_ctrl(etha->port_num, 0); return ret; } static void rswitch_rmac_setting(struct rswitch_etha *etha) { uint32 val; switch (etha->speed) { case 10: val = MPIC_LSC_10M; break; case 100: val = MPIC_LSC_100M; break; case 1000: val = MPIC_LSC_1G; break; #ifdef __Drogon__ case 2500: val = MPIC_LSC_2_5G; break; default: #endif return; } #ifdef __Drogon__ reg_write32(MPIC_PIS_XGMII | val, etha->base_addr + MPIC); #else reg_write32(MPIC_PIS_GMII | val, etha->base_addr + MPIC); #endif } #ifdef __Drogon__0 /*in uboot??*/ static void rswitch_etha_enable_usxgmii(struct rswitch_etha *etha) { rswitch_modify(etha->base_addr + MPIC, MPIC_PSMCS_MASK | MPIC_PSMHT_MASK, MPIC_PSMCS(0x7f) | MPIC_PSMHT(0x06)); rswitch_modify(etha->base_addr + MPSM, 0, MPSM_MFF_C45); } #else static void rswitch_etha_enable_mii(struct rswitch_etha *etha) { rswitch_modify(etha->base_addr + MPIC, MPIC_PSMCS_MASK | MPIC_PSMHT_MASK, MPIC_PSMCS(0x05) | MPIC_PSMHT(0x06)); rswitch_modify(etha->base_addr + MPSM, 0, MPSM_MFF_C45); } #endif static uint8 rswitch_etha_wait_link_verification(struct rswitch_etha *etha) { /* Request Link Verification */ reg_write32(MLVC_PLV, etha->base_addr + MLVC); return rswitch_reg_wait(etha->base_addr + MLVC, MLVC_PLV, 0); } static int rswitch_etha_hw_init(struct rswitch_etha *etha) { int ret; uint32 reg_val; /* Change to CONFIG Mode */ CHECK_RET(rswitch_etha_change_mode(etha, EAMC_OPC_DISABLE)); CHECK_RET(rswitch_etha_change_mode(etha, EAMC_OPC_CONFIG)); rswitch_etha_set_mac_address(etha); reg_write32(EAVCC_VEM_SC_TAG, etha->base_addr + EAVCC); rswitch_rmac_setting(etha); #ifdef __Drogon__0 //reg_write32(0x07E707E7, etha->base_addr + MRAFC); rswitch_etha_enable_usxgmii(etha); #else rswitch_etha_enable_mii(etha); #endif /* Change to OPERATION Mode */ CHECK_RET(rswitch_etha_change_mode(etha, EAMC_OPC_OPERATION)); /* Link Verification */ return rswitch_etha_wait_link_verification(etha); } static int rswitch_phy_init(struct rswitch_etha *etha) { uint32 reg_data; int ret; /* Set Mac Type: 000b = SXGMII */ /* Set Mac Type: 100b = 5.0GR/2.5GX/SGMII Auto-Negotiation On */ /* Set Mac Type: 101b = 5.0GR/2.5GX/SGMII Auto-Negotiation Off */ #ifdef __Drogon__0 uint16 LusMacType = 0; /* RSW2_SGMII_1G */ // CHECK_RET(rswitch_mii_read(etha, etha->port_num + 1, 31, 0xF000, ®_data)); // reg_data &= ~(0x3 << 6); // reg_data |= BIT(7); // CHECK_RET(rswitch_mii_write(etha, etha->port_num + 1, 1, 0xF000, ®_data)); #else uint16 LusMacType = 4; #endif /* Reset */ CHECK_RET(rswitch_mii_read(etha, etha->port_num + 1, 1, 0xC04A, ®_data)); reg_data |= BIT(15); CHECK_RET(rswitch_mii_write(etha, etha->port_num + 1, 1, 0xC04A, ®_data)); CHECK_RET(rswitch_mii_write(etha, etha->port_num + 1, 1, 0xC04A, ®_data)); /* MCAL does it twice... */ /* Check mode */ CHECK_RET(rswitch_mii_read(etha, etha->port_num + 1, 1, 0xC04A, ®_data)); if ((reg_data & 0x7) != LusMacType ) { /* 4 stands for SGMII 0 for ETH_USXGMII??*/// reg_data &= ~0x7; reg_data |= BIT(15) | LusMacType; CHECK_RET(rswitch_mii_write(etha, etha->port_num + 1, 1, 0xC04A, ®_data)); /* Run SERDES Init */ #ifdef __Drogon__0 CHECK_RET(rswitch_mii_read(etha, etha->port_num + 1, MDIO_DEVAD_AN, 0x800F, ®_data)); reg_data |= BIT(15) | BIT(13); CHECK_RET(rswitch_mii_write(etha, etha->port_num + 1, MDIO_DEVAD_AN, 0x800F, ®_data)); reg_data = 0x0U; do { CHECK_RET(rswitch_mii_read(etha, etha->port_num + 1, MDIO_DEVAD_AN, 0x800F, ®_data)); } while (reg_data & BIT(15)); /* Auto SERDES Init Disable */ reg_data &= ~BIT(13); CHECK_RET(rswitch_mii_write(etha, etha->port_num + 1, MDIO_DEVAD_AN, 0x800F, ®_data)); #else CHECK_RET(rswitch_mii_read(etha, etha->port_num + 1, 1, 0x800F, ®_data)); reg_data |= BIT(15) | BIT(13); CHECK_RET(rswitch_mii_write(etha, etha->port_num + 1, 1, 0x800F, ®_data)); reg_data = 0x0U; do { CHECK_RET(rswitch_mii_read(etha, etha->port_num + 1, 1, 0x800F, ®_data)); } while (reg_data & BIT(15)); /* Auto SERDES Init Disable */ reg_data &= ~BIT(13); CHECK_RET(rswitch_mii_write(etha, etha->port_num + 1, 1, 0x800F, ®_data)); #endif } return 0; } int rswitch_open(void) { int ret; CHECK_RET(rswitch_etha_hw_init(rsw_dev.etha)); CHECK_RET(rswitch_phy_init(rsw_dev.etha)); /* Enable RX */ rswitch_modify(rsw_dev.gwca->base_addr + GWTRC0, 0, BIT(rsw_dev.gwca->rx_chain.chain_index)); /* Enable interrupt */ rswitch_enadis_data_irq(rsw_dev.gwca, rsw_dev.gwca->tx_chain.chain_index, TRUE); rswitch_enadis_data_irq(rsw_dev.gwca, rsw_dev.gwca->rx_chain.chain_index, TRUE); return 0; } int rswitch_send_data(uint8 *buf, uint16 size) { struct rswitch_gwca_chain *chain = &rsw_dev.gwca->tx_chain; struct rswitch_ext_desc *desc; uint8 *dest_buf; /* Temporary code: try to fit into a single packet. This can be extended * in the future but it requires code to handle data splitting. */ if (size > PKT_BUF_SZ) { debug_msg("Error: transmitted buffer is too large"); return ERR_BUFF_TOO_LARGE; } /* Pick the 1st not used descriptor that can be used for transmission */ desc = &chain->ring[chain->next_index]; /* Check that this descriptor is REALLY free */ if (desc->die_dt != (DT_EEMPTY | DIE)) { debug_msg("Error: invalid descriptor selected for transmission"); return ERR_TX_FAILURE; } /* Prepare the descriptor */ desc->die_dt = DT_FSINGLE | DIE; desc->info_ds = size & 0xFFF; /* Copy data to be transmitted */ dest_buf = (uint8 *) desc->dptrl; memcpy(dest_buf, buf, size); /* Start transmission */ rswitch_modify(rsw_dev.gwca->base_addr + GWTRC0, 0, BIT(chain->chain_index)); /* Move to the next item in the list. */ chain->next_index++; /* The last item is a LINKFIX and we know that, so we wrap back * to the 1st item earlier. */ if (chain->next_index >= chain->num_ring - 1) { chain->next_index = 0; } return 0; } void rswitch_regiter_data_received_callback(rx_callback_fn func) { rx_cb = func; } /* Determine which chain (rx or tx) generated the interrupt */ static void rswitch_get_interrupt_source_and_clear() { struct rswitch_gwca_chain *chain; uint32 chain_index; uint32 reg_val; /* Check if the IRQ was triggered by the RX chain */ chain = &rsw_dev.gwca->rx_chain; chain_index = chain->chain_index; reg_val = reg_read32(rsw_dev.gwca->base_addr + GWDIS0i_OFFSET(chain_index)); if (reg_val & GWDIS0i_BIT(chain_index)) { chain->irq_triggered = 1; reg_write32(GWDIS0i_BIT(chain_index), rsw_dev.gwca->base_addr + GWDIS0i_OFFSET(chain_index)); } /* Check if the IRQ was triggered by the TX chain */ chain_index = rsw_dev.gwca->tx_chain.chain_index; reg_val = reg_read32(rsw_dev.gwca->base_addr + GWDIS0i_OFFSET(chain_index)); if (reg_val & GWDIS0i_BIT(chain_index)) { rsw_dev.gwca->tx_chain.irq_triggered = 1; reg_write32(GWDIS0i_BIT(chain_index), rsw_dev.gwca->base_addr + GWDIS0i_OFFSET(chain_index)); } } /*****************************************************************************/ /*****************************************************************************/ #define APP_ISR_gwca1_rx_tx_int_START_SEC_CODE #include "tpl_memmap.h" static int nb_int = 0; ISR(gwca1_rx_tx_int) { nb_int++; ActivateTask(gwca1_rx_tx_task); CallTerminateISR2(); } TASK(gwca1_rx_tx_task) { struct rswitch_gwca_chain *chain; struct rswitch_ext_ts_desc *ts_desc; struct rswitch_ext_desc *desc; uint8 *data_ptr; uint16 data_len; int i; // debug_msg("%s", __func__); while(1) { chain = &rsw_dev.gwca->rx_chain; if (chain->irq_triggered != 0) { /* Go through the descriptors chain to parse received data */ while (1) { invalidate_data_cache_by_address(rx_ring, sizeof(rx_ring)); ts_desc = &(chain->ts_ring[chain->next_index]); /* Stop once we get to a descriptor that was not modified */ if (ts_desc->die_dt == (DT_FEMPTY | DIE)) { break; } /* We know that "dptrh" is always 0x0 so we ignore it intentionally */ data_ptr = (uint8 *) ts_desc->dptrl; data_len = (ts_desc->info_ds) & 0xFFF; /* If the callback is present then call it, otherwise just print * the data */ if (rx_cb != NULL) { rx_cb(data_ptr, data_len); } else { debug_print_buffer(data_ptr, data_len); } /* Reset the data buffer */ memset(data_ptr, 0, PKT_BUF_SZ_ALIGN); /* Reset the descriptor so that it can be used again in the next round */ memset(ts_desc, 0, sizeof(*ts_desc)); ts_desc->die_dt = (DT_FEMPTY | DIE); ts_desc->info_ds = PKT_BUF_SZ; ts_desc->dptrl = (uint32) data_ptr; /* Move to the next item in the list. */ chain->next_index++; /* The last item is a LINKFIX and we know that, so we wrap back * to the 1st item earlier. */ if (chain->next_index >= chain->num_ring - 1) { chain->next_index = 0; } } /* Remove the flag for the received data. * Note = this is not the best technique because another IRQ might * be triggered in the meanwhile (is it possible?) so we might * miss it. Might be improved if problems appear... */ chain->irq_triggered = 0; } chain = &rsw_dev.gwca->tx_chain; if (chain->irq_triggered) { /* Here we reset all the descriptors and data buffers of the TX chain. * It works only if 1 descriptor is transmitted at a time. * TODO: improve to handle transmissions of multiple descriptors. */ for (i = 0; i < chain->num_ring - 1; i++) { desc = &(chain->ring[i]); desc->die_dt = DT_EEMPTY | DIE; desc->info_ds = 0; desc->info1 = 0; data_ptr = (uint8 *) desc->dptrl; memset(data_ptr, 0, PKT_BUF_SZ_ALIGN); } chain->irq_triggered = 0; } DisableAllInterrupts(); nb_int--; if (nb_int==0) break; EnableAllInterrupts(); } TerminateTask(); } FUNC(void, OS_CODE) GWCA1_RX_TX_INT_ClearFlag(void) { //debug_msg("%s", __func__); rswitch_get_interrupt_source_and_clear(); } #define APP_ISR_gwca1_rx_tx_int_STOP_SEC_CODE #include "tpl_memmap.h" /*****************************************************************************/ /*****************************************************************************/ #define APP_ISR_gwca1_rx_ts_int_START_SEC_CODE #include "tpl_memmap.h" ISR(gwca1_rx_ts_int) { debug_msg("%s", __func__); CallTerminateISR2(); } FUNC(void, OS_CODE) GWCA1_RX_TS_INT_ClearFlag(void) { debug_msg("%s", __func__); } #define APP_ISR_gwca1_rx_ts_int_STOP_SEC_CODE #include "tpl_memmap.h" /*****************************************************************************/ /*****************************************************************************/ #define APP_ISR_coma_err_int_START_SEC_CODE #include "tpl_memmap.h" ISR(coma_err_int) { debug_msg("%s", __func__); CallTerminateISR2(); } FUNC(void, OS_CODE) COMA_ERR_INT_ClearFlag(void) { debug_msg("%s", __func__); } #define APP_ISR_coma_err_int_STOP_SEC_CODE #include "tpl_memmap.h" /*****************************************************************************/ /*****************************************************************************/ #define APP_ISR_gwca1_err_int_START_SEC_CODE #include "tpl_memmap.h" ISR(gwca1_err_int) { debug_msg("%s", __func__); CallTerminateISR2(); } FUNC(void, OS_CODE) GWCA1_ERR_INT_ClearFlag(void) { debug_msg("%s", __func__); } #define APP_ISR_gwca1_err_int_STOP_SEC_CODE #include "tpl_memmap.h" /*****************************************************************************/ /*****************************************************************************/ #define APP_ISR_etha0_err_int_START_SEC_CODE #include "tpl_memmap.h" ISR(etha0_err_int) { debug_msg("%s", __func__); CallTerminateISR2(); } FUNC(void, OS_CODE) ETHA0_ERR_INT_ClearFlag(void) { debug_msg("%s", __func__); } #define APP_ISR_etha0_err_int_STOP_SEC_CODE #include "tpl_memmap.h"
#include "tpl_os.h" #include "eth_serdes.h" #include "err_codes.h" #include "utils.h" //#define ETH_SERDES_AN_ENABLED #define __Drogon__ #ifdef __Drogon__ //Max_Add #define BANK_180 0x0180 #define VR_XS_PMA_MP_12G_16G_25G_REF_CLK_CTRL 0x0244 #define VR_XS_PMA_MP_10G_MPLLA_CTRL2 0x01cc #define VR_XS_PMA_MP_12G_16G_MPLLA_CTRL0 0x01c4 #define VR_XS_PMA_MP_12G_MPLLA_CTRL1 0x01c8 #define VR_XS_PMA_MP_12G_MPLLA_CTRL3 0x01dc #define BANK_300 0x0300 #define BANK_380 0x0380 #define SR_XS_PCS_CTRL2 0x001c #define VR_XS_PCS_DEBUG_CTRL 0x0014 #define VR_XS_PCS_DIG_CTRL1 0x0000 #define VR_XS_PCS_KR_CTRL 0x001c #define VR_XS_PMA_MP_12G_16G_25G_MPLL_CMN_CTRL 0x01c0 #define VR_XS_PMA_MP_12G_16G_25G_VCO_CAL_LD0 0x0248 #define VR_XS_PMA_MP_12G_VCO_CAL_REF0 0x0258 #define VR_XS_PMA_MP_12G_16G_25G_RX_GENCTRL1 0x0144 #define VR_XS_PMA_CONSUMER_10G_RX_GENCTRL4 0x01a0 #define VR_XS_PMA_MP_12G_16G_25G_TX_RATE_CTRL 0x00d0 #define VR_XS_PMA_MP_12G_16G_25G_RX_RATE_CTRL 0x0150 #define VR_XS_PMA_MP_12G_16G_TX_GENCTRL2 0x00c8 #define VR_XS_PMA_MP_12G_16G_RX_GENCTRL2 0x0148 #define VR_XS_PMA_MP_12G_AFE_DFE_EN_CTRL 0x0174 #define VR_XS_PMA_MP_12G_RX_EQ_CTRL0 0x0160 #define VR_XS_PMA_MP_10G_RX_IQ_CTRL0 0x01ac #define VR_XS_PMA_MP_12G_16G_25G_TX_GENCTRL1 0x00c4 #define VR_XS_PMA_MP_12G_16G_25G_TX_EQ_CTRL0 0x00d8 #define VR_XS_PMA_MP_12G_16G_25G_TX_EQ_CTRL1 0x00dc #define SR_MII_CTRL 0x0000 #endif #define ETH_SERDES_CH_NUM 3 #define ETH_SERDES_XPCS_CH0 0UL #define ETH_SERDES_XPCS_CH1 1UL #define ETH_SERDES_XPCS_CH2 2UL /* Maximum Ethernet Timeout Count */ #define ETH_TIMEOUT_COUNT 63158UL /* Ethernet SERDES registers Base Address */ #define ETH_SERDES_XPCS0_BASE 0xE6444000UL #define ETH_SERDES_XPCS1_BASE 0xE6444400UL #define ETH_SERDES_XPCS2_BASE 0xE6444800UL /* Ehternet SERDES Bank Register Address */ #define ETH_SERDES_XPCS0_BANK 0xE64443FCUL #define ETH_SERDES_XPCS1_BANK 0xE64447FCUL #define ETH_SERDES_XPCS2_BANK 0xE6444BFCUL uint32 eth_serdes_base_addr[ETH_SERDES_CH_NUM] = { ETH_SERDES_XPCS0_BASE, ETH_SERDES_XPCS1_BASE, ETH_SERDES_XPCS2_BASE }; uint32 eth_serdes_bank_addr[ETH_SERDES_CH_NUM] = { ETH_SERDES_XPCS0_BANK, ETH_SERDES_XPCS1_BANK, ETH_SERDES_XPCS2_BANK }; typedef enum { ETH_MAC_LAYER_SPEED_10M = 0, ETH_MAC_LAYER_SPEED_100M, ETH_MAC_LAYER_SPEED_1G, ETH_MAC_LAYER_SPEED_2500M, ETH_MAC_LAYER_SPEED_10G } eth_serdes_speed_t; #ifdef __Drogon__ #define ETH_SERDES_BPS ETH_MAC_LAYER_SPEED_2500M #else #define ETH_SERDES_BPS ETH_MAC_LAYER_SPEED_1G #endif #define ETH_SERDES_SEL_BANK(ch, value) \ {*(volatile uint32*)eth_serdes_bank_addr[ch] = (uint32)value;} #define ETH_SERDES_REG_WRITE(ch, offset, value) \ {*(volatile uint32*)(eth_serdes_base_addr[ch] + (uint16)offset) = (uint32)value;} #define ETH_SERDES_REG_READ(ch, offset) \ (*(volatile uint32*)(eth_serdes_base_addr[ch] + (uint16)offset)) void eth_disable_fuse_ovr(void) { /* Disable Fuse_override_en */ uint32 address; volatile uint32 val; address = 0xE6446600; val = *((volatile uint32*)address); *((volatile uint32*)address) = 0x00000000U; val = *((volatile uint32*)address); (void) val; } static int eth_serdes_wait_for_update(uint32 ch, uint16 offset, uint32 mask, uint32 exp_val, uint32 timeout) { uint32 start_time; uint32 reg_val; start_time = get_time(); do { reg_val = ETH_SERDES_REG_READ(ch, offset); if ((reg_val & mask) == exp_val) { return 0; } } while (get_elapsed_time(start_time) < timeout); return ERR_TIMEOUT; } static int eth_serdes_wait_reset(void) { int ch, ret; for (ch = ETH_SERDES_XPCS_CH0; ch < ETH_SERDES_CH_NUM; ch++) { ETH_SERDES_SEL_BANK(ch, 0x0180U); ret = eth_serdes_wait_for_update(ch, 0x026C, BIT(0), BIT(0), ETH_TIMEOUT_COUNT); if (ret != 0) { break; } } return ret; } static int eth_serdes_initialize_SRAM(void) { int ch, ret; ret = eth_serdes_wait_reset(); if (ret != 0) { return ret; } for (ch = ETH_SERDES_XPCS_CH0; ch < ETH_SERDES_CH_NUM; ch++) { ETH_SERDES_SEL_BANK(ch, 0x0180); ETH_SERDES_REG_WRITE(ch, 0x026C, 0x3); } for (ch = ETH_SERDES_XPCS_CH0; ch < ETH_SERDES_CH_NUM; ch++) { ETH_SERDES_SEL_BANK(ch, 0x0300U); ret = eth_serdes_wait_for_update(ch, 0x0000U, BIT(15), 0UL, ETH_TIMEOUT_COUNT); if (ret != 0) { break; } } return ret; } #ifdef __Drogon__ static void eth_serdes_set_USXGMII_common_settings(void) { int ch; /* Steps U.4.1 to U.4.5 */ for (ch = ETH_SERDES_XPCS_CH0; ch < ETH_SERDES_CH_NUM; ch++) { ETH_SERDES_SEL_BANK(ch, BANK_180); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_16G_25G_REF_CLK_CTRL, 0x57); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_10G_MPLLA_CTRL2, 0xc200); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_16G_MPLLA_CTRL0, 0x42); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_MPLLA_CTRL1, 0x0); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_MPLLA_CTRL3, 0x2f); } } #else static void eth_serdes_set_SGMII_common_settings(void) { int ch; /* Steps S.4.1 to S.4.5 */ for (ch = ETH_SERDES_XPCS_CH0; ch < ETH_SERDES_CH_NUM; ch++) { ETH_SERDES_SEL_BANK(ch, 0x0180); ETH_SERDES_REG_WRITE(ch, 0x0244, 0x97); ETH_SERDES_REG_WRITE(ch, 0x01D0, 0x60); ETH_SERDES_REG_WRITE(ch, 0x01D8, 0x2200); ETH_SERDES_REG_WRITE(ch, 0x01D4, 0x0); ETH_SERDES_REG_WRITE(ch, 0x01E0, 0x3D); } } #endif static int eth_serdes_PHY_soft_reset(void) { int ch, ret; /* Step:5 */ for (ch = ETH_SERDES_XPCS_CH0; ch < ETH_SERDES_CH_NUM; ch++) { ETH_SERDES_SEL_BANK(ch, 0x0380); ETH_SERDES_REG_WRITE(ch, 0x0000, 0x8000); } /* Step:6 */ ret = eth_serdes_wait_reset(); if (ret != 0) { return ret; } /* Step:7 */ for (ch = ETH_SERDES_XPCS_CH0; ch < ETH_SERDES_CH_NUM; ch++) { ETH_SERDES_SEL_BANK(ch, 0x0180U); ETH_SERDES_REG_WRITE(ch, 0x026CU, 0x00000003UL); } /* Step:8 */ for (ch = ETH_SERDES_XPCS_CH0; ch < ETH_SERDES_CH_NUM; ch++) { ETH_SERDES_SEL_BANK(ch, 0x0380U); ret = eth_serdes_wait_for_update(ch, 0x0000U, BIT(15), 0UL, ETH_TIMEOUT_COUNT); if (ret != 0) { return ret; } } return 0; } #ifdef __Drogon__ static int eth_serdes_channel_USXGMII_common_configuration(uint32 ch){ int ret; ETH_SERDES_SEL_BANK(ch, BANK_300); ETH_SERDES_REG_WRITE(ch, VR_XS_PCS_KR_CTRL, 0x0); ETH_SERDES_SEL_BANK(ch, BANK_380); ETH_SERDES_REG_WRITE(ch, VR_XS_PCS_DEBUG_CTRL, 0x50); ETH_SERDES_REG_WRITE(ch, VR_XS_PCS_DIG_CTRL1, 0x2200); ETH_SERDES_REG_WRITE(ch, VR_XS_PCS_KR_CTRL, 0x400); ETH_SERDES_SEL_BANK(ch, BANK_180); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_16G_25G_MPLL_CMN_CTRL, 0x01); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_16G_25G_VCO_CAL_LD0, 0x56a); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_VCO_CAL_REF0, 0x15); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_16G_25G_RX_GENCTRL1, 0x1100); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_CONSUMER_10G_RX_GENCTRL4, 0x1); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_16G_25G_TX_RATE_CTRL, 0x1); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_16G_25G_RX_RATE_CTRL, 0x1); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_16G_TX_GENCTRL2, 0x300); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_16G_RX_GENCTRL2, 0x300); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_AFE_DFE_EN_CTRL, 0x0); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_RX_EQ_CTRL0, 0x04); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_10G_RX_IQ_CTRL0, 0x0); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_16G_25G_TX_GENCTRL1, 0x310); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_16G_TX_GENCTRL2, 0x0301); ret = eth_serdes_wait_for_update(ch, VR_XS_PMA_MP_12G_16G_TX_GENCTRL2, BIT(0), 0x0, ETH_TIMEOUT_COUNT); if (ret != 0) { return ret; } ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_16G_RX_GENCTRL2, 0x0301); ret = eth_serdes_wait_for_update(ch, VR_XS_PMA_MP_12G_16G_RX_GENCTRL2, BIT(0), 0x0, ETH_TIMEOUT_COUNT); if (ret != 0) { return ret; } ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_16G_25G_TX_GENCTRL1, 0x1310); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_16G_25G_TX_EQ_CTRL0, 0x1800); ETH_SERDES_REG_WRITE(ch, VR_XS_PMA_MP_12G_16G_25G_TX_EQ_CTRL1, 0x0); ETH_SERDES_SEL_BANK(ch, BANK_380); ETH_SERDES_REG_WRITE(ch, VR_XS_PCS_DIG_CTRL1, 0x2300); ret = eth_serdes_wait_for_update(ch, VR_XS_PCS_DIG_CTRL1, BIT(8), 0x0, ETH_TIMEOUT_COUNT); return ret; } #else static int eth_serdes_channel_SGMII_common_configuration(uint32 ch) { int ret; /* Steps S/SA.9.1 to S/SA.9.14 */ ETH_SERDES_SEL_BANK(ch, 0x0380); ETH_SERDES_REG_WRITE(ch, 0x0000, 0x2000); ETH_SERDES_SEL_BANK(ch, 0x0180); ETH_SERDES_REG_WRITE(ch, 0x01C0, 0x11); ETH_SERDES_REG_WRITE(ch, 0x0248, 0x540); ETH_SERDES_REG_WRITE(ch, 0x0258, 0x15); ETH_SERDES_REG_WRITE(ch, 0x0144, 0x100); ETH_SERDES_REG_WRITE(ch, 0x01A0, 0x0); ETH_SERDES_REG_WRITE(ch, 0x00D0, 0x2); ETH_SERDES_REG_WRITE(ch, 0x0150, 0x3); ETH_SERDES_REG_WRITE(ch, 0x00C8, 0x100); ETH_SERDES_REG_WRITE(ch, 0x0148, 0x100); ETH_SERDES_REG_WRITE(ch, 0x0174, 0x0); ETH_SERDES_REG_WRITE(ch, 0x0160, 0x7); ETH_SERDES_REG_WRITE(ch, 0x01AC, 0x0); ETH_SERDES_REG_WRITE(ch, 0x00C4, 0x310); /* Step: S/SA.9.15 */ ETH_SERDES_REG_WRITE(ch, 0x00C8, 0x101); /* Step: S/SA.9.16 */ ret = eth_serdes_wait_for_update(ch, 0x00C8, BIT(0), 0x0, ETH_TIMEOUT_COUNT); if (ret != 0) { return ret; } /* Step: S/SA.9.17 */ ETH_SERDES_REG_WRITE(ch, 0x0148, 0x101); /* Step: S/SA.9.18 */ ret = eth_serdes_wait_for_update(ch, 0x0148, BIT(0), 0x0, ETH_TIMEOUT_COUNT); if (ret != 0) { return ret; } /* Steps S/SA.9.19 to S/SA.9.24 */ ETH_SERDES_REG_WRITE(ch, 0x00C4, 0x1310); ETH_SERDES_REG_WRITE(ch, 0x00D8, 0x1800); ETH_SERDES_REG_WRITE(ch, 0x00DC, 0x0); ETH_SERDES_SEL_BANK(ch, 0x0300); ETH_SERDES_REG_WRITE(ch, 0x001C, 0x1); ETH_SERDES_SEL_BANK(ch, 0x0380); ETH_SERDES_REG_WRITE(ch, 0x0000, 0x2100); ret = eth_serdes_wait_for_update(ch, 0x0000, BIT(8), 0x0, ETH_TIMEOUT_COUNT); return ret; } #endif #if defined(ETH_SERDES_AN_ENABLED) static void eth_serdes_channel_SGMII_enable_AN(uint32 ch, eth_serdes_speed_t LenSpeed) { /* Step: SA.9.25 */ ETH_SERDES_SEL_BANK(ch, 0x1F00); if (ETH_MAC_LAYER_SPEED_1G == ch) { /* select when 1Gbps */ ETH_SERDES_REG_WRITE(ch, 0x0000, 0x140); } else { /* select when 100Mbps */ ETH_SERDES_REG_WRITE(ch, 0x0000, 0x2100); } /* Steps SA.9.26 to SA.9.28 */ ETH_SERDES_SEL_BANK(ch, 0x1F80); ETH_SERDES_REG_WRITE(ch, 0x0004, 0x005); ETH_SERDES_REG_WRITE(ch, 0x0028, 0x7A1); ETH_SERDES_REG_WRITE(ch, 0x0000, 0x208); } #endif #if defined(ETH_SERDES_AN_ENABLED) static int eth_serdes_channel_set_bps_SGMII_ANON(uint32 ch, eth_serdes_speed_t LenSpeed) { int ret; /* Step: SA.10.1 */ ETH_SERDES_SEL_BANK(ch, 0x1F00); /* Mode Selection Method is provisional */ if (ETH_MAC_LAYER_SPEED_1G == LenSpeed) { /* select 1Gbps */ ETH_SERDES_REG_WRITE(ch, 0x0000, 0x1140); } else { /* select 100Mbps */ ETH_SERDES_REG_WRITE(ch, 0x0000, 0x3100); } /* Step: SA.10.2 */ ETH_SERDES_SEL_BANK(ch, 0x1F80); ret = eth_serdes_wait_for_update(ch, 0x0008, BIT(0), BIT(0), ETH_TIMEOUT_COUNT); if (ret != 0) { return ret; } ETH_SERDES_REG_WRITE(ch, 0x0008, 0x0); return ret; } #endif #ifdef __Drogon__ //u10.1 static void eth_serdes_channel_set_bps_USXGMII_ANOFF(uint32 ch, eth_serdes_speed_t LenSpeed) { if(ETH_MAC_LAYER_SPEED_2500M==LenSpeed){ /* Step:U.10.1 USXGMII - 2.5Gbps */ ETH_SERDES_SEL_BANK(ch, 0x1F00); ETH_SERDES_REG_WRITE(ch, SR_MII_CTRL, 0x120); } /* Step:U.10.2 Wait for the required amount of time (>10us).*/ ms_delay(10); /* Step:U.10.3*/ ETH_SERDES_SEL_BANK(ch, 0x0380); ETH_SERDES_REG_WRITE(ch, SR_MII_CTRL, 0x2600); eth_serdes_wait_for_update(ch, 0x0000, BIT(10), 0x0, ETH_TIMEOUT_COUNT); } #else static void eth_serdes_channel_set_bps_SGMII_ANOFF(uint32 ch, eth_serdes_speed_t LenSpeed) { /* Step:S.10.1 */ ETH_SERDES_SEL_BANK(ch, 0x1F00); /* Mode Selection Method is provisional */ if (ETH_MAC_LAYER_SPEED_1G == LenSpeed) { /* select 1Gbps */ ETH_SERDES_REG_WRITE(ch, 0x0000, 0x140); } else { /* select 100Mbps */ ETH_SERDES_REG_WRITE(ch, 0x0000, 0x2100); } } #endif /* Following User's Manual at section 100.6.3 */ int eth_serdes_initialize(void) { int ret, ch; /* Step 1: Initialize SRAM */ ret = eth_serdes_initialize_SRAM(); if (ret != 0) { return ret; } /* Step 2 */ for (ch = ETH_SERDES_XPCS_CH0; ch < ETH_SERDES_CH_NUM; ch++) { ETH_SERDES_SEL_BANK(ch, 0x0380); ETH_SERDES_REG_WRITE(ch, 0x03D4, 0x443); } /* Step 3 */ #ifdef __Drogon__ eth_serdes_set_USXGMII_common_settings(); #else eth_serdes_set_SGMII_common_settings(); #endif /* Step 4 */ for (ch = ETH_SERDES_XPCS_CH0; ch < ETH_SERDES_CH_NUM; ch++) { ETH_SERDES_SEL_BANK(ch, 0x0380); ETH_SERDES_REG_WRITE(ch, 0X03D0, 0x1); } /* Steps 5 to 8 */ ret = eth_serdes_PHY_soft_reset(); if (ret != 0) { return ret; } /* Step 9 */ for (ch = ETH_SERDES_XPCS_CH0; ch < ETH_SERDES_CH_NUM; ch++) { #ifdef __Drogon__ /* Steps U.9.1 to U.9.26 */ ret = eth_serdes_channel_USXGMII_common_configuration(ch); #else /* Steps S/SA.9.1 to S/SA.9.24 */ ret = eth_serdes_channel_SGMII_common_configuration(ch); #endif if (ret != 0) { return ret; } #if defined(ETH_SERDES_AN_ENABLED) /* Steps SA.9.25 to SA.9.28 */ eth_serdes_channel_SGMII_enable_AN(ch, ETH_SERDES_BPS); #endif } /* Step 10: */ for (ch = ETH_SERDES_XPCS_CH0; ch < ETH_SERDES_CH_NUM; ch++) { #if defined(ETH_SERDES_AN_ENABLED) /* Steps SA.10.1 to SA.10.3 */ ret = eth_serdes_channel_set_bps_SGMII_ANON(ch, ETH_SERDES_BPS); if (ret != 0) { return ret; } #else #ifdef __Drogon__ /*Step: U10.1 to U10.4*/ eth_serdes_channel_set_bps_USXGMII_ANOFF(ch, ETH_SERDES_BPS); #else /* Step: S.10.1 */ eth_serdes_channel_set_bps_SGMII_ANOFF(ch, ETH_SERDES_BPS); #endif #endif } /* Step 11 */ for (ch = ETH_SERDES_XPCS_CH0; ch < ETH_SERDES_CH_NUM; ch++) { ETH_SERDES_SEL_BANK(ch, 0x0380); ETH_SERDES_REG_WRITE(ch, 0x03C0, 0x0); } /* Step 12 */ for (ch = ETH_SERDES_XPCS_CH0; ch < ETH_SERDES_CH_NUM; ch++) { ETH_SERDES_SEL_BANK(ch, 0x0380); ETH_SERDES_REG_WRITE(ch, 0x03D0, 0x0); } return 0; }
#include "tpl_os.h" #include "utils.h" #include "rswitch.h" #include "rswitch_regs.h" #include "err_codes.h" #include "spider_utils.h" #include <string.h> #define __Drogon__ #ifdef __Drogon__ #define MPIC_MDC_CLK_SET (0x063f0000) #define MDIO_DEVAD_AN 7 #endif #define PORT_TSNA_N 3 #define PORT_GWCA_N 2 #define TOT_PORT_NUM (PORT_TSNA_N + PORT_GWCA_N) #define RSWITCH_GWCA_IDX_TO_HW_NUM(i) ((i) + PORT_TSNA_N) #define RSWITCH_HW_NUM_TO_GWCA_IDX(i) ((i) - PORT_TSNA_N) #define TSNA0_PORT_NUM 0 #define TSNA1_PORT_NUM 1 #define TSNA2_PORT_NUM 2 #define GWCA0_PORT_NUM 3 #define GWCA1_PORT_NUM 4 #define TSN_PORT_IN_USE TSNA0_PORT_NUM #define GWCA_PORT_IN_USE (GWCA1_PORT_NUM - PORT_TSNA_N) /* GWCA */ enum rswitch_gwca_mode { GWMC_OPC_RESET, GWMC_OPC_DISABLE, GWMC_OPC_CONFIG, GWMC_OPC_OPERATION, }; #define GWMS_OPS_MASK GWMC_OPC_OPERATION #define GWVCC_VEM_SC_TAG (0x3 << 16) #define GWMTIRM_MTIOG BIT(0) #define GWMTIRM_MTR BIT(1) #define GWARIRM_ARIOG BIT(0) #define GWARIRM_ARR BIT(1) #define GWDCC_BALR BIT(24) #define GWDCC_DCP(q, idx) ((q + (idx * 2)) << 16) #define GWDCC_DQT BIT(11) #define GWDCC_ETS BIT(9) #define GWDCC_EDE BIT(8) #define GWDCC_OFFS(chain) (GWDCC0 + (chain) * 4) enum DIE_DT { /* Frame data */ DT_FSINGLE = 0x80, DT_FSTART = 0x90, DT_FMID = 0xA0, DT_FEND = 0xB8, /* Chain control */ DT_LEMPTY = 0xC0, DT_EEMPTY = 0xD0, DT_LINKFIX = 0x00, DT_LINK = 0xE0, DT_EOS = 0xF0, /* HW/SW arbitration */ DT_FEMPTY = 0x40, DT_FEMPTY_IS = 0x10, DT_FEMPTY_IC = 0x20, DT_FEMPTY_ND = 0x38, DT_FEMPTY_START = 0x50, DT_FEMPTY_MID = 0x60, DT_FEMPTY_END = 0x70, DT_MASK = 0xF0, DIE = 0x08, /* Descriptor Interrupt Enable */ }; struct rswitch_desc { uint16 info_ds; /* Descriptor size */ uint8 die_dt; /* Descriptor interrupt enable and type */ uint8 dptrh; /* Descriptor pointer MSB */ uint32 dptrl; /* Descriptor pointer LSW */ } __attribute__((packed)); struct rswitch_ts_desc { uint16 info_ds; /* Descriptor size */ uint8 die_dt; /* Descriptor interrupt enable and type */ uint8 dptrh; /* Descriptor pointer MSB */ uint32 dptrl; /* Descriptor pointer LSW */ uint32 ts_nsec; uint32 ts_sec; } __attribute__((packed)); struct rswitch_ext_desc { uint16 info_ds; /* Descriptor size */ uint8 die_dt; /* Descriptor interrupt enable and type */ uint8 dptrh; /* Descriptor pointer MSB */ uint32 dptrl; /* Descriptor pointer LSW */ uint64 info1; } __attribute__((packed)); struct rswitch_ext_ts_desc { uint16 info_ds; /* Descriptor size */ uint8 die_dt; /* Descriptor interrupt enable and type */ uint8 dptrh; /* Descriptor pointer MSB */ uint32 dptrl; /* Descriptor pointer LSW */ uint64 info1; uint32 ts_nsec; uint32 ts_sec; } __attribute__((packed)); #define NUM_DEVICES 3 #define NUM_CHAINS_PER_NDEV 2 #define RSWITCH_MAX_NUM_CHAINS 128 #define RSWITCH_NUM_IRQ_REGS (RSWITCH_MAX_NUM_CHAINS / BITS_PER_TYPE(uint32)) #define MAC_ADDRESS_LEN 6 struct rswitch_etha { uint8 port_num; uint32 base_addr; uint8 mac_addr[MAC_ADDRESS_LEN]; int speed; } etha_props[PORT_TSNA_N] = { { .port_num = TSNA0_PORT_NUM, .base_addr = RSWITCH_ETHA0_ADDR }, { .port_num = TSNA1_PORT_NUM, .base_addr = RSWITCH_ETHA1_ADDR }, { .port_num = TSNA2_PORT_NUM, .base_addr = RSWITCH_ETHA2_ADDR }, }; enum rswitch_etha_mode { EAMC_OPC_RESET, EAMC_OPC_DISABLE, EAMC_OPC_CONFIG, EAMC_OPC_OPERATION, }; #define EAMS_OPS_MASK EAMC_OPC_OPERATION #define EAVCC_VEM_SC_TAG (0x3 << 16) #define MPIC_PIS_MII 0x00 #define MPIC_PIS_GMII 0x02 #define MPIC_PIS_XGMII 0x04 #define MPIC_LSC_SHIFT 3 #define MPIC_LSC_10M (0 << MPIC_LSC_SHIFT) #define MPIC_LSC_100M (1 << MPIC_LSC_SHIFT) #define MPIC_LSC_1G (2 << MPIC_LSC_SHIFT) #define MPIC_LSC_2_5G (3 << MPIC_LSC_SHIFT) #define MPIC_LSC_5G (4 << MPIC_LSC_SHIFT) #define MPIC_LSC_10G (5 << MPIC_LSC_SHIFT) #define MPIC_PSMCS_SHIFT 16 #define MPIC_PSMCS_MASK GENMASK(22, MPIC_PSMCS_SHIFT) #define MPIC_PSMCS(val) ((val) << MPIC_PSMCS_SHIFT) #define MPIC_PSMHT_SHIFT 24 #define MPIC_PSMHT_MASK GENMASK(26, MPIC_PSMHT_SHIFT) #define MPIC_PSMHT(val) ((val) << MPIC_PSMHT_SHIFT) #define MPSM_MFF_C45 BIT(2) #define MLVC_PLV BIT(16) #define MDIO_READ_C45 0x03 #define MDIO_WRITE_C45 0x01 #define MII_ADDR_C45 (1<<30) #define MII_DEVADDR_C45_SHIFT 16 #define MII_REGADDR_C45_MASK GENMASK(15, 0) #define MMIS1_PAACS BIT(2) /* Address */ #define MMIS1_PWACS BIT(1) /* Write */ #define MMIS1_PRACS BIT(0) /* Read */ #define MMIS1_CLEAR_FLAGS 0xf #define MPSM_PSME BIT(0) #define MPSM_MFF_C45 BIT(2) #define MPSM_PDA_SHIFT 3 #define MPSM_PDA_MASK GENMASK(7, MPSM_PDA_SHIFT) #define MPSM_PDA(val) ((val) << MPSM_PDA_SHIFT) #define MPSM_PRA_SHIFT 8 #define MPSM_PRA_MASK GENMASK(12, MPSM_PRA_SHIFT) #define MPSM_PRA(val) ((val) << MPSM_PRA_SHIFT) #define MPSM_POP_SHIFT 13 #define MPSM_POP_MASK GENMASK(14, MPSM_POP_SHIFT) #define MPSM_POP(val) ((val) << MPSM_POP_SHIFT) #define MPSM_PRD_SHIFT 16 #define MPSM_PRD_MASK GENMASK(31, MPSM_PRD_SHIFT) #define MPSM_PRD_WRITE(val) ((val) << MPSM_PRD_SHIFT) #define MPSM_PRD_READ(val) ((val) & MPSM_PRD_MASK >> MPSM_PRD_SHIFT) #define TX_RX_RING_SIZE 32 #define PKT_BUF_SZ 1584 #define PKT_BUF_SZ_ALIGN 1792 /* 0x700 to keep elements aligned to the value specified by RSWITCH_ALIGN */ #define RSWITCH_ALIGN 1 /* kenel uses 128, so 7 bits (=log_2(128)) are enough * to represent this value, so we round up to 1 byte. */ /* These elements should either be rswitch_ext_desc or rswitch_ext_ts_desc * depending if gptp is used or not. */ struct rswitch_ext_ts_desc rx_ring[TX_RX_RING_SIZE]; struct rswitch_ext_desc tx_ring[TX_RX_RING_SIZE]; uint8 rx_data_buff[TX_RX_RING_SIZE][PKT_BUF_SZ_ALIGN] __attribute__((aligned(RSWITCH_ALIGN))); uint8 tx_data_buff[TX_RX_RING_SIZE][PKT_BUF_SZ_ALIGN] __attribute__((aligned(RSWITCH_ALIGN))); struct rswitch_gwca_chain { uint8 chain_index; uint8 dir_tx; // bool gptp; union { struct rswitch_ext_desc *ring; struct rswitch_ext_ts_desc *ts_ring; }; uint32 num_ring; uint32 next_index; // next descriptor (as index) to be processed uint8 *data_buffers[TX_RX_RING_SIZE]; uint8 irq_triggered; }; struct rswitch_gwca { uint8 port_num; uint32 base_addr; struct rswitch_gwca_chain rx_chain; struct rswitch_gwca_chain tx_chain; int speed; } gwca_props[PORT_GWCA_N] = { { .port_num = GWCA0_PORT_NUM, .base_addr = RSWITCH_GWCA0_ADDR }, { .port_num = GWCA1_PORT_NUM, .base_addr = RSWITCH_GWCA1_ADDR }, }; struct rswitch_device { struct rswitch_desc base_descriptors[NUM_CHAINS_PER_NDEV]; struct rswitch_etha *etha; struct rswitch_gwca *gwca; uint32 fwd_base_addr; uint32 coma_base_addr; } rsw_dev = { .fwd_base_addr = RSWITCH_FWD_ADDR, .coma_base_addr = RSWITCH_COMA_ADDR, .etha = ða_props[TSN_PORT_IN_USE], .gwca = &gwca_props[GWCA_PORT_IN_USE], }; /* MFWD */ #define FWPC0_LTHTA BIT(0) #define FWPC0_IP4UE BIT(3) #define FWPC0_IP4TE BIT(4) #define FWPC0_IP4OE BIT(5) #define FWPC0_L2SE BIT(9) #define FWPC0_IP4EA BIT(10) #define FWPC0_IPDSA BIT(12) #define FWPC0_IPHLA BIT(18) #define FWPC0_MACSDA BIT(20) #define FWPC0_MACHLA BIT(26) #define FWPC0_MACHMA BIT(27) #define FWPC0_VLANSA BIT(28) #define FWPC0(i) (FWPC00 + (i) * 0x10) #define FWPC1(i) (FWPC10 + (i) * 0x10) #define FWPC0_DEFAULT (FWPC0_LTHTA | FWPC0_IP4UE | FWPC0_IP4TE | \ FWPC0_IP4OE | FWPC0_L2SE | FWPC0_IP4EA | \ FWPC0_IPDSA | FWPC0_IPHLA | FWPC0_MACSDA | \ FWPC0_MACHLA | FWPC0_MACHMA | FWPC0_VLANSA) #define FWPC1_DDE BIT(0) #define FWPBFC(i) (FWPBFCi + (i) * 0x10) #define FWPBFCSDC(j, i) (FWPBFCSDC00 + (i) * 0x10 + (j) * 0x04) /* Interrupts */ #define GWCA0_RX_TX_IRQ 312 #define GWCA1_RX_TX_IRQ 320 #define GWCA0_RX_TS_IRQ 328 #define GWCA1_RX_TS_IRQ 330 #define COMA_ERR_IRQ 290 #define GWCA0_ERR_IRQ 291 #define GWCA1_ERR_IRQ 292 #define ETHA0_ERR_IRQ 293 #define ETHA1_ERR_IRQ 294 #define ETHA2_ERR_IRQ 295 /* GWCA data interrupt status */ #define GWDIS0i_OFFSET(index) (GWDIS0 + (index/32) * 0x10) #define GWDIS0i_BIT(index) (BIT(index % 32)) /* Well, this shouldn't be here as it should be included from "tpl_os.h", but * that doesn't work, so we will redeclare it here... */ extern FUNC(void, OS_CODE) CallTerminateISR2(void); /* This is the callback function that is invoked every time some data is received */ rx_callback_fn rx_cb = NULL; #define RSWITCH_TIMEOUT_MS 10000 static int rswitch_reg_wait(uint32 addr, uint32 mask, uint32 expected) { int i; for (i = 0; i < RSWITCH_TIMEOUT_MS; i++) { if ((reg_read32(addr) & mask) == expected) return 0; ms_delay(1); } return ERR_TIMEOUT; } static void rswitch_modify(uint32 addr, uint32 clear, uint32 set) { uint32 val = reg_read32(addr) & ~clear; reg_write32(val | set, addr); } static int rswitch_mii_set_access(struct rswitch_etha *etha, uint8 read, uint32 phyad, uint32 devad, uint32 regad, uint16 *data) { int pop = read ? MDIO_READ_C45 : MDIO_WRITE_C45; uint32 val; int ret; /* Clear completion flags */ reg_write32(MMIS1_CLEAR_FLAGS, etha->base_addr + MMIS1); /* Submit address to PHY (MDIO_ADDR_C45 << 13) */ val = MPSM_PSME | MPSM_MFF_C45; reg_write32((regad << 16) | (devad << 8) | (phyad << 3) | val, etha->base_addr + MPSM); CHECK_RET(rswitch_reg_wait(etha->base_addr + MMIS1, MMIS1_PAACS, MMIS1_PAACS)); /* Clear address completion flag */ rswitch_modify(etha->base_addr + MMIS1, MMIS1_PAACS, MMIS1_PAACS); /* Read/Write PHY register */ if (read) { reg_write32((pop << 13) | (devad << 8) | (phyad << 3) | val, etha->base_addr + MPSM); CHECK_RET(rswitch_reg_wait(etha->base_addr + MMIS1, MMIS1_PRACS, MMIS1_PRACS)); /* Read data */ ret = (reg_read32(etha->base_addr + MPSM) & MPSM_PRD_MASK) >> 16; *data = ret; /* Clear read completion flag */ rswitch_modify(etha->base_addr + MMIS1, MMIS1_PRACS, MMIS1_PRACS); } else { reg_write32((*data << 16) | (pop << 13) | (devad << 8) | (phyad << 3) | val, etha->base_addr + MPSM); ret = rswitch_reg_wait(etha->base_addr + MMIS1, MMIS1_PWACS, MMIS1_PWACS); } return 0; } static int rswitch_mii_read(struct rswitch_etha *etha, uint32 phyaddr, uint32 devad, uint32 regad, uint32 *data) { uint16 tmp; int ret; ret = rswitch_mii_set_access(etha, TRUE, phyaddr, devad, regad, &tmp); *data = tmp; return ret; } static int rswitch_mii_write(struct rswitch_etha *etha, uint32 phyaddr, uint32 devad, uint32 regad, uint32 *data) { uint16 tmp = (uint16) *data; return rswitch_mii_set_access(etha, FALSE, phyaddr, devad, regad, &tmp); } static int rswitch_agent_clock_is_enabled(int port) { uint32 val = reg_read32(rsw_dev.coma_base_addr + RCEC); if (val & RCEC_RCE) return (val & BIT(port)) ? TRUE : FALSE; else return FALSE; } static void rswitch_agent_clock_ctrl(int port, int enable) { uint32 val; if (enable) { val = reg_read32(rsw_dev.coma_base_addr + RCEC); reg_write32(val | RCEC_RCE | BIT(port), rsw_dev.coma_base_addr + RCEC); } else { val = reg_read32(rsw_dev.coma_base_addr + RCDC); reg_write32(val | BIT(port), rsw_dev.coma_base_addr + RCDC); } } /* mac is supposed to be an array of 6 bytes */ static void __unused rswitch_etha_read_mac_address(struct rswitch_etha *eth_dev) { uint32 mrmac0 = reg_read32(eth_dev->base_addr + MRMAC0); uint32 mrmac1 = reg_read32(eth_dev->base_addr + MRMAC1); eth_dev->mac_addr[0] = (mrmac0 >> 8) & 0xFF; eth_dev->mac_addr[1] = (mrmac0 >> 0) & 0xFF; eth_dev->mac_addr[2] = (mrmac1 >> 24) & 0xFF; eth_dev->mac_addr[3] = (mrmac1 >> 16) & 0xFF; eth_dev->mac_addr[4] = (mrmac1 >> 8) & 0xFF; eth_dev->mac_addr[5] = (mrmac1 >> 0) & 0xFF; } /* This function sets only data in the global strucure, not on the hardware */ static void rswitch_etha_set_mac_address(struct rswitch_etha *eth_dev) { int i; uint32 tmp = 0; // Values from MCAL static const uint8 predefined_mac_address[PORT_TSNA_N][6] = { {0x74U, 0x90U, 0x50U, 0x00U, 0x00U, 0x00U}, {0x74U, 0x90U, 0x50U, 0x00U, 0x00U, 0x01U}, {0x74U, 0x90U, 0x50U, 0x00U, 0x00U, 0x02U}, }; /* The following works only because TSN port's numbers starts from 0 as * if they were indexes. */ for (i = 0; i < MAC_ADDRESS_LEN; i++) { eth_dev->mac_addr[i] = predefined_mac_address[eth_dev->port_num][i]; } tmp = (eth_dev->mac_addr[0] << 8) + (eth_dev->mac_addr[1]); reg_write32(tmp, eth_dev->base_addr + MRMAC0); tmp = (eth_dev->mac_addr[2] << 24) + (eth_dev->mac_addr[3] << 16) + (eth_dev->mac_addr[4] << 8) + (eth_dev->mac_addr[5]); reg_write32(tmp, eth_dev->base_addr + MRMAC1); } static void rswitch_soft_reset(void) { reg_write32(RRC_RR, rsw_dev.coma_base_addr + RRC); reg_write32(RRC_RR_CLR, rsw_dev.coma_base_addr + RRC); /* Clock initialization and module reset have already been done before * in utils.c */ } static int rswitch_gwca_change_mode(struct rswitch_gwca *gwca, enum rswitch_gwca_mode mode) { int ret; /* Enable clock */ if (!rswitch_agent_clock_is_enabled(gwca->port_num)) rswitch_agent_clock_ctrl(gwca->port_num, 1); reg_write32(mode, gwca->base_addr + GWMC); ret = rswitch_reg_wait(gwca->base_addr + GWMS, GWMS_OPS_MASK, mode); /* Disable clock */ if (mode == GWMC_OPC_DISABLE) rswitch_agent_clock_ctrl(gwca->port_num, 0); return ret; } static int rswitch_gwca_mcast_table_reset(struct rswitch_gwca *gwca) { reg_write32(GWMTIRM_MTIOG, gwca->base_addr + GWMTIRM); return rswitch_reg_wait(gwca->base_addr + GWMTIRM, GWMTIRM_MTR, GWMTIRM_MTR); } static int rswitch_gwca_axi_ram_reset(struct rswitch_gwca *gwca) { reg_write32(GWARIRM_ARIOG, gwca->base_addr + GWARIRM); return rswitch_reg_wait(gwca->base_addr + GWARIRM, GWARIRM_ARR, GWARIRM_ARR); } static void rswitch_gwca_set_rate_limit(struct rswitch_gwca *gwca) { uint32 gwgrlulc, gwgrlc; switch (gwca->speed) { case 1000: gwgrlulc = 0x0000005f; gwgrlc = 0x00010260; break; default: debug_msg("%s: This rate is not supported (%d)\n", __func__, gwca->speed); return; } //100_4_ASOP-C21-042_GWCA_IPSpec_UM_1_01. //GWCA Global Rate Limiter Upper Limit Configuration reg_write32(gwgrlulc, gwca->base_addr + GWGRLULC); //GWCA Global Rate Limiter Configuration reg_write32(gwgrlc, gwca->base_addr + GWGRLC); } static int rswitch_gwca_hw_init(struct rswitch_device *rswitch) { int ret, i; uint32 descriptors_addr = (uint32) rswitch->base_descriptors; struct rswitch_gwca *gwca = rswitch->gwca; for (i = 0; i < NUM_CHAINS_PER_NDEV; i++) { rswitch->base_descriptors[i].die_dt = DT_EOS; } CHECK_RET(rswitch_gwca_change_mode(gwca, GWMC_OPC_DISABLE)); CHECK_RET(rswitch_gwca_change_mode(gwca, GWMC_OPC_CONFIG)); CHECK_RET(rswitch_gwca_mcast_table_reset(gwca)); CHECK_RET(rswitch_gwca_axi_ram_reset(gwca)); /* Full setting flow */ reg_write32(GWVCC_VEM_SC_TAG, gwca->base_addr + GWVCC); reg_write32(0, gwca->base_addr + GWTTFC); reg_write32(descriptors_addr, gwca->base_addr + GWDCBAC1); reg_write32(0x00, gwca->base_addr + GWDCBAC0); #ifdef __Drogon__ gwca->speed = 2500; /*Disable Global rate limit*/ #else gwca->speed = 1000; rswitch_gwca_set_rate_limit(gwca); #endif CHECK_RET(rswitch_gwca_change_mode(gwca, GWMC_OPC_DISABLE)); CHECK_RET(rswitch_gwca_change_mode(gwca, GWMC_OPC_OPERATION)); return 0; } static void rswitch_gwca_chain_format(struct rswitch_device *rswitch, struct rswitch_gwca_chain *c) { struct rswitch_ext_desc *ring; struct rswitch_desc *base_desc; int ring_size = sizeof(*ring) * c->num_ring; int i; memset(c->ring, 0, ring_size); for (i = 0, ring = c->ring; i < c->num_ring - 1; i++, ring++) { if (!c->dir_tx) { ring->info_ds = PKT_BUF_SZ; ring->dptrl = (uint32) c->data_buffers[i]; ring->dptrh = 0x00; ring->die_dt = DT_FEMPTY | DIE; } else { ring->info_ds = 0; ring->dptrl = (uint32) c->data_buffers[i]; ring->dptrh = 0x00; ring->die_dt = DT_EEMPTY | DIE; } } /* Close the loop */ ring->dptrl = (uint32) c->ring; ring->dptrh = 0x00; ring->die_dt = DT_LINKFIX; /* Configure the base descriptor */ base_desc = &rswitch->base_descriptors[c->chain_index]; base_desc->die_dt = DT_LINKFIX; base_desc->dptrl = (uint32) c->ring; base_desc->dptrh = 0x00; /* FIXME: GWDCC_DCP */ reg_write32(GWDCC_BALR | (c->dir_tx ? GWDCC_DQT : 0) | GWDCC_EDE, rswitch->gwca->base_addr + GWDCC_OFFS(c->chain_index)); } static void rswitch_gwca_chain_ts_format(struct rswitch_device *rswitch, struct rswitch_gwca_chain *c) { struct rswitch_ext_ts_desc *ring; struct rswitch_desc *base_desc; int ring_size = sizeof(*ring) * c->num_ring; int i; memset(c->ring, 0, ring_size); for (i = 0, ring = c->ts_ring; i < c->num_ring - 1; i++, ring++) { if (!c->dir_tx) { ring->info_ds = PKT_BUF_SZ; ring->dptrl = (uint32) c->data_buffers[i]; ring->dptrh = 0x00; ring->die_dt = DT_FEMPTY | DIE; } else { ring->info_ds = 0; ring->dptrl = (uint32) c->data_buffers[i]; ring->dptrh = 0x00; ring->die_dt = DT_EEMPTY | DIE; } } /* Close the loop */ ring->dptrl = (uint32) c->ts_ring; ring->dptrh = 0x00; ring->die_dt = DT_LINKFIX; /* Configure the base descriptor */ base_desc = &rswitch->base_descriptors[c->chain_index]; base_desc->die_dt = DT_LINKFIX; base_desc->dptrl = (uint32) c->ts_ring; base_desc->dptrh = 0x00; /* FIXME: GWDCC_DCP */ reg_write32(GWDCC_BALR | (c->dir_tx ? GWDCC_DQT : 0) | GWDCC_ETS | GWDCC_EDE, rswitch->gwca->base_addr + GWDCC_OFFS(c->chain_index)); } static int rswitch_bpool_config(struct rswitch_device *rswitch) { uint32 val; val = reg_read32(rswitch->coma_base_addr + CABPIRM); if (val & CABPIRM_BPR) return 0; reg_write32(CABPIRM_BPIOG, rswitch->coma_base_addr + CABPIRM); return rswitch_reg_wait(rswitch->coma_base_addr + CABPIRM, CABPIRM_BPR, CABPIRM_BPR); } static void rswitch_fwd_init(struct rswitch_device *rswitch) { int eth_port_num = rswitch->etha->port_num; int gwca_port_num = rswitch->gwca->port_num; int gwca_idx = RSWITCH_HW_NUM_TO_GWCA_IDX(gwca_port_num); /* Set RX chain as destination for port based forwarding from the selected * ethernet port. */ reg_write32(rswitch->gwca->rx_chain.chain_index, rswitch->fwd_base_addr + FWPBFCSDC(gwca_idx, eth_port_num)); /* Static routing ETHA --> GWCA. */ reg_write32(BIT(rswitch->gwca->port_num), rswitch->fwd_base_addr + FWPBFC(eth_port_num)); /* Static routing GWCA --> ETHA. */ reg_write32(0, rswitch->fwd_base_addr + FWPBFC(gwca_port_num)); reg_write32(BIT(eth_port_num), rswitch->fwd_base_addr + FWPBFC(gwca_port_num)); } static __unused void rswitch_get_data_irq_status(struct rswitch_device *rswitch, uint32 *dis) { int i; for (i = 0; i < RSWITCH_NUM_IRQ_REGS; i++) { dis[i] = reg_read32(rswitch->gwca->base_addr + GWDIS0 + i * 0x10); } } static void rswitch_enadis_data_irq(struct rswitch_gwca *gwca, int index, int enable) { uint32 offs = (enable ? GWDIE0 : GWDID0) + (index / 32) * 0x10; uint32 tmp = 0; /* For VPF? */ if (enable) tmp = reg_read32(gwca->base_addr + offs); reg_write32(BIT(index % 32) | tmp, gwca->base_addr + offs); } static void rswitch_enable_irqs(void) { /* Enables IRQs on the core side */ enable_int(GWCA1_RX_TX_IRQ); enable_int(GWCA1_RX_TS_IRQ); enable_int(GWCA1_ERR_IRQ); enable_int(ETHA0_ERR_IRQ); enable_int(COMA_ERR_IRQ); } int rswitch_init(void) { int i, ret; memset(rx_data_buff, 0, sizeof(rx_data_buff)); memset(tx_data_buff, 0, sizeof(tx_data_buff)); // Enable clocks to all the agents for (i = 0; i < TOT_PORT_NUM; i++) { rswitch_agent_clock_ctrl(i, 1); } rswitch_soft_reset(); CHECK_RET(rswitch_gwca_hw_init(&rsw_dev)); /* Copy speed property from GWCA */ rsw_dev.etha->speed = rsw_dev.gwca->speed; rsw_dev.gwca->rx_chain.chain_index = 0; rsw_dev.gwca->rx_chain.ts_ring = rx_ring; for (i = 0; i < TX_RX_RING_SIZE; i++) { rsw_dev.gwca->rx_chain.data_buffers[i] = rx_data_buff[i]; } rsw_dev.gwca->rx_chain.num_ring = TX_RX_RING_SIZE; rsw_dev.gwca->rx_chain.dir_tx = 0; rswitch_gwca_chain_ts_format(&rsw_dev, &(rsw_dev.gwca->rx_chain)); rsw_dev.gwca->tx_chain.chain_index = 1; rsw_dev.gwca->tx_chain.ring = tx_ring; for (i = 0; i < TX_RX_RING_SIZE; i++) { rsw_dev.gwca->tx_chain.data_buffers[i] = tx_data_buff[i]; } rsw_dev.gwca->tx_chain.num_ring = TX_RX_RING_SIZE; rsw_dev.gwca->tx_chain.dir_tx = 1; rswitch_gwca_chain_format(&rsw_dev, &(rsw_dev.gwca->tx_chain)); CHECK_RET(rswitch_bpool_config(&rsw_dev)); rswitch_fwd_init(&rsw_dev); rswitch_enable_irqs(); return 0; } static int rswitch_etha_change_mode(struct rswitch_etha *etha, enum rswitch_etha_mode mode) { int ret; /* Enable clock */ if (!rswitch_agent_clock_is_enabled(etha->port_num)) rswitch_agent_clock_ctrl(etha->port_num, 1); reg_write32(mode, etha->base_addr + EAMC); ret = rswitch_reg_wait(etha->base_addr + EAMS, EAMS_OPS_MASK, mode); /* Disable clock */ if (mode == EAMC_OPC_DISABLE) rswitch_agent_clock_ctrl(etha->port_num, 0); return ret; } static void rswitch_rmac_setting(struct rswitch_etha *etha) { uint32 val; switch (etha->speed) { case 10: val = MPIC_LSC_10M; break; case 100: val = MPIC_LSC_100M; break; case 1000: val = MPIC_LSC_1G; break; #ifdef __Drogon__ case 2500: val = MPIC_LSC_2_5G; break; default: #endif return; } #ifdef __Drogon__ reg_write32(MPIC_PIS_XGMII | val, etha->base_addr + MPIC); #else reg_write32(MPIC_PIS_GMII | val, etha->base_addr + MPIC); #endif } #ifdef __Drogon__0 /*in uboot??*/ static void rswitch_etha_enable_usxgmii(struct rswitch_etha *etha) { rswitch_modify(etha->base_addr + MPIC, MPIC_PSMCS_MASK | MPIC_PSMHT_MASK, MPIC_PSMCS(0x7f) | MPIC_PSMHT(0x06)); rswitch_modify(etha->base_addr + MPSM, 0, MPSM_MFF_C45); } #else static void rswitch_etha_enable_mii(struct rswitch_etha *etha) { rswitch_modify(etha->base_addr + MPIC, MPIC_PSMCS_MASK | MPIC_PSMHT_MASK, MPIC_PSMCS(0x05) | MPIC_PSMHT(0x06)); rswitch_modify(etha->base_addr + MPSM, 0, MPSM_MFF_C45); } #endif static uint8 rswitch_etha_wait_link_verification(struct rswitch_etha *etha) { /* Request Link Verification */ reg_write32(MLVC_PLV, etha->base_addr + MLVC); return rswitch_reg_wait(etha->base_addr + MLVC, MLVC_PLV, 0); } static int rswitch_etha_hw_init(struct rswitch_etha *etha) { int ret; uint32 reg_val; /* Change to CONFIG Mode */ CHECK_RET(rswitch_etha_change_mode(etha, EAMC_OPC_DISABLE)); CHECK_RET(rswitch_etha_change_mode(etha, EAMC_OPC_CONFIG)); rswitch_etha_set_mac_address(etha); reg_write32(EAVCC_VEM_SC_TAG, etha->base_addr + EAVCC); rswitch_rmac_setting(etha); #ifdef __Drogon__0 //reg_write32(0x07E707E7, etha->base_addr + MRAFC); rswitch_etha_enable_usxgmii(etha); #else rswitch_etha_enable_mii(etha); #endif /* Change to OPERATION Mode */ CHECK_RET(rswitch_etha_change_mode(etha, EAMC_OPC_OPERATION)); /* Link Verification */ return rswitch_etha_wait_link_verification(etha); } static int rswitch_phy_init(struct rswitch_etha *etha) { uint32 reg_data; int ret; /* Set Mac Type: 000b = SXGMII */ /* Set Mac Type: 100b = 5.0GR/2.5GX/SGMII Auto-Negotiation On */ /* Set Mac Type: 101b = 5.0GR/2.5GX/SGMII Auto-Negotiation Off */ #ifdef __Drogon__0 uint16 LusMacType = 0; /* RSW2_SGMII_1G */ // CHECK_RET(rswitch_mii_read(etha, etha->port_num + 1, 31, 0xF000, ®_data)); // reg_data &= ~(0x3 << 6); // reg_data |= BIT(7); // CHECK_RET(rswitch_mii_write(etha, etha->port_num + 1, 1, 0xF000, ®_data)); #else uint16 LusMacType = 4; #endif /* Reset */ CHECK_RET(rswitch_mii_read(etha, etha->port_num + 1, 1, 0xC04A, ®_data)); reg_data |= BIT(15); CHECK_RET(rswitch_mii_write(etha, etha->port_num + 1, 1, 0xC04A, ®_data)); CHECK_RET(rswitch_mii_write(etha, etha->port_num + 1, 1, 0xC04A, ®_data)); /* MCAL does it twice... */ /* Check mode */ CHECK_RET(rswitch_mii_read(etha, etha->port_num + 1, 1, 0xC04A, ®_data)); if ((reg_data & 0x7) != LusMacType ) { /* 4 stands for SGMII 0 for ETH_USXGMII??*/// reg_data &= ~0x7; reg_data |= BIT(15) | LusMacType; CHECK_RET(rswitch_mii_write(etha, etha->port_num + 1, 1, 0xC04A, ®_data)); /* Run SERDES Init */ #ifdef __Drogon__0 CHECK_RET(rswitch_mii_read(etha, etha->port_num + 1, MDIO_DEVAD_AN, 0x800F, ®_data)); reg_data |= BIT(15) | BIT(13); CHECK_RET(rswitch_mii_write(etha, etha->port_num + 1, MDIO_DEVAD_AN, 0x800F, ®_data)); reg_data = 0x0U; do { CHECK_RET(rswitch_mii_read(etha, etha->port_num + 1, MDIO_DEVAD_AN, 0x800F, ®_data)); } while (reg_data & BIT(15)); /* Auto SERDES Init Disable */ reg_data &= ~BIT(13); CHECK_RET(rswitch_mii_write(etha, etha->port_num + 1, MDIO_DEVAD_AN, 0x800F, ®_data)); #else CHECK_RET(rswitch_mii_read(etha, etha->port_num + 1, 1, 0x800F, ®_data)); reg_data |= BIT(15) | BIT(13); CHECK_RET(rswitch_mii_write(etha, etha->port_num + 1, 1, 0x800F, ®_data)); reg_data = 0x0U; do { CHECK_RET(rswitch_mii_read(etha, etha->port_num + 1, 1, 0x800F, ®_data)); } while (reg_data & BIT(15)); /* Auto SERDES Init Disable */ reg_data &= ~BIT(13); CHECK_RET(rswitch_mii_write(etha, etha->port_num + 1, 1, 0x800F, ®_data)); #endif } return 0; } int rswitch_open(void) { int ret; CHECK_RET(rswitch_etha_hw_init(rsw_dev.etha)); CHECK_RET(rswitch_phy_init(rsw_dev.etha)); /* Enable RX */ rswitch_modify(rsw_dev.gwca->base_addr + GWTRC0, 0, BIT(rsw_dev.gwca->rx_chain.chain_index)); /* Enable interrupt */ rswitch_enadis_data_irq(rsw_dev.gwca, rsw_dev.gwca->tx_chain.chain_index, TRUE); rswitch_enadis_data_irq(rsw_dev.gwca, rsw_dev.gwca->rx_chain.chain_index, TRUE); return 0; } int rswitch_send_data(uint8 *buf, uint16 size) { struct rswitch_gwca_chain *chain = &rsw_dev.gwca->tx_chain; struct rswitch_ext_desc *desc; uint8 *dest_buf; /* Temporary code: try to fit into a single packet. This can be extended * in the future but it requires code to handle data splitting. */ if (size > PKT_BUF_SZ) { debug_msg("Error: transmitted buffer is too large"); return ERR_BUFF_TOO_LARGE; } /* Pick the 1st not used descriptor that can be used for transmission */ desc = &chain->ring[chain->next_index]; /* Check that this descriptor is REALLY free */ if (desc->die_dt != (DT_EEMPTY | DIE)) { debug_msg("Error: invalid descriptor selected for transmission"); return ERR_TX_FAILURE; } /* Prepare the descriptor */ desc->die_dt = DT_FSINGLE | DIE; desc->info_ds = size & 0xFFF; /* Copy data to be transmitted */ dest_buf = (uint8 *) desc->dptrl; memcpy(dest_buf, buf, size); /* Start transmission */ rswitch_modify(rsw_dev.gwca->base_addr + GWTRC0, 0, BIT(chain->chain_index)); /* Move to the next item in the list. */ chain->next_index++; /* The last item is a LINKFIX and we know that, so we wrap back * to the 1st item earlier. */ if (chain->next_index >= chain->num_ring - 1) { chain->next_index = 0; } return 0; } void rswitch_regiter_data_received_callback(rx_callback_fn func) { rx_cb = func; } /* Determine which chain (rx or tx) generated the interrupt */ static void rswitch_get_interrupt_source_and_clear() { struct rswitch_gwca_chain *chain; uint32 chain_index; uint32 reg_val; /* Check if the IRQ was triggered by the RX chain */ chain = &rsw_dev.gwca->rx_chain; chain_index = chain->chain_index; reg_val = reg_read32(rsw_dev.gwca->base_addr + GWDIS0i_OFFSET(chain_index)); if (reg_val & GWDIS0i_BIT(chain_index)) { chain->irq_triggered = 1; reg_write32(GWDIS0i_BIT(chain_index), rsw_dev.gwca->base_addr + GWDIS0i_OFFSET(chain_index)); } /* Check if the IRQ was triggered by the TX chain */ chain_index = rsw_dev.gwca->tx_chain.chain_index; reg_val = reg_read32(rsw_dev.gwca->base_addr + GWDIS0i_OFFSET(chain_index)); if (reg_val & GWDIS0i_BIT(chain_index)) { rsw_dev.gwca->tx_chain.irq_triggered = 1; reg_write32(GWDIS0i_BIT(chain_index), rsw_dev.gwca->base_addr + GWDIS0i_OFFSET(chain_index)); } } /*****************************************************************************/ /*****************************************************************************/ #define APP_ISR_gwca1_rx_tx_int_START_SEC_CODE #include "tpl_memmap.h" static int nb_int = 0; ISR(gwca1_rx_tx_int) { nb_int++; ActivateTask(gwca1_rx_tx_task); CallTerminateISR2(); } TASK(gwca1_rx_tx_task) { struct rswitch_gwca_chain *chain; struct rswitch_ext_ts_desc *ts_desc; struct rswitch_ext_desc *desc; uint8 *data_ptr; uint16 data_len; int i; // debug_msg("%s", __func__); while(1) { chain = &rsw_dev.gwca->rx_chain; if (chain->irq_triggered != 0) { /* Go through the descriptors chain to parse received data */ while (1) { invalidate_data_cache_by_address(rx_ring, sizeof(rx_ring)); ts_desc = &(chain->ts_ring[chain->next_index]); /* Stop once we get to a descriptor that was not modified */ if (ts_desc->die_dt == (DT_FEMPTY | DIE)) { break; } /* We know that "dptrh" is always 0x0 so we ignore it intentionally */ data_ptr = (uint8 *) ts_desc->dptrl; data_len = (ts_desc->info_ds) & 0xFFF; /* If the callback is present then call it, otherwise just print * the data */ if (rx_cb != NULL) { rx_cb(data_ptr, data_len); } else { debug_print_buffer(data_ptr, data_len); } /* Reset the data buffer */ memset(data_ptr, 0, PKT_BUF_SZ_ALIGN); /* Reset the descriptor so that it can be used again in the next round */ memset(ts_desc, 0, sizeof(*ts_desc)); ts_desc->die_dt = (DT_FEMPTY | DIE); ts_desc->info_ds = PKT_BUF_SZ; ts_desc->dptrl = (uint32) data_ptr; /* Move to the next item in the list. */ chain->next_index++; /* The last item is a LINKFIX and we know that, so we wrap back * to the 1st item earlier. */ if (chain->next_index >= chain->num_ring - 1) { chain->next_index = 0; } } /* Remove the flag for the received data. * Note = this is not the best technique because another IRQ might * be triggered in the meanwhile (is it possible?) so we might * miss it. Might be improved if problems appear... */ chain->irq_triggered = 0; } chain = &rsw_dev.gwca->tx_chain; if (chain->irq_triggered) { /* Here we reset all the descriptors and data buffers of the TX chain. * It works only if 1 descriptor is transmitted at a time. * TODO: improve to handle transmissions of multiple descriptors. */ for (i = 0; i < chain->num_ring - 1; i++) { desc = &(chain->ring[i]); desc->die_dt = DT_EEMPTY | DIE; desc->info_ds = 0; desc->info1 = 0; data_ptr = (uint8 *) desc->dptrl; memset(data_ptr, 0, PKT_BUF_SZ_ALIGN); } chain->irq_triggered = 0; } DisableAllInterrupts(); nb_int--; if (nb_int==0) break; EnableAllInterrupts(); } TerminateTask(); } FUNC(void, OS_CODE) GWCA1_RX_TX_INT_ClearFlag(void) { //debug_msg("%s", __func__); rswitch_get_interrupt_source_and_clear(); } #define APP_ISR_gwca1_rx_tx_int_STOP_SEC_CODE #include "tpl_memmap.h" /*****************************************************************************/ /*****************************************************************************/ #define APP_ISR_gwca1_rx_ts_int_START_SEC_CODE #include "tpl_memmap.h" ISR(gwca1_rx_ts_int) { debug_msg("%s", __func__); CallTerminateISR2(); } FUNC(void, OS_CODE) GWCA1_RX_TS_INT_ClearFlag(void) { debug_msg("%s", __func__); } #define APP_ISR_gwca1_rx_ts_int_STOP_SEC_CODE #include "tpl_memmap.h" /*****************************************************************************/ /*****************************************************************************/ #define APP_ISR_coma_err_int_START_SEC_CODE #include "tpl_memmap.h" ISR(coma_err_int) { debug_msg("%s", __func__); CallTerminateISR2(); } FUNC(void, OS_CODE) COMA_ERR_INT_ClearFlag(void) { debug_msg("%s", __func__); } #define APP_ISR_coma_err_int_STOP_SEC_CODE #include "tpl_memmap.h" /*****************************************************************************/ /*****************************************************************************/ #define APP_ISR_gwca1_err_int_START_SEC_CODE #include "tpl_memmap.h" ISR(gwca1_err_int) { debug_msg("%s", __func__); CallTerminateISR2(); } FUNC(void, OS_CODE) GWCA1_ERR_INT_ClearFlag(void) { debug_msg("%s", __func__); } #define APP_ISR_gwca1_err_int_STOP_SEC_CODE #include "tpl_memmap.h" /*****************************************************************************/ /*****************************************************************************/ #define APP_ISR_etha0_err_int_START_SEC_CODE #include "tpl_memmap.h" ISR(etha0_err_int) { debug_msg("%s", __func__); CallTerminateISR2(); } FUNC(void, OS_CODE) ETHA0_ERR_INT_ClearFlag(void) { debug_msg("%s", __func__); } #define APP_ISR_etha0_err_int_STOP_SEC_CODE #include "tpl_memmap.h"
Hi Willh,
Unfortunately, RSwitch documentation is not porvided in public if I understand correctly.Thus, we can not discuss this topic in the forum.
If you want support for this issue, you need to contact Renesas offical support using Ticket system or vis sales person.
Following is my comment which I can post in this forum.I think your steps to use Ethernet on CR52 is not wrong.However, I concern 3 points.
1. In your Linux -- Linux connection case, Machine A execute following command:
ifconfig tsn1 192.168.1.22ping 192.168.1.22
I think this command send ping to oneself.So, I don't know communication between board A and B is success or not.
2. It seems that Linux requires extra command to link up the phy.I don't know which code is relared with link up.But, did you adds code for link up into CR52 source code?
3. Did you check the CR52 log?If I remember correctly, ethernet sample outputs some log.To refer that log, you might confirm where process is stopped.
I hope these points help your debugging and to resolve the issue.
>Thus, we can not discuss this topic in the forum.So I will close this ticket. If you can post question without non-public information, please submit new question.