#include #include #if __has_include() #include #else #include #endif #include #include #include "edma.h" /* * Configuration */ #define MAX_PACKET 1500 #define TX_DESCS 8 #define IPQ40XX_MDIO_RETRY 1000 #define IPQ40XX_MDIO_DELAY 5 /* * Register definitions * Mainly borrowed from urlader */ #define GCC_ESS_BCR 0x01812008 #define GCC_MDIO_AHB_CBCR 0x1826000 #define IPQ40XX_NSS_BASE 0xC000000 #define IPQ40XX_EDMA_BASE 0xC080000 #define S17_P0STATUS_REG 0x007c #define S17_PORT_SPEED(x) (x << 0) #define S17_TX_FLOW_EN (1 << 4) #define S17_RX_FLOW_EN (1 << 5) #define S17_PORT_TX_MAC_EN (1 << 2) #define S17_PORT_RX_MAC_EN (1 << 3) #define S17_PORT_FULL_DUP (1 << 6) #define S17_GLOFW_CTRL1_REG 0x0624 #define S17_P0LOOKUP_CTRL_REG 0x0660 #define S17_P1LOOKUP_CTRL_REG 0x066c #define S17_P2LOOKUP_CTRL_REG 0x0678 #define S17_P3LOOKUP_CTRL_REG 0x0684 #define S17_P4LOOKUP_CTRL_REG 0x0690 #define S17_P5LOOKUP_CTRL_REG 0x069c #define S17_ATU_FUNC_REG 0x060C #define S17_ATU_FUNC_BUSY (1<<31) #define S17_ATU_FUNC_TRUNC_PORT(a) ((a)<<22) #define S17_ATU_FUNC_ATU_INDEX(a) ((a)<<16) #define S17_ATU_FUNC_VID_EN (1<<15) #define S17_ATU_FUNC_PORT_EN (1<<14) #define S17_ATU_FUNC_MULTI_EN (1<<13) #define S17_ATU_FUNC_FULL_VIO (1<<12) #define S17_ATU_FUNC_PORT_NUM(a) ((a)<<8) #define S17_ATU_FUNC_TYPE (1<<5) #define S17_ATU_FUNC_FLUSH_STATIC (1<<4) #define S17_ATU_FUNC_FLUSH_ALL 1 #define S17_ATU_FUNC_LOAD 2 #define S17_ATU_FUNC_PURGE 3 #define S17_ATU_FUNC_FLUSH_UNLOCKED 4 #define S17_ATU_FUNC_FLUSH_ONE 5 #define S17_ATU_FUNC_GET_NEXT 6 #define S17_ATU_FUNC_SEARCH_MAC 7 #define S17_ATU_FUNC_CHANGE_TRUNK 8 /* Queue Management Registers */ #define S17_PORT0_HOL_CTRL0 0x0970 #define S17_PORT0_HOL_CTRL1 0x0974 #define S17_PORT1_HOL_CTRL0 0x0978 #define S17_PORT1_HOL_CTRL1 0x097c #define S17_PORT2_HOL_CTRL0 0x0980 #define S17_PORT2_HOL_CTRL1 0x0984 #define S17_PORT3_HOL_CTRL0 0x0988 #define S17_PORT3_HOL_CTRL1 0x098c #define S17_PORT4_HOL_CTRL0 0x0990 #define S17_PORT4_HOL_CTRL1 0x0994 #define S17_PORT5_HOL_CTRL0 0x0998 #define S17_PORT5_HOL_CTRL1 0x099c #define ESS_MIB_OFFSET 0x30 #define ESS_MIB_EN (1 << 0) #define S17_PxLOOKUP_CTRL_REG(a) (S17_P0LOOKUP_CTRL_REG + ((a) * 0xc)) #define S17_LOOKUP_CTRL_LEARN_EN_0 (1 << 20) #define S17_LOOKUP_CTRL_PORTSTATE_DISABLE (0 << 16) #define S17_LOOKUP_CTRL_PORTSTATE_BLOCK (1 << 16) #define S17_LOOKUP_CTRL_PORTSTATE_LISTEN (2 << 16) #define S17_LOOKUP_CTRL_PORTSTATE_LEARN (3 << 16) #define S17_LOOKUP_CTRL_PORTSTATE_FORWARD (4 << 16) #define S17_LOOKUP_CTRL_PORT_VLAN_EN (1 << 10) #define S17_LOOKUP_CTRL_VLAN_MODE_DISABLE (0 << 8) #define S17_LOOKUP_CTRL_VLAN_MODE_FALLBACK (1 << 8) #define S17_LOOKUP_CTRL_VLAN_MODE_CHECK (2 << 8) #define S17_LOOKUP_CTRL_VLAN_MODE_SECURE (3 << 8) #if defined(CONFIG_CPU_BIG_ENDIAN) #define addr_to_words(addr, w1, w2) { \ w1 = (addr[5] << 24) | (addr[4] << 16) | (addr[3] << 8) | addr[2]; \ w2 = (addr[1] << 24) | (addr[0] << 16) | 0; \ } #else #define addr_to_words(addr, w1, w2) { \ w1 = (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3]; \ w2 = (addr[4] << 24) | (addr[5] << 16) | 0; \ } #endif #define IPQ40XX_MDIO_BASE 0x90000 #define MDIO_CTRL_0_REG 0x40 #define MDIO_CTRL_1_REG 0x44 #define MDIO_CTRL_2_REG 0x48 #define MDIO_CTRL_3_REG 0x4c #define MDIO_CTRL_4_REG 0x50 #define MDIO_CTRL_4_ACCESS_BUSY (1 << 16) #define MDIO_CTRL_4_ACCESS_START (1 << 8) #define MDIO_CTRL_4_ACCESS_CODE_READ 0 #define MDIO_CTRL_4_ACCESS_CODE_WRITE 1 /* from ipq40xx_qca8075.h */ #define QCA8075_PHY_V1_0_5P 0x004DD0B0 #define QCA8075_PHY_V1_1_5P 0x004DD0B1 #define QCA8075_PHY_V1_1_2P 0x004DD0B2 #define QCA8075_PHY_CONTROL 0 #define QCA8075_PHY_STATUS 1 #define QCA8075_PHY_ID1 2 #define QCA8075_PHY_ID2 3 #define QCA8075_AUTONEG_ADVERT 4 #define QCA8075_LINK_PARTNER_ABILITY 5 #define QCA8075_AUTONEG_EXPANSION 6 #define QCA8075_NEXT_PAGE_TRANSMIT 7 #define QCA8075_LINK_PARTNER_NEXT_PAGE 8 #define QCA8075_1000BASET_CONTROL 9 #define QCA8075_1000BASET_STATUS 10 #define QCA8075_MMD_CTRL_REG 13 #define QCA8075_MMD_DATA_REG 14 #define QCA8075_EXTENDED_STATUS 15 #define QCA8075_PHY_SPEC_CONTROL 16 #define QCA8075_PHY_SPEC_STATUS 17 #define QCA8075_PHY_INTR_MASK 18 #define QCA8075_PHY_INTR_STATUS 19 #define QCA8075_PHY_CDT_CONTROL 22 #define QCA8075_PHY_CDT_STATUS 28 #define QCA8075_DEBUG_PORT_ADDRESS 29 #define QCA8075_DEBUG_PORT_DATA 30 #define COMBO_PHY_ID 4 #define PSGMII_ID 5 #define QCA8075_DEBUG_PHY_HIBERNATION_CTRL 0xb #define QCA8075_DEBUG_PHY_POWER_SAVING_CTRL 0x29 #define QCA8075_PHY_MMD7_ADDR_8023AZ_EEE_CTRL 0x3c #define QCA8075_PHY_MMD3_ADDR_REMOTE_LOOPBACK_CTRL 0x805a #define QCA8075_PHY_MMD3_WOL_MAGIC_MAC_CTRL1 0x804a #define QCA8075_PHY_MMD3_WOL_MAGIC_MAC_CTRL2 0x804b #define QCA8075_PHY_MMD3_WOL_MAGIC_MAC_CTRL3 0x804c #define QCA8075_PHY_MMD3_WOL_CTRL 0x8012 #define QCA8075_PSGMII_FIFI_CTRL 0x6e #define QCA8075_PSGMII_CALIB_CTRL 0x27 #define QCA8075_PSGMII_MODE_CTRL 0x6d #define QCA8075_PHY_PSGMII_MODE_CTRL_ADJUST_VALUE 0x220c #define QCA8075_PHY_MMD7_NUM 7 #define QCA8075_PHY_MMD3_NUM 3 #define QCA8075_PHY_MMD1_NUM 1 #define QCA8075_PHY_SGMII_STATUS 0x1a /* sgmii_status Register */ #define QCA8075_PHY4_AUTO_SGMII_SELECT 0x40 #define QCA8075_PHY4_AUTO_COPPER_SELECT 0x20 #define QCA8075_PHY4_AUTO_BX1000_SELECT 0x10 #define QCA8075_PHY4_AUTO_FX100_SELECT 0x8 #define QCA8075_PHY_CHIP_CONFIG 0x1f /* Chip Configuration Register */ #define BT_BX_SG_REG_SELECT BIT_15 #define BT_BX_SG_REG_SELECT_OFFSET 15 #define BT_BX_SG_REG_SELECT_LEN 1 #define QCA8075_SG_BX_PAGES 0x0 #define QCA8075_SG_COPPER_PAGES 0x1 #define QCA8075_PHY_PSGMII_BASET 0x0 #define QCA8075_PHY_PSGMII_BX1000 0x1 #define QCA8075_PHY_PSGMII_FX100 0x2 #define QCA8075_PHY_PSGMII_AMDET 0x3 #define QCA8075_PHY_SGMII_BASET 0x4 #define QCA8075_PHY4_PREFER_FIBER 0x400 #define PHY4_PREFER_COPPER 0x0 #define PHY4_PREFER_FIBER 0x1 #define QCA8075_PHY4_FIBER_MODE_1000BX 0x100 #define AUTO_100FX_FIBER 0x0 #define AUTO_1000BX_FIBER 0x1 #define QCA8075_PHY_MDIX 0x0020 #define QCA8075_PHY_MDIX_AUTO 0x0060 #define QCA8075_PHY_MDIX_STATUS 0x0040 #define MODE_CFG_QUAL BIT_4 #define MODE_CFG_QUAL_OFFSET 4 #define MODE_CFG_QUAL_LEN 4 #define MODE_CFG BIT_0 #define MODE_CFG_OFFSET 0 #define MODE_CFG_LEN 4 #define QCA8075_PHY_MMD3_ADDR_8023AZ_CLD_CTRL 0x8007 #define QCA8075_PHY_MMD3_ADDR_8023AZ_TIMER_CTRL 0x804e #define QCA8075_PHY_MMD3_ADDR_CLD_CTRL5 0x8005 #define QCA8075_PHY_MMD3_ADDR_CLD_CTRL3 0x8003 #define AZ_TIMER_CTRL_DEFAULT_VALUE 0x3062 #define AZ_CLD_CTRL_DEFAULT_VALUE 0x83f6 #define AZ_TIMER_CTRL_ADJUST_VALUE 0x7062 #define AZ_CLD_CTRL_ADJUST_VALUE 0x8396 /*debug port */ #define QCA8075_DEBUG_PORT_RGMII_MODE 18 #define QCA8075_DEBUG_PORT_RGMII_MODE_EN 0x0008 #define QCA8075_DEBUG_PORT_RX_DELAY 0 #define QCA8075_DEBUG_PORT_RX_DELAY_EN 0x8000 #define QCA8075_DEBUG_PORT_TX_DELAY 5 #define QCA8075_DEBUG_PORT_TX_DELAY_EN 0x0100 /* PHY Registers Field */ /* Control Register fields offset:0 */ /* bits 6,13: 10=1000, 01=100, 00=10 */ #define QCA8075_CTRL_SPEED_MSB 0x0040 /* Collision test enable */ #define QCA8075_CTRL_COLL_TEST_ENABLE 0x0080 /* FDX =1, half duplex =0 */ #define QCA8075_CTRL_FULL_DUPLEX 0x0100 /* Restart auto negotiation */ #define QCA8075_CTRL_RESTART_AUTONEGOTIATION 0x0200 /* Isolate PHY from MII */ #define QCA8075_CTRL_ISOLATE 0x0400 /* Power down */ #define QCA8075_CTRL_POWER_DOWN 0x0800 /* Auto Neg Enable */ #define QCA8075_CTRL_AUTONEGOTIATION_ENABLE 0x1000 /* Local Loopback Enable */ #define QCA8075_LOCAL_LOOPBACK_ENABLE 0x4000 /* bits 6,13: 10=1000, 01=100, 00=10 */ #define QCA8075_CTRL_SPEED_LSB 0x2000 /* 0 = normal, 1 = loopback */ #define QCA8075_CTRL_LOOPBACK 0x4000 #define QCA8075_CTRL_SOFTWARE_RESET 0x8000 #define QCA8075_CTRL_SPEED_MASK 0x2040 #define QCA8075_CTRL_SPEED_1000 0x0040 #define QCA8075_CTRL_SPEED_100 0x2000 #define QCA8075_CTRL_SPEED_10 0x0000 #define QCA8075_RESET_DONE(phy_control) \ (((phy_control) & (QCA8075_CTRL_SOFTWARE_RESET)) == 0) /* Status Register fields offset:1 */ /* Extended register capabilities */ #define QCA8075_STATUS_EXTENDED_CAPS 0x0001 /* Jabber Detected */ #define QCA8075_STATUS_JABBER_DETECT 0x0002 /* Link Status 1 = link */ #define QCA8075_STATUS_LINK_STATUS_UP 0x0004 /* Auto Neg Capable */ #define QCA8075_STATUS_AUTONEG_CAPS 0x0008 /* Remote Fault Detect */ #define QCA8075_STATUS_REMOTE_FAULT 0x0010 /* Auto Neg Complete */ #define QCA8075_STATUS_AUTO_NEG_DONE 0x0020 /* Preamble may be suppressed */ #define QCA8075_STATUS_PREAMBLE_SUPPRESS 0x0040 /* Ext. status info in Reg 0x0F */ #define QCA8075_STATUS_EXTENDED_STATUS 0x0100 /* 100T2 Half Duplex Capable */ #define QCA8075_STATUS_100T2_HD_CAPS 0x0200 /* 100T2 Full Duplex Capable */ #define QCA8075_STATUS_100T2_FD_CAPS 0x0400 /* 10T Half Duplex Capable */ #define QCA8075_STATUS_10T_HD_CAPS 0x0800 /* 10T Full Duplex Capable */ #define QCA8075_STATUS_10T_FD_CAPS 0x1000 /* 100X Half Duplex Capable */ #define QCA8075_STATUS_100X_HD_CAPS 0x2000 /* 100X Full Duplex Capable */ #define QCA8075_STATUS_100X_FD_CAPS 0x4000 /* 100T4 Capable */ #define QCA8075_STATUS_100T4_CAPS 0x8000 /* extended status register capabilities */ #define QCA8075_STATUS_1000T_HD_CAPS 0x1000 #define QCA8075_STATUS_1000T_FD_CAPS 0x2000 #define QCA8075_STATUS_1000X_HD_CAPS 0x4000 #define QCA8075_STATUS_1000X_FD_CAPS 0x8000 #define QCA8075_AUTONEG_DONE(ip_phy_status) \ (((ip_phy_status) & (QCA8075_STATUS_AUTO_NEG_DONE)) == \ (QCA8075_STATUS_AUTO_NEG_DONE)) /* PHY identifier1 offset:2 */ //Organizationally Unique Identifier bits 3:18 /* PHY identifier2 offset:3 */ //Organizationally Unique Identifier bits 19:24 /* Auto-Negotiation Advertisement register. offset:4 */ /* indicates IEEE 802.3 CSMA/CD */ #define QCA8075_ADVERTISE_SELECTOR_FIELD 0x0001 /* 10T Half Duplex Capable */ #define QCA8075_ADVERTISE_10HALF 0x0020 /* 10T Full Duplex Capable */ #define QCA8075_ADVERTISE_10FULL 0x0040 /* 100TX Half Duplex Capable */ #define QCA8075_ADVERTISE_100HALF 0x0080 /* 100TX Full Duplex Capable */ #define QCA8075_ADVERTISE_100FULL 0x0100 /* 100T4 Capable */ #define QCA8075_ADVERTISE_100T4 0x0200 /* Pause operation desired */ #define QCA8075_ADVERTISE_PAUSE 0x0400 /* Asymmetric Pause Direction bit */ #define QCA8075_ADVERTISE_ASYM_PAUSE 0x0800 /* Remote Fault detected */ #define QCA8075_ADVERTISE_REMOTE_FAULT 0x2000 /* Next Page ability supported */ #define QCA8075_ADVERTISE_NEXT_PAGE 0x8000 /* 100TX Half Duplex Capable */ #define QCA8075_ADVERTISE_1000HALF 0x0100 /* 100TX Full Duplex Capable */ #define QCA8075_ADVERTISE_1000FULL 0x0200 #define QCA8075_ADVERTISE_ALL \ (QCA8075_ADVERTISE_10HALF | QCA8075_ADVERTISE_10FULL | \ QCA8075_ADVERTISE_100HALF | QCA8075_ADVERTISE_100FULL | \ QCA8075_ADVERTISE_1000FULL) #define QCA8075_ADVERTISE_MEGA_ALL \ (QCA8075_ADVERTISE_10HALF | QCA8075_ADVERTISE_10FULL | \ QCA8075_ADVERTISE_100HALF | QCA8075_ADVERTISE_100FULL) #define QCA8075_BX_ADVERTISE_1000FULL 0x0020 #define QCA8075_BX_ADVERTISE_1000HALF 0x0040 #define QCA8075_BX_ADVERTISE_PAUSE 0x0080 #define QCA8075_BX_ADVERTISE_ASYM_PAUSE 0x0100 #define QCA8075_BX_ADVERTISE_ALL \ (QCA8075_BX_ADVERTISE_ASYM_PAUSE | QCA8075_BX_ADVERTISE_PAUSE | \ QCA8075_BX_ADVERTISE_1000HALF | QCA8075_BX_ADVERTISE_1000FULL) /* Link Partner ability offset:5 */ /* Same as advertise selector */ #define QCA8075_LINK_SLCT 0x001f /* Can do 10mbps half-duplex */ #define QCA8075_LINK_10BASETX_HALF_DUPLEX 0x0020 /* Can do 10mbps full-duplex */ #define QCA8075_LINK_10BASETX_FULL_DUPLEX 0x0040 /* Can do 100mbps half-duplex */ #define QCA8075_LINK_100BASETX_HALF_DUPLEX 0x0080 /* Can do 100mbps full-duplex */ #define QCA8075_LINK_100BASETX_FULL_DUPLEX 0x0100 /* Can do 1000mbps full-duplex */ #define QCA8075_LINK_1000BASETX_FULL_DUPLEX 0x0800 /* Can do 1000mbps half-duplex */ #define QCA8075_LINK_1000BASETX_HALF_DUPLEX 0x0400 /* 100BASE-T4 */ #define QCA8075_LINK_100BASE4 0x0200 /* PAUSE */ #define QCA8075_LINK_PAUSE 0x0400 /* Asymmetrical PAUSE */ #define QCA8075_LINK_ASYPAUSE 0x0800 /* Link partner faulted */ #define QCA8075_LINK_RFAULT 0x2000 /* Link partner acked us */ #define QCA8075_LINK_LPACK 0x4000 /* Next page bit */ #define QCA8075_LINK_NPAGE 0x8000 /* Auto-Negotiation Expansion Register offset:6 */ /* Next Page Transmit Register offset:7 */ /* Link partner Next Page Register offset:8 */ /* 1000BASE-T Control Register offset:9 */ /* Advertise 1000T HD capability */ #define QCA8075_CTL_1000T_HD_CAPS 0x0100 /* Advertise 1000T FD capability */ #define QCA8075_CTL_1000T_FD_CAPS 0x0200 /* 1=Repeater/switch device port 0=DTE device */ #define QCA8075_CTL_1000T_REPEATER_DTE 0x0400 /* 1=Configure PHY as Master 0=Configure PHY as Slave */ #define QCA8075_CTL_1000T_MS_VALUE 0x0800 /* 1=Master/Slave manual config value 0=Automatic Master/Slave config */ #define QCA8075_CTL_1000T_MS_ENABLE 0x1000 /* Normal Operation */ #define QCA8075_CTL_1000T_TEST_MODE_NORMAL 0x0000 /* Transmit Waveform test */ #define QCA8075_CTL_1000T_TEST_MODE_1 0x2000 /* Master Transmit Jitter test */ #define QCA8075_CTL_1000T_TEST_MODE_2 0x4000 /* Slave Transmit Jitter test */ #define QCA8075_CTL_1000T_TEST_MODE_3 0x6000 /* Transmitter Distortion test */ #define QCA8075_CTL_1000T_TEST_MODE_4 0x8000 #define QCA8075_CTL_1000T_SPEED_MASK 0x0300 #define QCA8075_CTL_1000T_DEFAULT_CAP_MASK 0x0300 /* 1000BASE-T Status Register offset:10 */ /* LP is 1000T HD capable */ #define QCA8075_STATUS_1000T_LP_HD_CAPS 0x0400 /* LP is 1000T FD capable */ #define QCA8075_STATUS_1000T_LP_FD_CAPS 0x0800 /* Remote receiver OK */ #define QCA8075_STATUS_1000T_REMOTE_RX_STATUS 0x1000 /* Local receiver OK */ #define QCA8075_STATUS_1000T_LOCAL_RX_STATUS 0x2000 /* 1=Local TX is Master, 0=Slave */ #define QCA8075_STATUS_1000T_MS_CONFIG_RES 0x4000 #define QCA8075_STATUS_1000T_MS_CONFIG_FAULT 0x8000 /* Master/Slave config fault */ #define QCA8075_STATUS_1000T_REMOTE_RX_STATUS_SHIFT 12 #define QCA8075_STATUS_1000T_LOCAL_RX_STATUS_SHIFT 13 /* Phy Specific Control Register offset:16 */ /* 1=Jabber Function disabled */ #define QCA8075_CTL_JABBER_DISABLE 0x0001 /* 1=Polarity Reversal enabled */ #define QCA8075_CTL_POLARITY_REVERSAL 0x0002 /* 1=SQE Test enabled */ #define QCA8075_CTL_SQE_TEST 0x0004 #define QCA8075_CTL_MAC_POWERDOWN 0x0008 /* 1=CLK125 low, 0=CLK125 toggling #define QCA8075_CTL_CLK125_DISABLE 0x0010 */ /* MDI Crossover Mode bits 6:5 */ /* Manual MDI configuration */ #define QCA8075_CTL_MDI_MANUAL_MODE 0x0000 /* Manual MDIX configuration */ #define QCA8075_CTL_MDIX_MANUAL_MODE 0x0020 /* 1000BASE-T: Auto crossover, 100BASE-TX/10BASE-T: MDI Mode */ #define QCA8075_CTL_AUTO_X_1000T 0x0040 /* Auto crossover enabled all speeds */ #define QCA8075_CTL_AUTO_X_MODE 0x0060 /* 1=Enable Extended 10BASE-T distance * (Lower 10BASE-T RX Threshold) * 0=Normal 10BASE-T RX Threshold */ #define QCA8075_CTL_10BT_EXT_DIST_ENABLE 0x0080 /* 1=5-Bit interface in 100BASE-TX * 0=MII interface in 100BASE-TX */ #define QCA8075_CTL_MII_5BIT_ENABLE 0x0100 /* 1=Scrambler disable */ #define QCA8075_CTL_SCRAMBLER_DISABLE 0x0200 /* 1=Force link good */ #define QCA8075_CTL_FORCE_LINK_GOOD 0x0400 /* 1=Assert CRS on Transmit */ #define QCA8075_CTL_ASSERT_CRS_ON_TX 0x0800 #define QCA8075_CTL_POLARITY_REVERSAL_SHIFT 1 #define QCA8075_CTL_AUTO_X_MODE_SHIFT 5 #define QCA8075_CTL_10BT_EXT_DIST_ENABLE_SHIFT 7 /* Phy Specific status fields offset:17 */ /* 1=Speed & Duplex resolved */ #define QCA8075_STATUS_LINK_PASS 0x0400 #define QCA8075_STATUS_RESOVLED 0x0800 /* 1=Duplex 0=Half Duplex */ #define QCA8075_STATUS_FULL_DUPLEX 0x2000 /* Speed, bits 14:15 */ #define QCA8075_STATUS_SPEED 0xC000 #define QCA8075_STATUS_SPEED_MASK 0xC000 /* 00=10Mbs */ #define QCA8075_STATUS_SPEED_10MBS 0x0000 /* 01=100Mbs */ #define QCA8075_STATUS_SPEED_100MBS 0x4000 /* 10=1000Mbs */ #define QCA8075_STATUS_SPEED_1000MBS 0x8000 #define QCA8075_SPEED_DUPLEX_RESOVLED(phy_status) \ (((phy_status) & \ (QCA8075_STATUS_RESOVLED)) == \ (QCA8075_STATUS_RESOVLED)) /*phy debug port1 register offset:29 */ /*phy debug port2 register offset:30 */ /*QCA8075 interrupt flag */ #define QCA8075_INTR_SPEED_CHANGE 0x4000 #define QCA8075_INTR_DUPLEX_CHANGE 0x2000 #define QCA8075_INTR_STATUS_UP_CHANGE 0x0400 #define QCA8075_INTR_STATUS_DOWN_CHANGE 0x0800 #define QCA8075_INTR_BX_FX_STATUS_DOWN_CHANGE 0x0100 #define QCA8075_INTR_BX_FX_STATUS_UP_CHANGE 0x0080 #define QCA8075_INTR_MEDIA_STATUS_CHANGE 0x1000 #define QCA8075_INTR_WOL 0x0001 #define QCA8075_INTR_POE 0x0002 #define RUN_CDT 0x8000 #define CABLE_LENGTH_UNIT 0x0400 #define PSGMIIPHY_BASE 0x00098000 /* From include/cortexa9/ipq40xx_edma_eth.h */ #define PSGMIIPHY_PLL_VCO_RELATED_CTRL 0x0000078c #define PSGMIIPHY_PLL_VCO_VAL 0x2803 #define PSGMIIPHY_VCO_CALIBRATION_CTRL 0x0000009c #define PSGMIIPHY_VCO_VAL 0x4ADA #define PSGMIIPHY_VCO_RST_VAL 0xADA #define QCA8075_MAX_TRIES 100 /* * End borrowed register definitions. */ struct avm_coredump_net_ctx { struct edma_tx_desc *tx_descs; dma_addr_t tx_descs_dma; u8 *packet_mem[TX_DESCS]; dma_addr_t packet_mem_dma[TX_DESCS]; // Mapped registers for GCC, Switch, MDIO and PHY void __iomem *edma_base; void __iomem *reset_reg; void __iomem *mdio_ahb_cbcr; void __iomem *ess_regs; void __iomem *mdio_base; void __iomem *psgmiiphy_base; }; static struct avm_coredump_net_ctx *ctx; /* * Resets the watchdog periodically. * * Needs to be called regulary to keep device alive. */ static void avm_coredump_periodic(void) { touch_nmi_watchdog(); ar7wdt_hw_trigger(); } static inline void edma_write_reg(u16 reg_addr, u32 reg_value) { writel(reg_value, ((void __iomem *)(ctx->edma_base + reg_addr))); } static inline void edma_read_reg(u16 reg_addr, volatile u32 *reg_value) { *reg_value = readl((void __iomem *)(ctx->edma_base + reg_addr)); } static void edma_clear_irq_status(void) { edma_write_reg(EDMA_REG_RX_ISR, 0xff); edma_write_reg(EDMA_REG_TX_ISR, 0xffff); edma_write_reg(EDMA_REG_MISC_ISR, 0x1fff); edma_write_reg(EDMA_REG_WOL_ISR, 0x1); }; #define PSGMIIPHY_REG(name) (__iomem void*)((u8*)ctx->psgmiiphy_base + name) static inline void ess_sw_rd(u32 addr, u32 * data) { *data = readl((void __iomem *)(ctx->ess_regs + addr)); } static inline void ess_sw_wr(u32 addr, u32 data) { writel(data, ((void __iomem *)(ctx->ess_regs + addr))); } static void ess_reset(void) { avm_coredump_periodic(); writel(1, ctx->reset_reg); mdelay(10); writel(0, ctx->reset_reg); mdelay(100); } void ess_enable_lookup(void) { ess_sw_wr(S17_P0LOOKUP_CTRL_REG, 0x14003e); ess_sw_wr(S17_P1LOOKUP_CTRL_REG, 0x14001d); ess_sw_wr(S17_P2LOOKUP_CTRL_REG, 0x14001b); ess_sw_wr(S17_P3LOOKUP_CTRL_REG, 0x140017); ess_sw_wr(S17_P4LOOKUP_CTRL_REG, 0x14000f); ess_sw_wr(S17_P5LOOKUP_CTRL_REG, 0x140001); ess_sw_wr(S17_GLOFW_CTRL1_REG, 0x3f3f3f); } void ess_disable_lookup(void) { ess_sw_wr(S17_P0LOOKUP_CTRL_REG, 0x140000); ess_sw_wr(S17_P1LOOKUP_CTRL_REG, 0x14001c); ess_sw_wr(S17_P2LOOKUP_CTRL_REG, 0x14001a); ess_sw_wr(S17_P3LOOKUP_CTRL_REG, 0x140016); ess_sw_wr(S17_P4LOOKUP_CTRL_REG, 0x14001e); ess_sw_wr(S17_P5LOOKUP_CTRL_REG, 0x140000); ess_sw_wr(S17_GLOFW_CTRL1_REG, 0x3e3e3e); } int ess_sw_init(void) { u32 data; ess_sw_wr(S17_GLOFW_CTRL1_REG, 0x3e3e3e); /* * configure Speed, Duplex. */ ess_sw_wr(S17_P0STATUS_REG, S17_PORT_SPEED(2) | S17_PORT_FULL_DUP | S17_TX_FLOW_EN | S17_RX_FLOW_EN); ess_sw_wr(S17_P0LOOKUP_CTRL_REG, 0x140000); ess_sw_wr(S17_P1LOOKUP_CTRL_REG, 0x140000); ess_sw_wr(S17_P2LOOKUP_CTRL_REG, 0x140000); ess_sw_wr(S17_P3LOOKUP_CTRL_REG, 0x140000); ess_sw_wr(S17_P4LOOKUP_CTRL_REG, 0x140000); ess_sw_wr(S17_P5LOOKUP_CTRL_REG, 0x140000); /* * HOL setting for Port0 */ ess_sw_wr(S17_PORT0_HOL_CTRL0, 0x1e444444); ess_sw_wr(S17_PORT0_HOL_CTRL1, 0x1c6); /* * HOL setting for Port1 */ ess_sw_wr(S17_PORT1_HOL_CTRL0, 0x1e004444); ess_sw_wr(S17_PORT1_HOL_CTRL1, 0x1c6); /* * HOL setting for Port2 */ ess_sw_wr(S17_PORT2_HOL_CTRL0, 0x1e004444); ess_sw_wr(S17_PORT2_HOL_CTRL1, 0x1c6); /* * HOL setting for Port3 */ ess_sw_wr(S17_PORT3_HOL_CTRL0, 0x1e004444); ess_sw_wr(S17_PORT3_HOL_CTRL1, 0x1c6); /* * HOL setting for Port4 */ ess_sw_wr(S17_PORT4_HOL_CTRL0, 0x1e004444); ess_sw_wr(S17_PORT4_HOL_CTRL1, 0x1c6); /* * HOL setting for Port5 */ ess_sw_wr(S17_PORT5_HOL_CTRL0, 0x1e444444); ess_sw_wr(S17_PORT5_HOL_CTRL1, 0x1c6); mdelay(1); /* * Enable Rx and Tx mac. */ ess_sw_rd(S17_P0STATUS_REG, &data); ess_sw_wr(S17_P0STATUS_REG, data | S17_PORT_TX_MAC_EN | S17_PORT_RX_MAC_EN); ess_sw_rd(ESS_MIB_OFFSET, &data); ess_sw_wr(ESS_MIB_OFFSET, (data | ESS_MIB_EN)); ess_sw_wr(S17_GLOFW_CTRL1_REG, 0x7f7f7f); /*--- DebugPrintf("%s done\n", __func__); ---*/ return 0; } static int ipq40xx_mdio_wait_busy(void) { int i; u32 busy; for (i = 0; i < IPQ40XX_MDIO_RETRY; i++) { udelay(IPQ40XX_MDIO_DELAY); busy = readl(ctx->mdio_base + MDIO_CTRL_4_REG) & MDIO_CTRL_4_ACCESS_BUSY; if (!busy) return 0; udelay(IPQ40XX_MDIO_DELAY); avm_coredump_periodic(); } printk(KERN_ERR "%s: MDIO operation timed out\n", __func__); return -ETIMEDOUT; } static int ipq40xx_mdio_write(int mii_id, int regnum, u16 value) { /* Issue the phy addreass and reg */ writel((mii_id << 8 | regnum), ctx->mdio_base + MDIO_CTRL_1_REG); /* Issue a write data */ writel(value, ctx->mdio_base + MDIO_CTRL_2_REG); /* Issue write command */ writel((MDIO_CTRL_4_ACCESS_START | MDIO_CTRL_4_ACCESS_CODE_WRITE), (ctx->mdio_base + MDIO_CTRL_4_REG)); /* Wait for write complete */ if (ipq40xx_mdio_wait_busy()) return -ETIMEDOUT; return 0; } static int ipq40xx_mdio_read(int mii_id, int regnum) { u32 val; /* Issue the phy address and reg */ writel((mii_id << 8) | regnum, ctx->mdio_base + MDIO_CTRL_1_REG); /* issue read command */ writel((MDIO_CTRL_4_ACCESS_START | MDIO_CTRL_4_ACCESS_CODE_READ), (ctx->mdio_base + MDIO_CTRL_4_REG)); if (ipq40xx_mdio_wait_busy()) return -ETIMEDOUT; /* Read data */ val = readl(ctx->mdio_base + MDIO_CTRL_3_REG); return val; } static u32 qca8075_phy_mmd_write(u32 phy_id, u16 mmd_num, u16 reg_id, u16 reg_val) { ipq40xx_mdio_write(phy_id, QCA8075_MMD_CTRL_REG, mmd_num); ipq40xx_mdio_write(phy_id, QCA8075_MMD_DATA_REG, reg_id); ipq40xx_mdio_write(phy_id, QCA8075_MMD_CTRL_REG, 0x4000 | mmd_num); ipq40xx_mdio_write(phy_id, QCA8075_MMD_DATA_REG, reg_val); return 0; } static u16 qca8075_phy_mmd_read(u32 phy_id, u16 mmd_num, u16 reg_id) { ipq40xx_mdio_write(phy_id, QCA8075_MMD_CTRL_REG, mmd_num); ipq40xx_mdio_write(phy_id, QCA8075_MMD_DATA_REG, reg_id); ipq40xx_mdio_write(phy_id, QCA8075_MMD_CTRL_REG, 0x4000 | mmd_num); return ipq40xx_mdio_read(phy_id, QCA8075_MMD_DATA_REG); } static void qca8075_ess_reset(void) { int i; u32 status; /* * Fix phy psgmii RX 20bit */ ipq40xx_mdio_write(5, QCA8075_PHY_CONTROL, 0x005b); /* * Reset phy psgmii */ ipq40xx_mdio_write(5, QCA8075_PHY_CONTROL, 0x001b); /* * Release reset phy psgmii */ ipq40xx_mdio_write(5, QCA8075_PHY_CONTROL, 0x005b); for (i = 0; i < QCA8075_MAX_TRIES; i++) { status = qca8075_phy_mmd_read(5, 1, 0x28); if(status & 0x1) break; mdelay(10); avm_coredump_periodic(); } if (i >= QCA8075_MAX_TRIES) printk(KERN_ERR "qca8075 PSGMII PLL_VCO_CALIB Not Ready\n"); avm_coredump_periodic(); mdelay(50); /* * Check qca8075 psgmii calibration done end. * Freeze phy psgmii RX CDR */ ipq40xx_mdio_write(5, 0x1a, 0x2230); ess_reset(); /* * Check ipq40xx psgmii calibration done start */ for (i = 0; i < QCA8075_MAX_TRIES; i++) { status = readl(PSGMIIPHY_REG(0x000000A0)); if (status & 0x1) break; mdelay(10); } if (i >= QCA8075_MAX_TRIES) printk(KERN_ERR "PSGMII PLL_VCO_CALIB Not Ready\n"); avm_coredump_periodic(); mdelay(50); /* * Check ipq40xx psgmii calibration done end. * Relesae phy psgmii RX CDR */ ipq40xx_mdio_write(5, 0x1a, 0x3230); /* * Release phy psgmii RX 20bit */ ipq40xx_mdio_write(5, QCA8075_PHY_CONTROL, 0x005f); avm_coredump_periodic(); mdelay(200); } static void psgmii_self_test(void) { int i, phy, j; u32 value; u32 phy_t_status; u16 status; u32 tx_counter_ok, tx_counter_error; /*--- u32 rx_counter_ok, rx_counter_error; ---*/ u32 tx_counter_ok_high16; /*--- u32 rx_counter_ok_high16; ---*/ u32 tx_ok;//, rx_ok; int phy_addr = 0; /* * Switch to access MII reg for copper */ ipq40xx_mdio_write(4, 0x1f, 0x8500); for (phy = 0; phy < 5; phy++) { /* * Enable phy mdio broadcast write */ qca8075_phy_mmd_write(phy, 7, 0x8028, 0x801f); } /* * Force no link by power down */ ipq40xx_mdio_write(0x1f, QCA8075_PHY_CONTROL, 0x1840); /* * Packet number */ qca8075_phy_mmd_write(0x1f, 7, 0x8021, 0x3000); qca8075_phy_mmd_write(0x1f, 7, 0x8062, 0x05e0); /* * Fix mdi status */ ipq40xx_mdio_write(0x1f, QCA8075_PHY_SPEC_CONTROL, 0x6800); for (i = 0; i < 100; i++) { phy_t_status = 0; for (phy = 0; phy < 5; phy++) { phy_addr = phy; ess_sw_rd(0x66c + (phy_addr * 0xc), &value); /* * Enable mac loop back */ ess_sw_wr(0x66c + (phy_addr * 0xc), (value|((1 << 21)))); } /* * Phy single test */ for (phy = 0; phy < 5; phy++) { phy_addr = phy; /* * Enable loopback */ ipq40xx_mdio_write(phy_addr, QCA8075_PHY_CONTROL, 0x9000); ipq40xx_mdio_write(phy_addr, QCA8075_PHY_CONTROL, 0x4140); /* * Check link */ j = 0; while (j < 100) { status = ipq40xx_mdio_read(phy_addr, QCA8075_PHY_SPEC_STATUS); if (status & (1 << 10)) break; mdelay(10); j++; } /* * Enable check */ qca8075_phy_mmd_write(phy_addr, 7, 0x8029, 0x0000); qca8075_phy_mmd_write(phy_addr, 7, 0x8029, 0x0003); /* * Start traffic */ qca8075_phy_mmd_write(phy_addr, 7, 0x8020, 0xa000); avm_coredump_periodic(); mdelay(200); /* * check counter */ tx_counter_ok = qca8075_phy_mmd_read(phy_addr, 7, 0x802e); tx_counter_ok_high16 = qca8075_phy_mmd_read(phy_addr, 7, 0x802d); tx_counter_error = qca8075_phy_mmd_read(phy_addr, 7, 0x802f); /*--- rx_counter_ok = qca8075_phy_mmd_read(phy_addr, 7, 0x802b); ---*/ /*--- rx_counter_ok_high16 = qca8075_phy_mmd_read(phy_addr, 7, 0x802a); ---*/ /*--- rx_counter_error = qca8075_phy_mmd_read(phy_addr, 7, 0x802c); ---*/ tx_ok = tx_counter_ok + (tx_counter_ok_high16 << 16); /*--- rx_ok = rx_counter_ok + (rx_counter_ok_high16 << 16); ---*/ /* * Success */ if((tx_ok == 0x3000) && (tx_counter_error == 0)) { phy_t_status &= (~(1 << phy_addr)); } else { phy_t_status |= (1 << phy_addr); } /* * Power down */ ipq40xx_mdio_write(phy_addr, QCA8075_PHY_CONTROL, 0x1840); } /* * Reset 5-phy */ ipq40xx_mdio_write(0x1f, QCA8075_PHY_CONTROL, 0x9000); /* * Enable 5-phy loopback */ ipq40xx_mdio_write(0x1f, QCA8075_PHY_CONTROL, 0x4140); /* * check link */ j = 0; while (j < 100) { for (phy = 0; phy < 5; phy++) { phy_addr = phy; status = ipq40xx_mdio_read(phy_addr, QCA8075_PHY_SPEC_STATUS); if (!(status & (1 << 10))) break; } if (phy >= 5) break; avm_coredump_periodic(); mdelay(10); j++; } /* * Enable check */ qca8075_phy_mmd_write(0x1f, 7, 0x8029, 0x0000); qca8075_phy_mmd_write(0x1f, 7, 0x8029, 0x0003); /* * Start traffic */ qca8075_phy_mmd_write(0x1f, 7, 0x8020, 0xa000); avm_coredump_periodic(); mdelay(200); for (phy = 0; phy < 5; phy++) { phy_addr = phy; /* * Check counter */ tx_counter_ok = qca8075_phy_mmd_read(phy_addr, 7, 0x802e); tx_counter_ok_high16 = qca8075_phy_mmd_read(phy_addr, 7, 0x802d); tx_counter_error = qca8075_phy_mmd_read(phy_addr, 7, 0x802f); /*--- rx_counter_ok = qca8075_phy_mmd_read(phy_addr, 7, 0x802b); ---*/ /*--- rx_counter_ok_high16 = qca8075_phy_mmd_read(phy_addr, 7, 0x802a); ---*/ /*--- rx_counter_error = qca8075_phy_mmd_read(phy_addr, 7, 0x802c); ---*/ tx_ok = tx_counter_ok + (tx_counter_ok_high16 << 16); /*--- rx_ok = rx_counter_ok + (rx_counter_ok_high16 << 16); ---*/ /* * Success */ if ((tx_ok == 0x3000) && (tx_counter_error == 0)) { phy_t_status &= (~(1 << (phy_addr + 8))); } else { phy_t_status |= (1 << (phy_addr + 8)); } } if (phy_t_status) { qca8075_ess_reset(); } else { break; } } /* * Configuration recover */ /* * Packet number */ qca8075_phy_mmd_write(0x1f, 7, 0x8021, 0x0); /* * Disable check */ qca8075_phy_mmd_write(0x1f, 7, 0x8029, 0x0); /* * Disable traffic */ qca8075_phy_mmd_write(0x1f, 7, 0x8020, 0x0); } static void clear_self_test_config(void) { int phy = 0, phy_addr = 0; u32 value = 0; /* * Disable phy internal loopback */ ipq40xx_mdio_write(0x1f, QCA8075_PHY_SPEC_CONTROL, 0x6860); /*--- ipq40xx_mdio_write(0x1f, QCA8075_PHY_CONTROL, 0x9040); ---*/ for (phy = 0; phy < 5; phy++) { phy_addr = phy; ess_sw_rd(0x66c + (phy_addr * 0xc), &value); /* * Disable mac loop back */ ess_sw_wr(0x66c + (phy_addr * 0xc), (value&(~(1 << 21)))); /* * Disable phy mdio broadcast write */ qca8075_phy_mmd_write(phy_addr, 7, 0x8028, 0x001f); /*--- restart autoneg ---*/ ipq40xx_mdio_write(phy_addr, QCA8075_PHY_CONTROL, QCA8075_CTRL_SOFTWARE_RESET | QCA8075_CTRL_AUTONEGOTIATION_ENABLE | QCA8075_CTRL_RESTART_AUTONEGOTIATION); } } static void psgmii_setup(void) { writel(PSGMIIPHY_PLL_VCO_VAL, PSGMIIPHY_REG(PSGMIIPHY_PLL_VCO_RELATED_CTRL)); writel(PSGMIIPHY_VCO_VAL, PSGMIIPHY_REG(PSGMIIPHY_VCO_CALIBRATION_CTRL)); /* wait for 10ms */ mdelay(10); writel(PSGMIIPHY_VCO_RST_VAL, PSGMIIPHY_REG(PSGMIIPHY_VCO_CALIBRATION_CTRL)); qca8075_ess_reset(); avm_coredump_periodic(); mdelay(100); psgmii_self_test(); avm_coredump_periodic(); mdelay(300); clear_self_test_config(); } static unsigned int get_port_status(void) { unsigned int status = 0; unsigned int i; unsigned int data; for (i = 0; i < 5; i++) { data = ipq40xx_mdio_read(i, QCA8075_PHY_STATUS); if (data & QCA8075_STATUS_LINK_STATUS_UP) { status |= (1 << i); } } return status; } #define S17_ARL_CTRL_REG 0x0618 #define MDIO_CTRL_0_DIV(x) (x << 0) #define MDIO_CTRL_0_MODE (1 << 8) #define MDIO_CTRL_0_RES(x) (x << 9) #define MDIO_CTRL_0_MDC_MODE (1 << 12) #define MDIO_CTRL_0_GPHY(x) (x << 13) #define MDIO_CTRL_0_RES1(x) (x << 17) int avm_coredump_net_force_setup(u8 *mac_addr) { unsigned int i; bool has_link = false; u32 data, w1, w2; u16 hw_cons_idx; printk(KERN_ERR "[avm_coredump_net] Begin force setup\n"); ess_reset(); writel(1, ctx->mdio_ahb_cbcr); writel(MDIO_CTRL_0_DIV(0xff) | MDIO_CTRL_0_MDC_MODE | MDIO_CTRL_0_GPHY(0xa), (__iomem void*)((u8*)ctx->mdio_base + MDIO_CTRL_0_REG)); psgmii_setup(); // Disable IRQs for (i = 0; i < EDMA_MAX_RECEIVE_QUEUE; i++) edma_write_reg(EDMA_REG_RX_INT_MASK_Q(i), 0x0); for (i = 0; i < EDMA_MAX_TRANSMIT_QUEUE; i++) edma_write_reg(EDMA_REG_TX_INT_MASK_Q(i), 0x0); edma_write_reg(EDMA_REG_MISC_IMR, 0); edma_write_reg(EDMA_REG_WOL_IMR, 0); edma_clear_irq_status(); // Disable RX and TX operation edma_read_reg(EDMA_REG_RXQ_CTRL, &data); data &= ~EDMA_RXQ_CTRL_EN; edma_write_reg(EDMA_REG_RXQ_CTRL, data); edma_read_reg(EDMA_REG_TXQ_CTRL, &data); data &= ~EDMA_TXQ_CTRL_TXQ_EN; edma_write_reg(EDMA_REG_TXQ_CTRL, data); // Let hardware write "software consumer index" edma_read_reg(EDMA_REG_INTR_CTRL, &data); data &= ~(1 << EDMA_INTR_SW_IDX_W_TYP_SHIFT); data |= 1 << EDMA_INTR_SW_IDX_W_TYP_SHIFT; edma_write_reg(EDMA_REG_INTR_CTRL, data); // Clear any WOL things edma_write_reg(EDMA_REG_WOL_CTRL, 0); data = (EDMA_TX_IMT << EDMA_IRQ_MODRT_TX_TIMER_SHIFT); data |= (EDMA_RX_IMT << EDMA_IRQ_MODRT_RX_TIMER_SHIFT); edma_write_reg(EDMA_REG_IRQ_MODRT_TIMER_INIT, data); // Configure TX data = (EDMA_TPD_BURST << EDMA_TXQ_NUM_TPD_BURST_SHIFT); data |= EDMA_TXQ_CTRL_TPD_BURST_EN; data |= (EDMA_TXF_BURST << EDMA_TXQ_TXF_BURST_NUM_SHIFT); edma_write_reg(EDMA_REG_TXQ_CTRL, data); // Setup tx ring edma_write_reg(EDMA_REG_TPD_BASE_ADDR_Q(0), ctx->tx_descs_dma); edma_read_reg(EDMA_REG_TPD_IDX_Q(0), &data); /* * Calculate hardware consumer index for Tx. */ hw_cons_idx = (data >> EDMA_TPD_CONS_IDX_SHIFT) & 0xffff; data &= ~(EDMA_TPD_PROD_IDX_MASK << EDMA_TPD_PROD_IDX_SHIFT); data |= hw_cons_idx; /* * Update producer index for Tx. */ edma_write_reg(EDMA_REG_TPD_IDX_Q(0), data); /* * Update SW consumer index register for Tx. */ edma_write_reg(EDMA_REG_TX_SW_CONS_IDX_Q(0), hw_cons_idx); edma_write_reg(EDMA_REG_TPD_RING_SIZE, TX_DESCS & EDMA_TPD_RING_SIZE_MASK); /* Disable TX FIFO low watermark and high watermark */ edma_write_reg(EDMA_REG_TXF_WATER_MARK, 0); addr_to_words(mac_addr, w1, w2); edma_write_reg(EDMA_REG_MAC_CTRL0, w1); edma_write_reg(EDMA_REG_MAC_CTRL1, w2); /* Load all of base address above */ edma_read_reg(EDMA_REG_TX_SRAM_PART, &data); data |= 1 << EDMA_LOAD_PTR_SHIFT; edma_write_reg(EDMA_REG_TX_SRAM_PART, data); ess_sw_init(); ess_disable_lookup(); for (i = 0; i < EDMA_NUM_IDT; i++) edma_write_reg(EDMA_REG_RSS_IDT(i), EDMA_RSS_IDT_VALUE); // Enable TX edma_read_reg(EDMA_REG_TXQ_CTRL, &data); data |= EDMA_TXQ_CTRL_TXQ_EN; edma_write_reg(EDMA_REG_TXQ_CTRL, data); ess_enable_lookup(); ess_sw_rd(S17_ARL_CTRL_REG, &data); data &= ~(0xFFFF); data |= 1; ess_sw_wr(S17_ARL_CTRL_REG, data); ess_sw_wr(S17_GLOFW_CTRL1_REG, 0x7f7f7f7f); ess_sw_wr(S17_P0STATUS_REG, 0x0000007E); // CPU-Port ess_sw_wr(S17_PxLOOKUP_CTRL_REG(0), S17_LOOKUP_CTRL_PORT_VLAN_EN | S17_LOOKUP_CTRL_PORTSTATE_FORWARD | S17_LOOKUP_CTRL_VLAN_MODE_DISABLE | S17_LOOKUP_CTRL_LEARN_EN_0 | 0x7e); // Switch Ports ess_sw_wr(S17_PxLOOKUP_CTRL_REG(1), S17_LOOKUP_CTRL_PORT_VLAN_EN | S17_LOOKUP_CTRL_PORTSTATE_FORWARD | S17_LOOKUP_CTRL_VLAN_MODE_DISABLE | S17_LOOKUP_CTRL_LEARN_EN_0 | 0x1); ess_sw_wr(S17_PxLOOKUP_CTRL_REG(2), S17_LOOKUP_CTRL_PORT_VLAN_EN | S17_LOOKUP_CTRL_PORTSTATE_FORWARD | S17_LOOKUP_CTRL_VLAN_MODE_DISABLE | S17_LOOKUP_CTRL_LEARN_EN_0 | 0x1); ess_sw_wr(S17_PxLOOKUP_CTRL_REG(3), S17_LOOKUP_CTRL_PORT_VLAN_EN | S17_LOOKUP_CTRL_PORTSTATE_FORWARD | S17_LOOKUP_CTRL_VLAN_MODE_DISABLE | S17_LOOKUP_CTRL_LEARN_EN_0 | 0x1); ess_sw_wr(S17_PxLOOKUP_CTRL_REG(4), S17_LOOKUP_CTRL_PORT_VLAN_EN | S17_LOOKUP_CTRL_PORTSTATE_FORWARD | S17_LOOKUP_CTRL_VLAN_MODE_DISABLE | S17_LOOKUP_CTRL_LEARN_EN_0 | 0x1); // WAN (Disabled) ess_sw_wr(S17_PxLOOKUP_CTRL_REG(5), S17_LOOKUP_CTRL_PORT_VLAN_EN | S17_LOOKUP_CTRL_PORTSTATE_FORWARD | S17_LOOKUP_CTRL_VLAN_MODE_DISABLE | S17_LOOKUP_CTRL_LEARN_EN_0 | 0x0); ess_sw_wr(S17_ATU_FUNC_REG, S17_ATU_FUNC_BUSY | S17_ATU_FUNC_FLUSH_ALL); printk(KERN_ERR "[avm_coredump_net] Setup done.\n"); printk(KERN_ERR "[avm_coredump_net] Waiting for link...\n"); for (i = 0; !has_link && i < 10000; i++) { // Found a link on one port if (get_port_status() != 0x0) { has_link = true; } avm_coredump_periodic(); mdelay(10); } if (!has_link) { printk(KERN_ERR "[avm_coredump_net] No link found.\n"); return -ENETDOWN; } avm_coredump_periodic(); printk(KERN_ERR "[avm_coredump_net] Link settled\n"); return 0; } int avm_coredump_net_send_eth(const void *data, unsigned int len) { unsigned int idx; struct edma_tx_desc *desc; void *packet_mem; u32 desc_idx_data; u16 prod_idx; u16 cons_idx; // Relax system udelay(50); edma_read_reg(EDMA_REG_TPD_IDX_Q(0), &desc_idx_data); prod_idx = (desc_idx_data >> EDMA_TPD_PROD_IDX_SHIFT) & EDMA_TPD_PROD_IDX_MASK; cons_idx = (desc_idx_data >> EDMA_TPD_CONS_IDX_SHIFT) & EDMA_TPD_CONS_IDX_MASK; if ((prod_idx + 1) % TX_DESCS == cons_idx) { // We would override the package that is currently consumed by the EDMA. return -EAGAIN; } // Advance to the new slot we're using idx = (prod_idx + 1) % TX_DESCS; desc = ctx->tx_descs + idx; packet_mem = ctx->packet_mem[idx]; memcpy(packet_mem, data, len); flush_cache_vmap((unsigned long)packet_mem, MAX_PACKET); desc->svlan_tag = 0; desc->len = cpu_to_le16(len); desc->word1 = (1 << EDMA_TPD_EOP_SHIFT); // Send to all port, bypassing switching desc->word3 = (0x3E << EDMA_TPD_PORT_BITMAP_SHIFT);// | (1 << EDMA_TPD_FROM_CPU_SHIFT); // Update queue prod_idx = idx; desc_idx_data = ((cons_idx & EDMA_TPD_CONS_IDX_MASK) << EDMA_TPD_CONS_IDX_SHIFT) | ((prod_idx & EDMA_TPD_PROD_IDX_MASK) << EDMA_TPD_PROD_IDX_SHIFT); //printk(KERN_ERR "[avm_coredump_net] Set new producer index to %d (0x%08x)\n", prod_idx, desc_idx_data); flush_cache_vmap((unsigned long)desc, sizeof(struct edma_tx_desc)); edma_write_reg(EDMA_REG_TPD_IDX_Q(0), desc_idx_data); return 0; } int avm_coredump_net_poll_eth(void) { ar7wdt_hw_trigger(); return 0; } int avm_coredump_net_shutdown(void) { u32 desc_idx_data; u16 prod_idx; u16 cons_idx; do { edma_read_reg(EDMA_REG_TPD_IDX_Q(0), &desc_idx_data); prod_idx = (desc_idx_data >> EDMA_TPD_PROD_IDX_SHIFT) & EDMA_TPD_PROD_IDX_MASK; cons_idx = (desc_idx_data >> EDMA_TPD_CONS_IDX_SHIFT) & EDMA_TPD_CONS_IDX_MASK; } while(prod_idx != cons_idx); udelay(250); return 0; } int avm_coredump_net_prepare_setup(void) { unsigned int i; ctx = kzalloc(sizeof(struct avm_coredump_net_ctx), GFP_KERNEL); if (ctx == NULL) { printk(KERN_ERR "[avm_coredump_net] could not allocate context\n"); return -ENOMEM; } ctx->tx_descs = dma_alloc_coherent(NULL, sizeof(struct edma_tx_desc) * TX_DESCS, &ctx->tx_descs_dma, GFP_DMA); if (ctx->tx_descs == NULL) { printk(KERN_ERR "[avm_coredump_net] could not allocate tx descs\n"); return -ENOMEM; } for (i = 0; i < TX_DESCS; i++) { ctx->packet_mem[i] = dma_alloc_coherent(NULL, MAX_PACKET * TX_DESCS, ctx->packet_mem_dma + i, GFP_DMA); if (ctx->packet_mem == NULL) { printk(KERN_ERR "[avm_coredump_net] could not allocate packet mem\n"); return -ENOMEM; } ctx->tx_descs[i].addr = cpu_to_le32(ctx->packet_mem_dma[i]); } // Technically this register is already mapped somewhere down in gcc // however its way easiert to create a new mapping for now. ctx->edma_base = ioremap(IPQ40XX_EDMA_BASE, 0x1000); ctx->mdio_ahb_cbcr = ioremap(GCC_MDIO_AHB_CBCR, 4); ctx->reset_reg = ioremap(GCC_ESS_BCR, 4); ctx->ess_regs = ioremap(IPQ40XX_NSS_BASE, 0x1000); ctx->mdio_base = ioremap(IPQ40XX_MDIO_BASE, 0x100); ctx->psgmiiphy_base = ioremap(PSGMIIPHY_BASE, 0x1000); printk(KERN_INFO "[avm_coredump_net] Prepared net driver.\n"); return 0; }