diff options
Diffstat (limited to 'cpsw/src/netif')
-rwxr-xr-x | cpsw/src/netif/FILES | 1 | ||||
-rwxr-xr-x | cpsw/src/netif/cache.c | 249 | ||||
-rwxr-xr-x | cpsw/src/netif/cp15.S | 588 | ||||
-rwxr-xr-x | cpsw/src/netif/cpsw.c | 1545 | ||||
-rwxr-xr-x | cpsw/src/netif/cpsw_bb.c | 161 | ||||
-rwxr-xr-x | cpsw/src/netif/cpswif.c | 2999 | ||||
-rw-r--r-- | cpsw/src/netif/delay.c | 6 | ||||
-rwxr-xr-x | cpsw/src/netif/mdio.c | 209 | ||||
-rwxr-xr-x | cpsw/src/netif/mmu.c | 184 | ||||
-rwxr-xr-x | cpsw/src/netif/phy.c | 402 |
10 files changed, 6344 insertions, 0 deletions
diff --git a/cpsw/src/netif/FILES b/cpsw/src/netif/FILES new file mode 100755 index 0000000..0411124 --- /dev/null +++ b/cpsw/src/netif/FILES @@ -0,0 +1 @@ +cpswif.c - lwIP Ethernet interface for CPSW based devices. diff --git a/cpsw/src/netif/cache.c b/cpsw/src/netif/cache.c new file mode 100755 index 0000000..699961b --- /dev/null +++ b/cpsw/src/netif/cache.c @@ -0,0 +1,249 @@ +/** + * \file cache.c + * + * \brief APIs for configuring Cache + * + * This file contains the APIs for configuring ARMv7a Cache +*/ + +/* +* Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ +*/ +/* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the +* distribution. +* +* Neither the name of Texas Instruments Incorporated nor the names of +* its contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +*/ +#include "cache.h" +#include "cp15.h" + +/***************************************************************************** +** INTERNAL MACRO DEFINITIONS +******************************************************************************/ +#define CORTEX_A8_L2EN (0x02) +#define PRIMARY_PART_CORTEX_A8 (0xC08) + +/***************************************************************************** +** FUNCTION DEFINITIONS +******************************************************************************/ +/** + * \brief Disables Cache. The levels/type of Cache to be disabled + * is passed as parameter. + * + * \param disFlag Caches to be disabled. + * 'disFlag' can take one of the below values. \n + * CACHE_ICACHE - To disable Instruction Cache \n + * CACHE_DCACHE - To disable Data/Unified Cache \n + * CACHE_ALL - To disable all levels of Cache + * + * \return None. + * + * \Note Disabling Data Cache disables Unified cache also, if present. + **/ +void CacheDisable(unsigned int disFlag) +{ + if(disFlag & CACHE_ICACHE) + { + /* + ** Disable I-Cache. The I-Cache invalidation is also done inside + ** CP15ICacheDisable(). + */ + CP15ICacheDisable(); + } + + /* Here D Cache Disabling disables Unified cache also */ + if(disFlag & CACHE_DCACHE) + { + /* + ** Disable D-Cache. The D-Cache Clean and Invalidation is also done + ** inside CP15DCacheDisable(). + */ + CP15DCacheDisable(); + + /* For Cortex A8, L2EN has to be disabled for L2 Cache */ + if(PRIMARY_PART_CORTEX_A8 == CP15MainIdPrimPartNumGet()) + { + CP15AuxControlFeatureDisable(CORTEX_A8_L2EN); + } + } +} + +/** + * \brief Enables Cache. The levels/type of Cache to be enabled + * is passed as parameter. + * + * \param enFlag Caches to be enabled. + * 'enFlag' can take one of the below values. \n + * CACHE_ICACHE - To enable Instruction Cache \n + * CACHE_DCACHE - To enable Data/Unified Cache \n + * CACHE_ALL - To enable all levels of Cache + * + * \return None. + * + * \Note Enabling Data Cache enables Unified cache also, if present. + **/ +void CacheEnable(unsigned int enFlag) +{ + if(enFlag & CACHE_ICACHE) + { + CP15ICacheFlush(); + CP15ICacheEnable(); + } + + if(enFlag & CACHE_DCACHE) + { + /* For Cortex A8, L2EN has to be enabled for L2 Cache */ + if(PRIMARY_PART_CORTEX_A8 == CP15MainIdPrimPartNumGet()) + { + CP15AuxControlFeatureEnable(CORTEX_A8_L2EN); + } + + CP15DCacheFlush(); + CP15DCacheEnable(); + } +} + +/** + * \brief This API invalidates the entire I-Cache + * + * \param None + * + * \return None. + * + **/ +void CacheInstInvalidateAll(void) +{ + CP15ICacheFlush(); +} + +/** + * \brief This API invalidates a section of I-Cache. + * + * \param startAddr Starting address to be invalidated + * \param numBytes The number of bytes to be invalidated + * + * \return None. + * + **/ +void CacheInstInvalidateBuff(unsigned int startAddr, unsigned int numBytes) +{ + CP15ICacheFlushBuff(startAddr, numBytes); +} + +/** + * \brief This API Cleans and Invalidates the entire Data Cache. + * + * \param None + * + * \return None. + * + **/ +void CacheDataCleanInvalidateAll(void) +{ + CP15DCacheCleanFlush(); +} + +/** + * \brief This API Cleans the entire Data Cache. + * + * \param None + * + * \return None. + * + **/ +void CacheDataCleanAll(void) +{ + CP15DCacheClean(); +} + +/** + * \brief This API Invalidates the entire Data Cache. + * + * \param None + * + * \return None. + * + **/ +void CacheDataInvalidateAll(void) +{ + CP15DCacheFlush(); +} + +/** + * \brief This API clean a section of D-Cache, upto PoC. This API + * can be used to make a buffer in D-Cache to be coherent + * with the memory. For example, If DMA engine has to access + * a memory area for transmitting, to make sure that the + * D-Cache values for the corresponding buffer is written to + * memory, this API can be used. + * + * \param startAddr Starting address of the buffer to be cleaned + * \param numBytes The number of bytes to be cleaned. + * + * \return None. + * + **/ +void CacheDataCleanBuff(unsigned int startAddr, unsigned int numBytes) +{ + CP15DCacheCleanBuff(startAddr, numBytes); +} + +/** + * \brief This API invalidates a section of D-Cache till PoC. With this + * API, we can make sure that the next read of the buffer happens + * from memory. This is required if any DMA engine has updated + * the memory area with any data, other than from the D-Cache. + * + * \param startAddr Starting address of the buffer to be invalidated + * \param numBytes The number of bytes to be invalidated + * + * \return None. + * + **/ +void CacheDataInvalidateBuff(unsigned int startAddr, unsigned int numBytes) +{ + CP15DCacheFlushBuff(startAddr, numBytes); +} + +/** + * \brief This API cleans and invalidates a section of D-Cache to PoC. + * + * \param startAddr Starting address of the buffer to be cleaned + * and invalidated + * \param numBytes The number of bytes to be cleaned and invalidated + * + * \return None. + * + **/ +void CacheDataCleanInvalidateBuff(unsigned int startAddr, unsigned int numBytes) +{ + CP15DCacheCleanFlushBuff(startAddr, numBytes); +} + +/***************************** End Of File ***********************************/ + diff --git a/cpsw/src/netif/cp15.S b/cpsw/src/netif/cp15.S new file mode 100755 index 0000000..530288b --- /dev/null +++ b/cpsw/src/netif/cp15.S @@ -0,0 +1,588 @@ +@****************************************************************************** +@ +@ cp15.S - CP15 coprocesser configuration APIs +@ +@****************************************************************************** +@ +@ Copyright (C) 2010 Texas InSTRuments Incorporated - http://www.ti.com/ +@ +@ +@ RediSTRibution and use in source and binary forms, with or without +@ modification, are permitted provided that the following conditions +@ are met: +@ +@ RediSTRibutions of source code must retain the above copyright +@ notice, this list of conditions and the following disclaimer. +@ +@ RediSTRibutions in binary form must reproduce the above copyright +@ notice, this list of conditions and the following disclaimer in the +@ documentation and/or other materials provided with the +@ diSTRibution. +@ +@ Neither the name of Texas InSTRuments Incorporated nor the names of +@ its contributors may be used to endorse or promote products derived +@ from this software without specific prior written permission. +@ +@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +@ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +@ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +@ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +@ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +@ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +@ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +@ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +@ +@****************************************************************************** + +@****************************** Global Symbols********************************* + .global CP15ICacheDisable + .global CP15DCacheDisable + .global CP15ICacheEnable + .global CP15DCacheEnable + .global CP15ICacheFlush + .global CP15DCacheCleanFlush + .global CP15DCacheClean + .global CP15DCacheFlush + .global CP15DCacheCleanBuff + .global CP15DCacheCleanFlushBuff + .global CP15DCacheFlushBuff + .global CP15ICacheFlushBuff + .global CP15Ttb0Set + .global CP15TlbInvalidate + .global CP15MMUDisable + .global CP15MMUEnable + .global CP15VectorBaseAddrSet + .global CP15BranchPredictorInvalidate + .global CP15BranchPredictionEnable + .global CP15BranchPredictionDisable + .global CP15DomainAccessClientSet + .global CP15ControlFeatureDisable + .global CP15ControlFeatureEnable + .global CP15TtbCtlTtb0Config + .global CP15AuxControlFeatureEnable + .global CP15AuxControlFeatureDisable + .global CP15MainIdPrimPartNumGet + +@**************************** Code section ************************************ + .text + + @ This code is assembled for ARM instructions + .code 32 + + +@***************************************************************************** +@ This API disable the InSTRuction cache. +@***************************************************************************** +CP15ICacheDisable: + PUSH {lr} + MRC p15, #0, r0, c1, c0, #0 + BIC r0, r0, #0x00001000 + MCR p15, #0, r0, c1, c0, #0 + BL CP15ICacheFlush + POP {lr} + BX lr + +@***************************************************************************** +@ This API disable the Data cache. +@***************************************************************************** +CP15DCacheDisable: + PUSH {r4-r11, lr} + MRC p15, #0, r0, c1, c0, #0 + BIC r0, r0, #0x00000004 + MCR p15, #0, r0, c1, c0, #0 + BL CP15DCacheCleanFlush + POP {r4-r11, lr} + BX lr + +@***************************************************************************** +@ This API enables I-cache +@***************************************************************************** +CP15ICacheEnable: + MRC p15, #0, r0, c1, c0, #0 + ORR r0, r0, #0x00001000 + MCR p15, #0, r0, c1, c0, #0 + BX lr + +@***************************************************************************** +@ This API enable the Data cache. +@***************************************************************************** +CP15DCacheEnable: + MRC p15, #0, r0, c1, c0, #0 + ORR r0, r0, #0x00000004 + MCR p15, #0, r0, c1, c0, #0 + BX lr + +@***************************************************************************** +@ This API Invalidates the entire Data/Unified Cache +@***************************************************************************** +CP15DCacheFlush: + PUSH {r4-r11} + DMB + MRC p15, #1, r0, c0, c0, #1 @ Read CLID register + ANDS r3, r0, #0x7000000 @ Get Level of Coherency + MOV r3, r3, lsr #23 + BEQ ffinished + MOV r10, #0 +floop1: + ADD r2, r10, r10, lsr #1 + MOV r1, r0, lsr r2 + AND r1, r1, #7 + CMP r1, #2 + BLT fskip + MCR p15, #2, r10, c0, c0, #0 + ISB + MRC p15, #1, r1, c0, c0, #0 + AND r2, r1, #7 + ADD r2, r2, #4 + LDR r4, _FLD_MAX_WAY + ANDS r4, r4, r1, lsr #3 + CLZ r5, r4 + LDR r7, _FLD_MAX_IDX + ANDS r7, r7, r1, lsr #13 +floop2: + MOV r9, r4 +floop3: + ORR r11, r10, r9, lsl r5 + ORR r11, r11, r7, lsl r2 + MCR p15, #0, r11, c7, c6, #2 + SUBS r9, r9, #1 + BGE floop3 + SUBS r7, r7, #1 + BGE floop2 +fskip: + ADD r10, r10, #2 + CMP r3, r10 + BGT floop1 + +ffinished: + DSB + ISB + POP {r4-r11} + BX lr + + +@***************************************************************************** +@ This API cleans the entire D Cache to PoC +@***************************************************************************** +CP15DCacheClean: + PUSH {r4-r11} + DMB + MRC p15, #1, r0, c0, c0, #1 @ Read CLID register + ANDS r3, r0, #0x7000000 @ Get Level of Coherency + MOV r3, r3, lsr #23 + BEQ cfinished + MOV r10, #0 +cloop1: + ADD r2, r10, r10, lsr #1 + MOV r1, r0, lsr r2 + AND r1, r1, #7 + CMP r1, #2 + BLT cskip + MCR p15, #2, r10, c0, c0, #0 + ISB + MRC p15, #1, r1, c0, c0, #0 + AND r2, r1, #7 + ADD r2, r2, #4 + LDR r4, _FLD_MAX_WAY + ANDS r4, r4, r1, lsr #3 + CLZ r5, r4 + LDR r7, _FLD_MAX_IDX + ANDS r7, r7, r1, lsr #13 +cloop2: + MOV r9, r4 +cloop3: + ORR r11, r10, r9, lsl r5 + ORR r11, r11, r7, lsl r2 + MCR p15, #0, r11, c7, c10, #2 + SUBS r9, r9, #1 + BGE cloop3 + SUBS r7, r7, #1 + BGE cloop2 +cskip: + ADD r10, r10, #2 + CMP r3, r10 + BGT cloop1 + +cfinished: + DSB + ISB + POP {r4-r11} + BX lr + +@***************************************************************************** +@ This API cleans and invalidates the entire D Cache to PoC +@***************************************************************************** +CP15DCacheCleanFlush: + PUSH {r4-r11} + DMB + MRC p15, #1, r0, c0, c0, #1 @ Read CLID register + ANDS r3, r0, #0x7000000 @ Get Level of Coherency + MOV r3, r3, lsr #23 + BEQ finished + MOV r10, #0 +loop1: + ADD r2, r10, r10, lsr #1 + MOV r1, r0, lsr r2 + AND r1, r1, #7 + CMP r1, #2 + BLT skip + MCR p15, #2, r10, c0, c0, #0 + ISB + MRC p15, #1, r1, c0, c0, #0 + AND r2, r1, #7 + ADD r2, r2, #4 + LDR r4, _FLD_MAX_WAY + ANDS r4, r4, r1, lsr #3 + CLZ r5, r4 + LDR r7, _FLD_MAX_IDX + ANDS r7, r7, r1, lsr #13 +loop2: + MOV r9, r4 +loop3: + ORR r11, r10, r9, lsl r5 + ORR r11, r11, r7, lsl r2 + MCR p15, #0, r11, c7, c14, #2 + SUBS r9, r9, #1 + BGE loop3 + SUBS r7, r7, #1 + BGE loop2 +skip: + ADD r10, r10, #2 + CMP r3, r10 + BGT loop1 + +finished: + DSB + ISB + POP {r4-r11} + BX lr + +@***************************************************************************** +@ This API invalidates entire I Cache +@***************************************************************************** +CP15ICacheFlush: + MOV r0, #0 + MCR p15, #0, r0, c7, c5, #0 + DSB + BX lr + +@***************************************************************************** +@ This API cleans the D-cache/Unified lines corresponding to the buffer +@ pointer upto the specified length to PoC. +@ r0 - Start Address +@ r1 - Number of bytes to be cleaned +@***************************************************************************** +CP15DCacheCleanBuff: + PUSH {r14} + ADD r14, r0, r1 @ Calculate the end address + DMB + MRC p15, #0, r2, c0, c0, #1 @ Read Cache Type Register + UBFX r2, r2, #16, #4 @ Extract the DMinLine + MOV r3, #2 + ADD r3, r3, r2 + MOV r2, #1 + LSL r2, r2, r3 @ Calculate the line size + + SUB r3, r2, #1 @ Calculate the mask + BIC r0, r0, r3 @ Align to cache line boundary + TST r3, r14 + BIC r14, r14, r3 + MCRNE p15, #0, r14, c7, c10, #1 @ Clean D/Unified to PoC by MVA + +cleanloop: + MCR p15, #0, r0 , c7, c10, #1 @ Clean D/Unified to PoC by MVA + ADDS r0, r0, r2 @ Go to next line + CMP r0, r14 + BLT cleanloop + + POP {r14} + DSB + BX lr + +@***************************************************************************** +@ This API cleans and invalidates the D-cache/Unified lines corresponding to +@ the buffer pointer upto the specified length to PoC. +@ r0 - Start Address +@ r1 - Number of bytes to be cleaned and flushed +@***************************************************************************** +CP15DCacheCleanFlushBuff: + PUSH {r14} + ADD r14, r0, r1 @ Calculate the end address + DMB + MRC p15, #0, r2, c0, c0, #1 @ Read Cache Type Register + UBFX r2, r2, #16, #4 @ Extract the DMinLine + MOV r3, #2 + ADD r3, r3, r2 + MOV r2, #1 + LSL r2, r2, r3 @ Calculate the line size + + SUB r3, r2, #1 @ Calculate the mask + BIC r0, r0, r3 @ Align to cache line boundary + TST r3, r14 + BIC r14, r14, r3 + MCRNE p15, #0, r14, c7, c14, #1 @ Clean and Flush D/U line to PoC + +cleanflushloop: + MCR p15, #0, r0 , c7, c14, #1 @ Clean and Flush D/U line to PoC + ADDS r0, r0, r2 @ Go to next line + CMP r0, r14 + BLT cleanflushloop + + POP {r14} + DSB + BX lr + +@***************************************************************************** +@ This API invalidates the D-cache/Unified lines corresponding to +@ the buffer pointer upto the specified length to PoC. +@ r0 - Start Address +@ r1 - Number of bytes to be flushed +@***************************************************************************** +CP15DCacheFlushBuff: + PUSH {r14} + ADD r14, r0, r1 @ Calculate the end address + DMB + MRC p15, #0, r2, c0, c0, #1 @ Read Cache Type Register + UBFX r2, r2, #16, #4 @ Extract the DMinLine + MOV r3, #2 + ADD r3, r3, r2 + MOV r2, #1 + LSL r2, r2, r3 @ Calculate the line size + + SUB r3, r2, #1 @ Calculate the mask + TST r3, r0 + BIC r0, r0, r3 @ Align to cache line boundary + MCRNE p15, #0, r0, c7, c14, #1 @ Clean and Flush D/U line to PoC + ADDNE r0, r0, r2 + TST r3, r14 + BIC r14, r14, r3 + MCRNE p15, #0, r14, c7, c14, #1 @ Clean and Flush D/U line to PoC + B dflushcmp + +dflushloop: + MCR p15, #0, r0 , c7, c6, #1 @ Flush D/U line to PoC + ADDS r0, r0, r2 @ Go to next line + +dflushcmp: + CMP r0, r14 + BLT dflushloop + POP {r14} + DSB + BX lr + +@***************************************************************************** +@ This API invlidates I-cache lines from the star address till the length +@ specified to PoU. +@ r0 - Start Address +@ r1 - Number of bytes to be cleaned +@***************************************************************************** +CP15ICacheFlushBuff: + PUSH {r14} + ADD r14, r0, r1 @ Calculate the end address + DMB + MRC p15, #0, r2, c0, c0, #1 @ Read Cache Type Register + UBFX r2, r2, #0, #4 @ Extract the DMinLine + MOV r3, #2 + ADD r3, r3, r2 + MOV r2, #1 + LSL r2, r2, r3 @ Calculate the line size + + SUB r3, r2, #1 @ Calculate the mask + BIC r0, r0, r3 @ Align to cache line boundary + TST r3, r14 + BIC r14, r14, r3 + MCRNE p15, #0, r14, c7, c5, #1 @ Invalidate by MVA to PoU + +iflushloop: + MCR p15, #0, r0, c7, c5, #1 @ Invalidate by MVA to PoU + ADDS r0, r0, r2 @ Go to next line + CMP r0, r14 + BLT iflushloop + + POP {r14} + DSB + BX lr + +@***************************************************************************** +@ Sets TTB0 Register +@ r0 - Translation Table Base Address +@***************************************************************************** +CP15Ttb0Set: + MCR p15, #0, r0, c2, c0, #0 + DMB + BX lr + +@***************************************************************************** +@ This API Invalidates the TLB +@***************************************************************************** +CP15TlbInvalidate: + MCR p15, #0, r0, c8, c7, #0 @ r0 value will be ignored + DSB + BX lr + +@***************************************************************************** +@ This API Disables MMU. +@***************************************************************************** +CP15MMUDisable: + MCR p15, #0, r0, c8, c7, #0 @ Invalidate TLB + MRC p15, #0, r0, c1, c0, #0 + BIC r0, r0, #1 + MCR p15, #0, r0, c1, c0, #0 @ Clear MMU bit + DSB + BX lr + +@***************************************************************************** +@ This API Enables MMU. +@***************************************************************************** +CP15MMUEnable: + MRC p15, #0, r0, c1, c0, #0 + ORR r0, r0, #0x001 + MCR p15, #0, r0, c1, c0, #0 @ Set MMU Enable bit + DSB + BX lr + +@***************************************************************************** +@ This API sets the interrupt vector table base address +@ r0 - Interrput Vector Base Address +@***************************************************************************** +CP15VectorBaseAddrSet: + MCR p15, #0, r0, c12, c0, #0 + DSB + BX lr + +@***************************************************************************** +@ This API invalidates the branch predictor +@***************************************************************************** +CP15BranchPredictorInvalidate: + MCR p15, #0, r0, c7, c5, #6 + ISB + BX lr + +@***************************************************************************** +@ This API enables the branch predictor +@***************************************************************************** +CP15BranchPredictionEnable: + MRC p15, #0, r0, c1, c0, #0 + ORR r0, r0, #0x00000800 + MCR p15, #0, r0, c1, c0, #0 + BX lr + +@***************************************************************************** +@ This API disables the branch predictor +@***************************************************************************** +CP15BranchPredictionDisable: + MRC p15, #0, r0, c1, c0, #0 + BIC r0, r0, #0x00000800 + MCR p15, #0, r0, c1, c0, #0 + BX lr + +@***************************************************************************** +@ This API sets the domain access to 'client' +@***************************************************************************** +CP15DomainAccessClientSet: + LDR r0, _CLIENTD + MCR p15, #0, r0, c3, c0, #0 + DSB + BX lr + + +@***************************************************************************** +@ This API Disables specified features in CP15 control register +@ r0 - features Features to disable in Coprocessor 15 control +@ register. +@ 'features' can take any OR a combination of the +@ below values. +@ CP15_CONTROL_TEXREMAP - TEX remap flag +@ CP15_CONTROL_ACCESSFLAG - Access flag Control +@ CP15_CONTROL_ALIGN_CHCK - Alignment Fault Checking +@ CP15_CONTROL_MMU - To enable MMU +@ +@ Note: Other fields of the CP15 c1 control register are not given here +@ as they are not of importance for StarterWare. However, optionally +@ they can also be ADDed. +@ +@***************************************************************************** +CP15ControlFeatureDisable: + MRC p15, #0, r1, c1, c0, #0 + BIC r0, r1, r0 + MCR p15, #0, r0, c1, c0, #0 + DSB + BX lr + +@***************************************************************************** +@ This API Enables specified features in CP15 control register +@ r0 - features Features to disable in Coprocessor 15 control +@ register. +@ 'features' can take any OR a combination of the +@ below values. +@ CP15_CONTROL_TEXREMAP - TEX remap flag +@ CP15_CONTROL_ACCESSFLAG - Access flag Control +@ CP15_CONTROL_ALIGN_CHCK - Alignment Fault Checking +@ CP15_CONTROL_MMU - To enable MMU +@ +@ Note: Other fields of the CP15 c1 control register are not given here +@ as they are not of importance for StarterWare. However, optionally +@ they can also be ADDed. +@ +@***************************************************************************** +CP15ControlFeatureEnable: + MRC p15, #0, r1, c1, c0, #0 + ORR r0, r1, r0 + MCR p15, #0, r0, c1, c0, #0 + DSB + BX lr + +@***************************************************************************** +@ This API Configures the TTB control register to use only TTB0 +@***************************************************************************** +CP15TtbCtlTtb0Config: + MOV r0, #0x0 + MCR p15, #0, r0, c2, c0, #2 + DSB + BX lr + +@***************************************************************************** +@ This API Sets the specified fields in Auxiliary Control Register +@ r0 - Bit Mask for the bits to be set in Auxiliary Control Register +@***************************************************************************** +CP15AuxControlFeatureEnable: + MRC p15, #0, r1, c1, c0, #1 + ORR r0, r0, r1 + MCR p15, #0, r0, c1, c0, #1 + DSB + BX lr + +@***************************************************************************** +@ This API Clears the specified fields in Auxiliary Control Register +@ r0 - Bit Mask for the bits to be cleared in Auxiliary Control Register +@***************************************************************************** +CP15AuxControlFeatureDisable: + MRC p15, #0, r1, c1, c0, #1 + BIC r0, r1, r0 + MCR p15, #0, r0, c1, c0, #1 + DSB + BX lr + +@***************************************************************************** +@ This API returns the main ID register in r0 +@***************************************************************************** +CP15MainIdPrimPartNumGet: + MRC p15, #0, r0, c0, c0, #0 + UBFX r0, r0, #4, #12 + BX lr + +_CLIENTD: + .word 0x55555555 +_FLD_MAX_WAY: + .word 0x3ff +_FLD_MAX_IDX: + .word 0x7ff + +@ +@ End of the file +@ + .end + diff --git a/cpsw/src/netif/cpsw.c b/cpsw/src/netif/cpsw.c new file mode 100755 index 0000000..e82595b --- /dev/null +++ b/cpsw/src/netif/cpsw.c @@ -0,0 +1,1545 @@ +/** + * \file cpsw.c + * + * \brief CPSW device abstraction layer APIs. + * + * This file contains the device abstraction layer APIs for CPSW RGMII. + */ + +/* +* Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ +*/ +/* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the +* distribution. +* +* Neither the name of Texas Instruments Incorporated nor the names of +* its contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +*/ + +#include "hw_types.h" +#include "cpsw.h" + +/******************************************************************************* +* INTERNAL MACRO DEFINITIONS +*******************************************************************************/ +#define CPSW_CORE_OFFSET (0x10u) +#define CPSW_MAX_HEADER_DESC (0x08u) +#define CPDMA_P0_DEF_TX_MAP (0x76543210u) +#define ALE_ENTRY_WORDS (0x03u) +#define CPDMA_ERR_CHANNEL_POS (0xFFu) +#define CPSW_PORT_DUAL_MAC_MODE (0x01u << \ + CPSW_PORT_P0_TX_IN_CTL_TX_IN_SEL_SHIFT) +#define CPSW_PORT_RATE_LIM_MODE (0x02u << \ + CPSW_PORT_P0_TX_IN_CTL_TX_IN_SEL_SHIFT) + +/******************************************************************************* +* API FUNCTION DEFINITIONS +*******************************************************************************/ +/** + * \brief Resets the CPSW Subsystem. + * + * \param baseAddr Base address of the CPSW Subsystem + * + * \return None + **/ +void CPSWSSReset(unsigned int baseAddr) +{ + /* Reset the CPSW */ + HWREG(baseAddr + CPSW_SS_SOFT_RESET) = CPSW_SS_SOFT_RESET_SOFT_RESET; + + while(HWREG(baseAddr + CPSW_SS_SOFT_RESET) + & CPSW_SS_SOFT_RESET_SOFT_RESET); +} + +/** + * \brief Force the CPGMAC_SL into gigabit mode if the input GMII_MTCLK has + * been stopped by the PHY + * + * \param baseAddr Base address of the CPSW Sliver Module registers. + * + * \return None + * + **/ +void CPSWSlGigModeForceEnable(unsigned int baseAddr) +{ + HWREG(baseAddr + CPSW_SL_MACCONTROL) |= CPSW_SL_MACCONTROL_GIG_FORCE; +} + +/** + * \brief Enables the fullduplex and gigabit mode to be selected + * from the FULLDUPLEX_IN and GIG_IN input signals and not from the + * fullduplex and gig bits in this register. The FULLDUPLEX_MODE bit + * reflects the actual fullduplex mode selected + * + * \param baseAddr Base address of the CPSW Sliver Module registers. + * + * \return None + * + **/ +void CPSWSlControlExtEnable(unsigned int baseAddr) +{ + HWREG(baseAddr + CPSW_SL_MACCONTROL) |= CPSW_SL_MACCONTROL_EXT_EN; +} + + +/** + * \brief Disables the CPGMAC_SL gigabit mode if the input GMII_MTCLK has + * been stopped by the PHY + * + * \param baseAddr Base address of the CPSW Sliver Module registers. + * + * \return None + * + **/ +void CPSWSlGigModeForceDisable(unsigned int baseAddr) +{ + HWREG(baseAddr + CPSW_SL_MACCONTROL) &= ~CPSW_SL_MACCONTROL_GIG_FORCE; +} + +/** + * \brief Sets the Transfer mode, 10/100 or gigabit mode and the duplex + * mode for the sliver. + * + * \param baseAddr Base address of the CPSW Sliver Module registers. + * \param mode The transfer mode + * 'mode' can take one of the below values. \n + * CPSW_SLIVER_NON_GIG_HALF_DUPLEX - 10/100 Mbps mode, half duplex. \n + * CPSW_SLIVER_NON_GIG_FULL_DUPLEX - 10/100 Mbps mode, full duplex. \n + * CPSW_SLIVER_GIG_FULL_DUPLEX - 1000 Mbps mode, full duplex. \n + * Note: for 10Mbps mode, CPSW_SLIVER_INBAND has to be configured. \n + * + * \return None + * + **/ +void CPSWSlTransferModeSet(unsigned int baseAddr, unsigned int mode) +{ + HWREG(baseAddr + CPSW_SL_MACCONTROL) &= ~(CPSW_SL_MACCONTROL_GIG + | CPSW_SL_MACCONTROL_FULLDUPLEX); + + HWREG(baseAddr + CPSW_SL_MACCONTROL) |= mode; +} + +/** + * \brief Returns the MAC Status. The value will be the contents of the MAC + * status register. + * + * \param baseAddr Base address of the CPSW Sliver Module registers. + * \param statFlag Status flags to be read. + * statFlag can take an OR combination of the below values. \n + * CPSW_SLIVER_STATE - The Sliver state. \n + * CPSW_SLIVER_EXT_GIG_INPUT_BIT - The EXT_GIG input bit mask.\n + * CPSW_SLIVER_EXT_FULL_DUPLEX_BIT - The EXT_FULLDUPLEX input + * bit mask. \n + * CPSW_SLIVER_RX_FLOWCTRL - The receive flow control active. \n + * CPSW_SLIVER_TX_FLOWCTRL - The transmit flow control. + * + * + * \return MAC Status + * The MAC status register value returned can be compared against + * the below tokens. \n + * CPSW_SLIVER_STATE_IDLE - The Sliver is in idle state. \n + * CPSW_SLIVER_EXT_GIG_INPUT_HIGH - The EXT_GIG input + * bit is in HIGH state.\n + * CPSW_SLIVER_EXT_FULL_DUPLEX_HIGH - The EXT_FULLDUPLEX input + * bit is in HIGH state. \n + * CPSW_SLIVER_RX_FLOWCTRL_ACTIVE - The receive flow control is + * active. \n + * CPSW_SLIVER_TX_FLOWCTRL_ACTIVE - The pause time period is + * observed for a received + * pause frame + * + **/ +unsigned int CPSWSlMACStatusGet(unsigned int baseAddr, unsigned int statFlag) +{ + /* Return the required status only */ + return (HWREG(baseAddr + CPSW_SL_MACSTATUS) & statFlag); +} + +/** + * \brief Resets the CPSW Sliver Logic. + * + * \param baseAddr Base address of the CPSW Sliver Module registers. + * + * \return None + * + **/ +void CPSWSlReset(unsigned int baseAddr) +{ + /* Reset the sliver logic */ + HWREG(baseAddr + CPSW_SL_SOFT_RESET) = CPSW_SL_SOFT_RESET_SOFT_RESET; + + /* Wait till the reset completes */ + while(CPSW_SL_SOFT_RESET_SOFT_RESET == + ((HWREG(baseAddr + CPSW_SL_SOFT_RESET)) + & CPSW_SL_SOFT_RESET_SOFT_RESET)); +} + + +/** + * \brief Sets the maximum length for received frame. + * + * \param baseAddr Base address of the CPSW Sliver Module registers. + * \param rxMaxLen Maximum length for a received frame + * The default value for 'rxMaxLen' is 1518. The maximum value + * which can be set is 16383. + * + * \return None + * + **/ +void CPSWSlRxMaxLenSet(unsigned int baseAddr, unsigned int rxMaxLen) +{ + /* Set the desired maximum length */ + HWREG(baseAddr + CPSW_SL_RX_MAXLEN) = rxMaxLen; +} + +/** + * \brief Enables GMII for the sliver. + * + * \param baseAddr Base address of the CPSW Sliver Module registers. + * + * \return None + **/ +void CPSWSlGMIIEnable(unsigned int baseAddr) +{ + HWREG(baseAddr + CPSW_SL_MACCONTROL) |= CPSW_SL_MACCONTROL_GMII_EN; +} + +/** + * \brief Enables RGMII for the sliver. + * + * \param baseAddr Base address of the CPSW Sliver Module registers. + * + * \return None + **/ +void CPSWSlRGMIIEnable(unsigned int baseAddr) +{ + HWREG(baseAddr + CPSW_SL_MACCONTROL) |= (CPSW_SL_MACCONTROL_GMII_EN + | CPSW_SL_MACCONTROL_IFCTL_A + | CPSW_SL_MACCONTROL_IFCTL_B); +} + +/** + * \brief Resets the CPSW Wrapper module. + * + * \param baseAddr Base address of the CPSW Wrapper Module + * + * \return None + **/ +void CPSWWrReset(unsigned int baseAddr) +{ + /* Reset the CPSW Wrapper */ + HWREG(baseAddr + CPSW_WR_SOFT_RESET) = CPSW_WR_SOFT_RESET_SOFT_RESET; + + while(HWREG(baseAddr + CPSW_WR_SOFT_RESET) + & CPSW_WR_SOFT_RESET_SOFT_RESET); +} + +/** + * \brief Resets the Control Register of CPSW Wrapper module + * + * \param baseAddr Base address of the CPSW Wrapper Module + * + * \return None + **/ +void CPSWWrControlRegReset(unsigned int baseAddr) +{ + /* Reset the CPSW Wrapper control Register */ + HWREG(baseAddr + CPSW_WR_CONTROL) = CPSW_WR_CONTROL_MMR_RESET; +} + +/** + * \brief Enables an interrupt for the specified core. + * + * \param baseAddr Base address of the CPSW Wrapper Module + * \param core Core number + * \param channel Channel number + * \param intFlag Interrupt to be enabled + * 'intFlag' can take one of the below values. \n + * CPSW_CORE_INT_RX_THRESH - RX threshold interrupt \n + * CPSW_CORE_INT_RX_PULSE - RX pulse interrupt \n + * CPSW_CORE_INT_TX_PULSE - TX pulse interrupt \n + * CPSW_CORE_INT_MISC - Miscellaneous interrupt + * + * \return None + **/ +void CPSWWrCoreIntEnable(unsigned int baseAddr, unsigned int core, + unsigned int channel, unsigned int intFlag) +{ + HWREG(baseAddr + CPSW_WR_C_RX_THRESH_EN(core) + intFlag) |= (1 << channel); +} + +/** + * \brief Disables an interrupt for the specified core. + * + * \param baseAddr Base address of thei CPSW Wrapper Module + * \param core Core number + * \param channel Channel number + * \param intFlag Interrupt to be disabled + * 'intFlag' can take one of the below values. \n + * CPSW_CORE_INT_RX_THRESH - RX threshold interrupt \n + * CPSW_CORE_INT_RX_PULSE - RX pulse interrupt \n + * CPSW_CORE_INT_TX_PULSE - TX pulse interrupt \n + * CPSW_CORE_INT_MISC - Miscellaneous interrupt + * + * \return None + **/ +void CPSWWrCoreIntDisable(unsigned int baseAddr, unsigned int core, + unsigned int channel, unsigned int intFlag) +{ + HWREG(baseAddr + CPSW_WR_C_RX_THRESH_EN(core) + intFlag) &= + ~(1 << channel); +} + +/** + * \brief Returns the interrupt status of the core for the specified + * channel + * + * \param baseAddr Base address of thei CPSW Wrapper Module + * \param core Core number + * \param channel Channel number + * \param intFlag Interrupt status to be read + * 'intFlag' can take one of the below values. \n + * CPSW_CORE_INT_RX_THRESH - RX threshold interrupt \n + * CPSW_CORE_INT_RX_PULSE - RX pulse interrupt \n + * CPSW_CORE_INT_TX_PULSE - TX pulse interrupt \n + * CPSW_CORE_INT_MISC - Miscellaneous interrupt + * + * \return same as intFlag if the status is set + * '0' if the status is cleared + **/ +unsigned int CPSWWrCoreIntStatusGet(unsigned int baseAddr, unsigned int core, + unsigned int channel, unsigned int intFlag) +{ + return (HWREG(baseAddr + CPSW_WR_C_RX_THRESH_STAT(core) + intFlag) + & (1 << channel)); +} + +/** + * \brief Returns the RGMII status requested. + * + * \param baseAddr Base address of the CPSW Wrapper Module + * \param statFlag Status to be checked + * 'statFlag' can take a combination of the below values. \n + * CPSW_RGMII2_DUPLEX - Duplex of RGMII2 \n + * CPSW_RGMII2_SPEED - Speed of RGMII2 \n + * CPSW_RGMII2_LINK_STAT - Link Status of RGMII2 \n + * CPSW_RGMII1_DUPLEX - Duplex of RGMII1 \n + * CPSW_RGMII1_SPEED - Speed of RGMII1 \n + * CPSW_RGMII1_LINK_STAT - Link Status of RGMII1 \n + * + * The returned value can be compared agains the below values \n + * CPSW_RGMII2_DUPLEX_FULL - RGMII2 full duplex \n + * CPSW_RGMII2_DUPLEX_HALF - RGMII2 half duplex \n + * CPSW_RGMII2_SPEED_10M - Speed is 10 Mbps \n + * CPSW_RGMII2_SPEED_100M - Speed is 100 Mbps \n + * CPSW_RGMII2_SPEED_1000M - Speed is 1000 Mbps \n + * CPSW_RGMII2_LINK_UP - RGMII2 link is up\n + * CPSW_RGMII2_LINK_DOWN - RGMII2 link is down \n + * CPSW_RGMII1_DUPLEX_FULL - RGMII1 full duplex \n + * CPSW_RGMII1_DUPLEX_HALF - RGMII1 half duplex \n + * CPSW_RGMII1_SPEED_10M - Speed is 10 Mbps \n + * CPSW_RGMII1_SPEED_100M - Speed is 100 Mbps \n + * CPSW_RGMII1_SPEED_1000M - Speed is 1000 Mbps \n + * CPSW_RGMII1_LINK_UP - RGMII1 link is up\n + * CPSW_RGMII1_LINK_DOWN - RGMII1 link is down \n + * + * \return Status of RGMII. Return value can be compared agains the same + * statFlag passed. + **/ +unsigned int CPSWWrRGMIIStatusGet(unsigned int baseAddr, unsigned int statFlag) +{ + return (HWREG(baseAddr + CPSW_WR_RGMII_CTL) & statFlag); +} + +/** + * \brief Initializes the ALE. The ALE logic is reset and the ALE table + * entries are cleared. + * + * \param baseAddr Base address of the ALE module + * + * \return None + **/ +void CPSWALEInit(unsigned int baseAddr) +{ + HWREG(baseAddr + CPSW_ALE_CONTROL) = (CPSW_ALE_CONTROL_CLEAR_TABLE + | CPSW_ALE_CONTROL_ENABLE_ALE); +} + +/** + * \brief Age Out Address Table. The Untouched ageable ALE table + * entries are cleared. + * + * \param baseAddr Base address of the ALE module + * + * \return None + **/ +void CPSWALEAgeOut(unsigned int baseAddr) +{ + HWREG(baseAddr + CPSW_ALE_CONTROL) |= CPSW_ALE_CONTROL_AGE_OUT_NOW; + + while(CPSW_ALE_CONTROL_AGE_OUT_NOW & (HWREG(baseAddr + CPSW_ALE_CONTROL))); + +} + +/** + * \brief Sets the Broadcast Packet Rate Limit + * + * \param baseAddr Base Address of the ALE module + * \param bplVal The Broadcast Packet Rate Limit Value + * + * \return None + * + **/ +void CPSWALEBroadcastRateLimitSet(unsigned int baseAddr, unsigned int portNum, + unsigned int bplVal) +{ + HWREG(baseAddr + CPSW_ALE_PORTCTL(portNum)) &= + ~CPSW_ALE_PORTCTL0_BCAST_LIMIT; + HWREG(baseAddr + CPSW_ALE_PORTCTL(portNum)) |= + (bplVal << CPSW_ALE_PORTCTL0_BCAST_LIMIT_SHIFT); +} + +/** + * \brief Sets the Multicast Packet Rate Limit + * + * \param baseAddr Base Address of the ALE module + * \param mplVal The Multicast Packet Rate Limit Value + * + * \return None + * + **/ +void CPSWALEMulticastRateLimitSet(unsigned int baseAddr, unsigned int portNum, + unsigned int mplVal) +{ + HWREG(baseAddr + CPSW_ALE_PORTCTL(portNum)) &= + ~CPSW_ALE_PORTCTL0_MCAST_LIMIT; + HWREG(baseAddr + CPSW_ALE_PORTCTL(portNum)) |= + (mplVal << CPSW_ALE_PORTCTL0_MCAST_LIMIT_SHIFT); +} + +/** + * \brief VLAN ID Ingress Check + * + * \param baseAddr Base Address of the ALE module + * + * \return None + * + **/ +void CPSWALEVIDIngressCheckSet(unsigned int baseAddr, unsigned int portNum) +{ + HWREG(baseAddr + CPSW_ALE_PORTCTL(portNum)) |= + CPSW_ALE_PORTCTL0_MCAST_LIMIT; +} + +/** + * \brief Sets the port state in the ALE for a given port + * + * \param baseAddr Base address of the ALE module + * \param portNum The port number + * \param portState The port state to be set + * 'portState' can take one of the below values \n + * CPSW_ALE_PORT_STATE_FWD - ALE state is Forward \n + * CPSW_ALE_PORT_STATE_LEARN - ALE state is Learn \n + * CPSW_ALE_PORT_STATE_BLOCKED - ALE state is Blocked \n + * CPSW_ALE_PORT_STATE_DISABLED - ALE state is Disabled + * + * \return None + **/ +void CPSWALEPortStateSet(unsigned int baseAddr, unsigned int portNum, + unsigned int portState) +{ + HWREG(baseAddr + CPSW_ALE_PORTCTL(portNum)) &= + ~CPSW_ALE_PORTCTL0_PORT_STATE; + + HWREG(baseAddr + CPSW_ALE_PORTCTL(portNum)) |= portState; +} + +/** + * \brief Sets VLAN Aware mode for ALE to Flood if VLAN not found + * + * \param baseAddr Base address of the ALE Module + * + * \return None + **/ +void CPSWALEVLANAwareSet(unsigned int baseAddr) +{ + HWREG(baseAddr + CPSW_ALE_CONTROL) |= CPSW_ALE_CONTROL_ALE_VLAN_AWARE; +} + +/** + * \brief Clears VLAN Aware mode for ALE to drop packets + * + * \param baseAddr Base address of the ALE Module + * + * \return None + **/ +void CPSWALEVLANAwareClear(unsigned int baseAddr) +{ + HWREG(baseAddr + CPSW_ALE_CONTROL) &= ~CPSW_ALE_CONTROL_ALE_VLAN_AWARE; +} + +/** + * \brief Configure Rate Limit to TX Mode + * + * \param baseAddr Base address of the ALE Module + * + * \return None + **/ +void CPSWALERateLimitTXMode(unsigned int baseAddr) +{ + HWREG(baseAddr + CPSW_ALE_CONTROL) |= CPSW_ALE_CONTROL_RATE_LIMIT_TX; +} + +/** + * \brief Configure Rate Limit to RX Mode + * + * \param baseAddr Base address of the ALE Module + * + * \return None + **/ +void CPSWALERateLimitRXMode(unsigned int baseAddr) +{ + HWREG(baseAddr + CPSW_ALE_CONTROL) &= ~CPSW_ALE_CONTROL_RATE_LIMIT_TX; +} + +/** + * \brief Enable Rate Limit for ALE + * + * \param baseAddr Base address of the ALE Module + * + * \return None + **/ +void CPSWALERateLimitEnable(unsigned int baseAddr) +{ + HWREG(baseAddr + CPSW_ALE_CONTROL) |= CPSW_ALE_CONTROL_ENABLE_RATE_LIMIT; +} + +/** + * \brief Disable Rate Limit for ALE + * + * \param baseAddr Base address of the ALE Module + * + * \return None + **/ +void CPSWALERateLimitDisable(unsigned int baseAddr) +{ + HWREG(baseAddr + CPSW_ALE_CONTROL) &= ~CPSW_ALE_CONTROL_ENABLE_RATE_LIMIT; +} + +/** + * \brief Enable MAC Authorization Mode for ALE + * + * \param baseAddr Base address of the ALE Module + * + * \return None + **/ +void CPSWALEAUTHModeSet(unsigned int baseAddr) +{ + HWREG(baseAddr + CPSW_ALE_CONTROL) |= CPSW_ALE_CONTROL_ENABLE_AUTH_MODE; +} + +/** + * \brief Disable MAC Authorization Mode for ALE + * + * \param baseAddr Base address of the ALE Module + * + * \return None + **/ +void CPSWALEAUTHModeClear(unsigned int baseAddr) +{ + HWREG(baseAddr + CPSW_ALE_CONTROL) &= ~CPSW_ALE_CONTROL_ENABLE_AUTH_MODE; +} + +/** + * \brief Sets an ALE table entry + * + * \param baseAddr Base address of the ALE Module + * \param aleTblIdx The Index of the table entry + * \param aleEntryPtr The address of the entry to be set + * + * \return None + **/ +void CPSWALETableEntrySet(unsigned int baseAddr, unsigned int aleTblIdx, + unsigned int *aleEntryPtr) +{ + unsigned int cnt; + + for (cnt = 0; cnt < ALE_ENTRY_WORDS; cnt++) + { + HWREG(baseAddr + CPSW_ALE_TBLW(cnt)) = *(aleEntryPtr + cnt); + } + + HWREG(baseAddr + CPSW_ALE_TBLCTL) = + aleTblIdx | CPSW_ALE_TBLCTL_WRITE_RDZ; +} + +/** + * \brief Returns an ALE table entry + * + * \param baseAddr Base address of the ALE Module + * \param aleTblIdx The Index of the table entry + * \param aleEntryPtr The address where the ALE entry to be written + * + * \return None + **/ +void CPSWALETableEntryGet(unsigned int baseAddr, unsigned int aleTblIdx, + unsigned int *aleEntryPtr) +{ + unsigned int cnt; + + HWREG(baseAddr + CPSW_ALE_TBLCTL) = aleTblIdx; + + for (cnt = 0; cnt < ALE_ENTRY_WORDS; cnt++) + { + *(aleEntryPtr + cnt) = HWREG(baseAddr + CPSW_ALE_TBLW(cnt)); + } +} + +/** + * \brief Returns the prescale value for ALE. The input clock is divided + * by this value to use in the broadcast/multicast rate limiters. + * + * \param baseAddr Base Address of the ALE module + * + * \return Prescale value + * + **/ +unsigned int CPSWALEPrescaleGet(unsigned int baseAddr) +{ + return (HWREG(baseAddr + CPSW_ALE_PRESCALE) + & CPSW_ALE_PRESCALE_ALE_PRESCALE); +} + +/** + * \brief Sets the prescale value for ALE. The input clock is divided + * by this value to use in the broadcast/multicast rate limiters. + * + * \param baseAddr Base Address of the ALE module + * \param psVal The prescale value + * + * \return None + * + **/ +void CPSWALEPrescaleSet(unsigned int baseAddr, unsigned int psVal) +{ + HWREG(baseAddr + CPSW_ALE_PRESCALE) |= psVal + & CPSW_ALE_PRESCALE_ALE_PRESCALE ; +} + +/** + * \brief Sets the Unknown VLAN Force Untagged Egress + * + * \param baseAddr Base Address of the ALE module + * \param ueVal The Unknown VLAN Fornce Untagged Egress value + * + * \return None + * + **/ +void CPSWALEUnknownUntaggedEgressSet(unsigned int baseAddr, unsigned int ueVal) +{ + HWREG(baseAddr + CPSW_ALE_UNKNOWN_VLAN) &= + ~CPSW_ALE_UNKNOWN_VLAN_UNKNOWN_FORCE_UNTA; + HWREG(baseAddr + CPSW_ALE_UNKNOWN_VLAN) |= + (ueVal << CPSW_ALE_UNKNOWN_VLAN_UNKNOWN_FORCE_UNTA_SHIFT); +} + +/** + * \brief Sets the Unknown VLAN Registered Multicast Flood Mask + * + * \param baseAddr Base Address of the ALE module + * \param rfmVal Unknown VLAN Registered Multicast Flood Mask Value + * + * \return None + * + **/ +void CPSWALEUnknownRegFloodMaskSet(unsigned int baseAddr, unsigned int rfmVal) +{ + HWREG(baseAddr + CPSW_ALE_UNKNOWN_VLAN) &= + ~CPSW_ALE_UNKNOWN_VLAN_UNKNOWN_MCAST_FLO; + HWREG(baseAddr + CPSW_ALE_UNKNOWN_VLAN) |= + (rfmVal << CPSW_ALE_UNKNOWN_VLAN_UNKNOWN_MCAST_FLO_SHIFT); +} + +/** + * \brief Sets the Unknown VLAN UnRegistered Multicast Flood Mask + * + * \param baseAddr Base Address of the ALE module + * \param ufmVal Unknown VLAN UnRegistered Multicast Flood Mask Value + * + * \return None + * + **/ +void CPSWALEUnknownUnRegFloodMaskSet(unsigned int baseAddr, unsigned int ufmVal) +{ + HWREG(baseAddr + CPSW_ALE_UNKNOWN_VLAN) &= + ~CPSW_ALE_UNKNOWN_VLAN_UNKNOWN_REG_MCAST; + HWREG(baseAddr + CPSW_ALE_UNKNOWN_VLAN) |= + (ufmVal << CPSW_ALE_UNKNOWN_VLAN_UNKNOWN_REG_MCAST_SHIFT); +} + +/** + * \brief Sets the Unknown VLAN Member List + * + * \param baseAddr Base Address of the ALE module + * \param mlVal Unknown VLAN UnRegistered Multicast Flood Mask Value + * + * \return None + * + **/ +void CPSWALEUnknownMemberListSet(unsigned int baseAddr, unsigned int mlVal) +{ + HWREG(baseAddr + CPSW_ALE_UNKNOWN_VLAN) &= + ~CPSW_ALE_UNKNOWN_VLAN_UNKNOWN_VLAN_MEM; + HWREG(baseAddr + CPSW_ALE_UNKNOWN_VLAN) |= + (mlVal << CPSW_ALE_UNKNOWN_VLAN_UNKNOWN_VLAN_MEM_SHIFT); +} + +/** + * \brief Enables the bypassing of the ALE logic + * + * \param baseAddr Base Address of the ALE module + * + * \return None + * + **/ +void CPSWALEBypassEnable(unsigned int baseAddr) +{ + HWREG(baseAddr + CPSW_ALE_CONTROL) |= CPSW_ALE_CONTROL_ALE_BYPASS; +} + +/** + * \brief Disables the bypassing of the ALE logic + * + * \param baseAddr Base Address of the ALE module + * + * \return None + * + **/ +void CPSWALEBypassDisable(unsigned int baseAddr) +{ + HWREG(baseAddr + CPSW_ALE_CONTROL) &= ~CPSW_ALE_CONTROL_ALE_BYPASS; +} + +/** + * \brief Enables the receive flow control for CPSW for a given port + * + * \param baseAddr Base Address of the CPSW subsystem + * \param portNum The port number + * + * \return None + * + **/ +void CPSWRxFlowControlEnable(unsigned int baseAddr, unsigned int portNum) +{ + HWREG(baseAddr + CPSW_SS_FLOW_CONTROL) |= (1 << portNum); +} + +/** + * \brief Disables the receive flow control for CPSW for a given port + * + * \param baseAddr Base Address of the CPSW subsystem + * \param portNum The port number + * + * \return None + * + **/ +void CPSWRxFlowControlDisable(unsigned int baseAddr, unsigned int portNum) +{ + HWREG(baseAddr + CPSW_SS_FLOW_CONTROL) &= ~(1 << portNum); +} + +/** + * \brief Enables the software idle mode, causing the switch fabric to stop + * forward packets at the next start of packet. + * + * \param baseAddr Base Address of the CPSW subsystem + * + * \return None + * + **/ +void CPSWSoftwareIdleEnable(unsigned int baseAddr) +{ + HWREG(baseAddr + CPSW_SS_SOFT_IDLE) |= CPSW_SS_SOFT_IDLE_SOFT_IDLE; +} + +/** + * \brief Disables the software idle mode, causing the switch fabric to + * forward packets at the next start of packet. + * + * \param baseAddr Base Address of the CPSW subsystem + * + * \return None + * + **/ +void CPSWSoftwareIdleDisable(unsigned int baseAddr, unsigned int portNum) +{ + (void) portNum; + HWREG(baseAddr + CPSW_SS_SOFT_IDLE) &= ~CPSW_SS_SOFT_IDLE_SOFT_IDLE; +} + +/** + * \brief Enables the CPSW statistics for the given port + * + * \param baseAddr Base Address of the CPSW subsystem + * \param portNum The port number + * + * \return None + * + **/ +void CPSWStatisticsEnable(unsigned int baseAddr) +{ + HWREG(baseAddr + CPSW_SS_STAT_PORT_EN) = CPSW_SS_STAT_PORT_EN_P0_STAT_EN + | CPSW_SS_STAT_PORT_EN_P1_STAT_EN + | CPSW_SS_STAT_PORT_EN_P2_STAT_EN; +} + +/** + * \brief Enables the VLAN aware mode for CPSW + * + * \param baseAddr Base Address of the CPSW subsystem + * + * \return None + * + **/ +void CPSWVLANAwareEnable(unsigned int baseAddr) +{ + HWREG(baseAddr + CPSW_SS_CONTROL) |= CPSW_SS_CONTROL_VLAN_AWARE; +} + +/** + * \brief Disables the VLAN aware mode for CPSW + * + * \param baseAddr Base Address of the CPSW subsystem + * + * \return None + * + **/ +void CPSWVLANAwareDisable(unsigned int baseAddr) +{ + HWREG(baseAddr + CPSW_SS_CONTROL) &= ~CPSW_SS_CONTROL_VLAN_AWARE; +} + +/** + * \brief Sets the ethernet address at the CPSW port + * + * \param baseAddr Base address of the CPSW Port Module registers + * \param ethAddr Start address of the 6 byte ethernet address + * + * \return None + * + **/ +void CPSWPortSrcAddrSet(unsigned int baseAddr, unsigned char *ethAddr) +{ + + HWREG(baseAddr + CPSW_PORT_SA_HI) = + ethAddr[0] + | (ethAddr[1] << CPSW_PORT_P1_SA_HI_MACSRCADDR_39_32_SHIFT) + | (ethAddr[2] << CPSW_PORT_P1_SA_HI_MACSRCADDR_31_24_SHIFT) + | (ethAddr[3] << CPSW_PORT_P1_SA_HI_MACSRCADDR_23_16_SHIFT); + HWREG(baseAddr + CPSW_PORT_SA_LO) = + ethAddr[4] + | (ethAddr[5] << CPSW_PORT_P1_SA_LO_MACSRCADDR_7_0_SHIFT); +} + +/** + * \brief Sets Dual Mac Mode for CPSW Port0 + * + * \param baseAddr Base address of the CPSW Host Port Module registers + * + * \return None + * + **/ +void CPSWHostPortDualMacModeSet(unsigned int baseAddr) +{ + HWREG(baseAddr + CPSW_PORT_TX_IN_CTL) &= ~CPSW_PORT_P0_TX_IN_CTL_TX_IN_SEL; + HWREG(baseAddr + CPSW_PORT_TX_IN_CTL) |= + CPSW_PORT_P0_TX_IN_CTL_TX_IN_DUAL_MAC; +} + +/** + * \brief Configures Port VLAN + * + * \param baseAddr Base address of the CPSW Port Module registers + * \param vlanId VLAN ID to be set + * \param cfiBit CFI value to be set + * \param vlanPri Port VLAN priority + * 'vlanId' can take a value from 0 to 0xFFF \n + * 'cfiBit' can be either 0 or 1. \n + * 'vlanPri' can be any value between and including 0 and 7. + * + * \return None + * + **/ +void CPSWPortVLANConfig(unsigned int baseAddr, unsigned int vlanId, + unsigned int cfiBit, unsigned int vlanPri) +{ + HWREG(baseAddr + CPSW_PORT_PORT_VLAN) = vlanId + | (cfiBit << CPSW_PORT_P2_PORT_VLAN_PORT_CFI_SHIFT) + | (vlanPri << CPSW_PORT_P2_PORT_VLAN_PORT_PRI_SHIFT); +} + +/** + * \brief Returns the requested CPSW Statistics + * + * \param baseAddr Base address of the CPSW Status Module registers. + * \param statReg Statistics Register to be read + * + * \return The requested statistics + * + **/ +unsigned int CPSWStatisticsGet(unsigned int baseAddr, unsigned int statReg) +{ + return (HWREG(baseAddr + statReg)); +} + +/** + * \brief Resets the CPDMA + * + * \param baseAddr Base address of the CPDMA Module registers. + * + * \return None + * + **/ +void CPSWCPDMAReset(unsigned int baseAddr) +{ + unsigned int cnt; + + /* Reset the CPDMA */ + HWREG(baseAddr + CPSW_CPDMA_CPDMA_SOFT_RESET) = + CPSW_CPDMA_CPDMA_SOFT_RESET_SOFT_RESET; + + /* Wait till the reset completes */ + while(HWREG(baseAddr + CPSW_CPDMA_CPDMA_SOFT_RESET) + & CPSW_CPDMA_CPDMA_SOFT_RESET_SOFT_RESET); + + /* Initialize all the header descriptor pointer registers */ + for(cnt = 0; cnt< CPSW_MAX_HEADER_DESC; cnt++) + { + HWREG(baseAddr + CPSW_CPDMA_TX_HDP(cnt)) = 0; + HWREG(baseAddr + CPSW_CPDMA_RX_HDP(cnt)) = 0; + HWREG(baseAddr + CPSW_CPDMA_TX_CP(cnt)) = 0; + HWREG(baseAddr + CPSW_CPDMA_RX_CP(cnt)) = 0; + } +} + +/** + * \brief Enables the TXPULSE Interrupt Generation. + * + * \param baseAddr Base address of the CPDMA Module registers. + * \param channel Channel number for which interrupt to be enabled + * + * \return None + * + **/ +void CPSWCPDMATxIntEnable(unsigned int baseAddr, unsigned int channel) +{ + HWREG(baseAddr + CPSW_CPDMA_TX_INTMASK_SET) |= (1 << channel); +} + +/** + * \brief Enables the RXPULSE Interrupt Generation. + * + * \param baseAddr Base address of the CPDMA Module registers. + * \param channel Channel number for which interrupt to be enabled + * + * \return None + * + **/ +void CPSWCPDMARxIntEnable(unsigned int baseAddr, unsigned int channel) +{ + HWREG(baseAddr + CPSW_CPDMA_RX_INTMASK_SET) |= (1 << channel); +} + +/** + * \brief Disables the TXPULSE Interrupt Generation. + * + * \param baseAddr Base address of the CPDMA Module registers. + * \param channel Channel number for which interrupt to be disabled + * + * \return None + * + **/ +void CPSWCPDMATxIntDisable(unsigned int baseAddr, unsigned int channel) +{ + HWREG(baseAddr + CPSW_CPDMA_TX_INTMASK_CLEAR) |= (1 << channel); + +} + +/** + * \brief Disables the RXPULSE Interrupt Generation. + * + * \param baseAddr Base address of the CPDMA Module registers. + * \param channel Channel number for which interrupt to be disabled + * + * \return None + * + **/ +void CPSWCPDMARxIntDisable(unsigned int baseAddr, unsigned int channel) +{ + HWREG(baseAddr + CPSW_CPDMA_RX_INTMASK_CLEAR) |= (1 << channel); + +} + +/** + * \brief API to enable the transmit in the TX Control Register. + * After the transmit is enabled, any write to TXHDP of + * a channel will start transmission + * + * \param baseAddr Base Address of the CPDMA module registers. + * + * \return None + * + **/ +void CPSWCPDMATxEnable(unsigned int baseAddr) +{ + HWREG(baseAddr + CPSW_CPDMA_TX_CONTROL) = CPSW_CPDMA_TX_CONTROL_TX_EN; +} + +/** + * \brief API to enable the receive in the RX Control Register. + * After the receive is enabled, and write to RXHDP of + * a channel, the data can be received in the destination + * specified by the corresponding RX buffer descriptor. + * + * \param baseAddr Base Address of the CPDMA module registers. + * + * \return None + * + **/ +void CPSWCPDMARxEnable(unsigned int baseAddr) +{ + HWREG(baseAddr + CPSW_CPDMA_RX_CONTROL) = CPSW_CPDMA_RX_CONTROL_RX_EN; +} + +/** + * \brief API to write the TX HDP register. If transmit is enabled, + * write to the TX HDP will immediately start transmission. + * The data will be taken from the buffer pointer of the TX buffer + * descriptor written to the TX HDP + * + * \param baseAddr Base Address of the CPDMA module registers. + * \param descHdr Address of the TX buffer descriptor + * \param channel Channel Number + * + * \return None + * + **/ +#include <bsp.h> +void CPSWCPDMATxHdrDescPtrWrite(unsigned int baseAddr, unsigned int descHdr, + unsigned int channel) +{ + HWREG(baseAddr + CPSW_CPDMA_TX_HDP(channel)) = descHdr; +} + +/** + * \brief API to write the RX HDP register. If receive is enabled, + * write to the RX HDP will enable data reception to point to + * the corresponding RX buffer descriptor's buffer pointer. + * + * \param baseAddr Base Address of the CPDMA module registers. + * \param descHdr Address of the RX buffer descriptor + * \param channel Channel Number + * + * \return None + * + **/ +void CPSWCPDMARxHdrDescPtrWrite(unsigned int baseAddr, unsigned int descHdr, + unsigned int channel) +{ + HWREG(baseAddr + CPSW_CPDMA_RX_HDP(channel)) = descHdr; +} + +/** + * \brief Writes the DMA End Of Interrupt Vector. + * + * \param baseAddr Base Address of the CPDMA module registers. + * \param eoiFlag Type of interrupt to acknowledge to the CPDMA + * 'eoiFlag' can take the following values \n + * CPSW_EOI_TX_PULSE - TX Pulse Interrupt \n + * CPSW_EOI_RX_PULSE - RX Pulse Interrupt \n + * CPSW_EOI_RX_THRESH_PULSE - RX Pulse Threshold Interrupt \n + * CPSW_EOI_MISC_PULSE - Misc Interrupt \n + * + * \return None + * + **/ +void CPSWCPDMAEndOfIntVectorWrite(unsigned int baseAddr, unsigned int eoiFlag) +{ + /* Acknowledge the CPDMA */ + HWREG(baseAddr + CPSW_CPDMA_CPDMA_EOI_VECTOR) = eoiFlag; +} + +/** + * \brief Reads the RX Completion Pointer for a specific channel + * + * \param baseAddr Base Address of the CPDMA module registers. + * \param channel Channel Number. + * + * \return RX Completion Pointer value. + * + **/ +unsigned int CPSWCPDMATxCPRead(unsigned int baseAddr, unsigned int channel) +{ + return HWREG(baseAddr + CPSW_CPDMA_TX_CP(channel)); +} + +/** + * \brief Writes the the TX Completion Pointer for a specific channel + * + * \param baseAddr Base Address of the CPDMA module registers. + * \param channel Channel Number. + * \param comPtr Completion Pointer Value to be written + * + * \return None + * + **/ +void CPSWCPDMATxCPWrite(unsigned int baseAddr, unsigned int channel, + unsigned int comPtr) +{ + HWREG(baseAddr + CPSW_CPDMA_TX_CP(channel)) = comPtr; +} + +/** + * \brief Reads the RX Completion Pointer for a specific channel + * + * \param baseAddr Base Address of the CPDMA module registers. + * \param channel Channel Number. + * + * \return RX Completion Pointer value. + * + **/ +unsigned int CPSWCPDMARxCPRead(unsigned int baseAddr, unsigned int channel) +{ + return HWREG(baseAddr + CPSW_CPDMA_RX_CP(channel)); +} + +/** + * \brief Writes the the RX Completion Pointer for a specific channel + * + * \param baseAddr Base Address of the CPDMA module registers. + * \param channel Channel Number. + * \param comPtr Completion Pointer Value to be written + * + * \return None + * + **/ +void CPSWCPDMARxCPWrite(unsigned int baseAddr, unsigned int channel, + unsigned int comPtr) +{ + HWREG(baseAddr + CPSW_CPDMA_RX_CP(channel)) = comPtr; +} + +/** + * \brief Set the free buffers for a specific channel + * + * \param baseAddr Base Address of the CPDMA module registers. + * \param channel Channel Number. + * \param nBuf Number of free buffers + * + * \return None + * + **/ +void CPSWCPDMANumFreeBufSet(unsigned int baseAddr, unsigned int channel, + unsigned int nBuf) +{ + HWREG(baseAddr + CPSW_CPDMA_RX_FREEBUFFER(channel)) = nBuf; +} + +/** + * \brief Returns the CPDMA Status. + * + * \param baseAddr Base Address of the CPDMA module registers. + * \param statFlag The status flags to be read + * 'statFlag' can take one of the following values \n + * CPDMA_STAT_IDLE - to check if CPDMA is idle. \n + * CPDMA_STAT_TX_HOST_ERR_CODE - TX host error code. \n + * CPDMA_STAT_TX_HOST_ERR_CHAN - TX host error channel. \n + * CPDMA_STAT_RX_HOST_ERR_CODE - RX host error code. \n + * CPDMA_STAT_RX_HOST_ERR_CHAN - RX host error channel. \n + * + * \return the DMA status for the status flag passed. + * The return values for CPDMA_STAT_IDLE are, \n + * CPDMA_STAT_IDLE - CPDMA is in idle state \n + * CPDMA_STAT_NOT_IDLE - CPDMA is not in idle state \n + * + * The return values for CPDMA_STAT_TX_HOST_ERR_CODE are, \n + * CPDMA_STAT_TX_NO_ERR - No error \n + * CPDMA_STAT_TX_SOP_ERR - SOP error \n + * CPDMA_STAT_TX_OWN_ERR - Ownership bit not + * set in SOP buffer \n + * CPDMA_STAT_TX_ZERO_DESC - Zero Next Buffer + * Descriptor Pointer Without EOP \n + * CPDMA_STAT_TX_ZERO_BUF_PTR - Zero Buffer Pointer \n + * CPDMA_STAT_TX_ZERO_BUF_LEN - Zero Buffer Length \n + * CPDMA_STAT_TX_PKT_LEN_ERR - Packet Length Error \n + * + * The return values for CPDMA_STAT_RX_HOST_ERR_CODE are, \n + * CPDMA_STAT_RXi_NO_ERR - No error \n + * CPDMA_STAT_RX_OWN_NOT_SET - Ownership bit not set in + input buffer \n + * CPDMA_STAT_RX_ZERO_BUF_PTR - Zero Buffer Pointer\n + * CPDMA_STAT_RX_ZERO_BUF_LEN - Zero Buffer Length on + * non-SOP descriptor \n + * CPDMA_STAT_RX_SOP_BUF_LEN_ERR - SOP buffer length not + * greater than offset\n + * + **/ +unsigned int CPSWCPDMAStatusGet(unsigned int baseAddr, unsigned int statFlag) +{ + return (((HWREG(baseAddr + CPSW_CPDMA_DMASTATUS)) & statFlag) + >> (statFlag & CPDMA_ERR_CHANNEL_POS)); +} + +/** + * \brief Configures the CPDMA module by writing the configuration value + * to the DMA control register. + * + * \param baseAddr Base Address of the CPDMA module registers + * \param cfg CPDMA configuration written to control register + * 'cfg' shall be CPDMA_CFG(tx_rlim, rx_cef, cmd_idle, + * rx_offlen_blk, rx_own, tx_ptype). \n + * The parameter 'tx_rlim' to CPDMA_CFG can take one of the below + * values, showing which all channels are rate-limited. \n + * CPDMA_CFG_TX_RATE_LIM_CH_7 \n + * CPDMA_CFG_TX_RATE_LIM_CH_7_TO_6 \n + * CPDMA_CFG_TX_RATE_LIM_CH_7_TO_5 \n + * CPDMA_CFG_TX_RATE_LIM_CH_7_TO_4 \n + * CPDMA_CFG_TX_RATE_LIM_CH_7_TO_3 \n + * CPDMA_CFG_TX_RATE_LIM_CH_7_TO_2 \n + * CPDMA_CFG_TX_RATE_LIM_CH_7_TO_1 \n + * CPDMA_CFG_TX_RATE_LIM_CH_7_TO_0 \n + * The parameter 'rx_cef' to CPDMA_CFG can take one of the below + * values \n + * CPDMA_CFG_COPY_ERR_FRAMES - To copy error frames to memory \n + * CPDMA_CFG_NO_COPY_ERR_FRAMES - Not to copy error frames \n + * The parameter 'cmd_idle' to CPDMA_CFG can take one of the below + * values \n + * CPDMA_CFG_IDLE_COMMAND - Idle commanded \n + * CPDMA_CFG_IDLE_COMMAND_NONE - Idle not commanded \n + * The parameter 'rx_offlen_blk' to CPDMA_CFG can take one of the below + * values \n + * CPDMA_CFG_BLOCK_RX_OFF_LEN_WRITE - Block the DMA writes to the + * offset/length field during + * packet processing. \n + * CPDMA_CFG_NOT_BLOCK_RX_OFF_LEN_WRITE - Do not Block the DMA writes + * to the offset/length field during + * packet processing. \n + * The parameter 'rx_own' to CPDMA_CFG can take one of the below + * values \n + * CPDMA_CFG_RX_OWN_1 - The CPDMA writes 1 to the ownership bit at + * the end of packet processing. \n + * CPDMA_CFG_RX_OWN_0 - The CPDMA writes 0 to the ownership bit at + * the end of packet processing. \n + * The parameter 'tx_ptype' to CPDMA_CFG can take one of the below + * values \n + * CPDMA_CFG_TX_PRI_ROUND_ROBIN - The next channel for transmit is + * chosen round-robin. \n + * CPDMA_CFG_TX_PRI_FIXED - The next channel for transmit is + * chosen priority based, channel 7 with the + * highest priority \n + * + * \return None + * + **/ +void CPSWCPDMAConfig(unsigned int baseAddr, unsigned int cfg) +{ + HWREG(baseAddr + CPSW_CPDMA_DMACONTROL) = cfg; +} + +/** + * \brief Enable the command idle mode for CPDMA. When this API is called, the + * CPSW stops all the reception and transmission. However, if receiving + * the current frame will be received completely before going to the idle + * state. Also, while transmitting, the contents in the fifo will be sent + * fully. + * + * \param baseAddr Base Address of the CPDMA module registers + * + * \return None + * + **/ +void CPSWCPDMACmdIdleEnable(unsigned int baseAddr) +{ + HWREG(baseAddr + CPSW_CPDMA_DMACONTROL) |= CPSW_CPDMA_DMACONTROL_CMD_IDLE; + + /* Wait till the state changes to idle */ + while((HWREG(baseAddr + CPSW_CPDMA_DMASTATUS) & CPSW_CPDMA_DMASTATUS_IDLE) + != CPSW_CPDMA_DMASTATUS_IDLE); +} + +/** + * \brief Disable the command idle mode for CPDMA. + * + * \param baseAddr Base Address of the CPDMA module registers + * + * \return None + * + **/ +void CPSWCPDMACmdIdleDisable(unsigned int baseAddr) +{ + HWREG(baseAddr + CPSW_CPDMA_DMACONTROL) &= ~CPSW_CPDMA_DMACONTROL_CMD_IDLE; +} + +/** + * \brief Sets the RX buffer offset value. The RX buffer offset will be + * written by the port into each frame SOP buffer descriptor + * buffer_offset field. The frame data will begin after the + * rx_buffer_offset value of bytes. This value will be used for + * all the channels . + * + * \param baseAddr Base Address of the CPDMA module registers + * \param bufOff Buffer offset value + * + * \return None + * + **/ +void CPSWCPDMARxBufOffsetSet(unsigned int baseAddr, unsigned int bufOff) +{ + HWREG(baseAddr + CPSW_CPDMA_RX_BUFFER_OFFSET) = bufOff; +} + +/** + * \brief Returns the raw transmit interrupt pending status. + * + * \param baseAddr Base Address of the CPDMA module registers + * \param chanMask Channel Mask + * 'chanMask' can be given for one or more channels. \n + * 0x01- for 0th channel, 0x80 for 7th channel, 0x81 for both 0th + * and 7th channel etc. \n + * + * \return Raw receive interrupt status \n + * bits for the 'chanMask' will be set if interrupt is pending \n + * bits for the 'chanMask' will be clear if interrupt is not + * pending \n + * + **/ +unsigned int CPSWCPDMATxIntStatRawGet(unsigned int baseAddr, + unsigned int chanMask) +{ + return (HWREG(baseAddr + CPSW_CPDMA_TX_INTSTAT_RAW) & chanMask); +} + +/** + * \brief Returns the masked transmit interrupt pending status. + * + * \param baseAddr Base Address of the CPDMA module registers + * \param chanMask Channel Mask + * 'chanMask' can be given for one or more channels. \n + * 0x01- for 0th channel, 0x80 for 7th channel, 0x81 for both 0th + * and 7th channel etc. \n + * + * \return Masked transmit interrupt status \n + * bits for the 'chanMask' will be set if interrupt is pending \n + * bits for the 'chanMask' will be cleared if interrupt is not + * pending \n + * + **/ +unsigned int CPSWCPDMATxIntStatMaskedGet(unsigned int baseAddr, + unsigned int chanMask) +{ + return (HWREG(baseAddr + CPSW_CPDMA_TX_INTSTAT_MASKED) & chanMask); +} + +/** + * \brief Returns the raw receive interrupt pending status. + * + * \param baseAddr Base Address of the CPDMA module registers + * \param chanMask Channel Mask + * \param intType Interrupt type + * 'chanMask' can be given for one or more channels. \n + * 0x01- for 0th channel, 0x80 for 7th channel, 0x81 for both 0th + * and 7th channel etc. \n + * 'intType' can take one of the following values. \n + * CPDMA_RX_INT_THRESH_PEND - RX threshold interrupt pending \n + * CPDMA_RX_INT_PULSE_PEND - RX pulse interrupt pending \n + * + * \return Raw receive interrupt status \n + * bits for the 'chanMask' will be set if interrupt is pending \n + * bits for the 'chanMask' will be cleared if interrupt is not + * pending \n + * + **/ +unsigned int CPSWCPDMARxIntStatRawGet(unsigned int baseAddr, + unsigned int chanMask, + unsigned int intType) +{ + return ((HWREG(baseAddr + CPSW_CPDMA_RX_INTSTAT_RAW) >> intType) + & chanMask); +} + +/** + * \brief Returns the masked receive interrupt pending status. + * + * \param baseAddr Base Address of the CPDMA module registers + * \param chanMask Channel Mask + * \param intType Interrupt type + * 'chanMask' can be given for one or more channels. \n + * 0x01- for 0th channel, 0x80 for 7th channel, 0x81 for both 0th + * and 7th channel etc. \n + * 'intType' can take one of the following values. \n + * CPDMA_RX_INT_THRESH_PEND - RX threshold interrupt pending \n + * CPDMA_RX_INT_PULSE_PEND - RX pulse interrupt pending \n + * + * \return Masked receive interrupt status \n + * bits for the 'chanMask' will be set if interrupt is pending \n + * bits for the 'chanMask' will be cleared if interrupt is not + * pending \n + * + **/ +unsigned int CPSWCPDMARxIntStatMaskedGet(unsigned int baseAddr, + unsigned int chanMask, + unsigned int intType) +{ + return ((HWREG(baseAddr + CPSW_CPDMA_RX_INTSTAT_MASKED) >> intType) + & chanMask); +} + +/** + * \brief Saves the CPSW register context. This can be used while going + * to power down mode where CPSW power will be cut down. + * + * \param contextPtr Pointer to the structure where CPSW register context + * need to be saved. + * + * \return None + * + **/ +void CPSWContextSave(CPSWCONTEXT *contextPtr) +{ + unsigned int idx; + unsigned int *cppiDest = (unsigned int*)contextPtr->cppiRamBase; + + CPSWCPDMACmdIdleEnable(contextPtr->cpdmaBase); + + /* Restore the CPPI RAM contents */ + for(idx = 0; idx < (CPSW_SIZE_CPPI_RAM / 4); idx++, cppiDest++) + { + contextPtr->cppiRam[idx] = *cppiDest; + } + + contextPtr->aleCtrl = HWREG(contextPtr->aleBase + CPSW_ALE_CONTROL); + contextPtr->alePortCtl[0] = HWREG(contextPtr->aleBase + CPSW_ALE_PORTCTL(0)); + contextPtr->alePortCtl[1] = HWREG(contextPtr->aleBase + CPSW_ALE_PORTCTL(1)); + contextPtr->alePortCtl[2] = HWREG(contextPtr->aleBase + CPSW_ALE_PORTCTL(2)); + + for(idx = 0; idx < CPSW_MAX_NUM_ALE_ENTRY; idx++) + { + CPSWALETableEntryGet(contextPtr->aleBase, idx, + &(contextPtr->aleEntry[idx * 3])); + } + + contextPtr->ssStatPortEn = HWREG(contextPtr->ssBase + CPSW_SS_STAT_PORT_EN); + contextPtr->port1SaHi = HWREG(contextPtr->port1Base + CPSW_PORT_SA_HI); + contextPtr->port1SaLo = HWREG(contextPtr->port1Base + CPSW_PORT_SA_LO); + contextPtr->port2SaHi = HWREG(contextPtr->port2Base + CPSW_PORT_SA_HI); + contextPtr->port2SaLo = HWREG(contextPtr->port2Base + CPSW_PORT_SA_LO); + contextPtr->port1TxInCtl = HWREG(contextPtr->port1Base + CPSW_PORT_TX_IN_CTL); + contextPtr->port2TxInCtl = HWREG(contextPtr->port2Base + CPSW_PORT_TX_IN_CTL); + contextPtr->port1Vlan = HWREG(contextPtr->port1Base + CPSW_PORT_PORT_VLAN); + contextPtr->port2Vlan = HWREG(contextPtr->port2Base + CPSW_PORT_PORT_VLAN); + contextPtr->cpdmaRxFB = HWREG(contextPtr->cpdmaBase + + CPSW_CPDMA_RX_FREEBUFFER(0)); + contextPtr->cpdmaTxCtl = HWREG(contextPtr->cpdmaBase + + CPSW_CPDMA_TX_CONTROL); + contextPtr->cpdmaRxCtl = HWREG(contextPtr->cpdmaBase + + CPSW_CPDMA_RX_CONTROL); + contextPtr->cpdmaRxHdp = HWREG(contextPtr->cpdmaBase + + CPSW_CPDMA_RX_HDP(0)); + contextPtr->txIntMaskSet = HWREG(contextPtr->cpdmaBase + + CPSW_CPDMA_TX_INTMASK_SET); + contextPtr->wrCoreIntTxPulse = HWREG(contextPtr->wrBase + + CPSW_WR_C_RX_THRESH_EN(0) + 0x04); + contextPtr->rxIntMaskSet = HWREG(contextPtr->cpdmaBase + + CPSW_CPDMA_RX_INTMASK_SET); + contextPtr->wrCoreIntRxPulse = HWREG(contextPtr->wrBase + + CPSW_WR_C_RX_THRESH_EN(0) + 0x08); + contextPtr->sl1MacCtl = HWREG(contextPtr->sl1Base + CPSW_SL_MACCONTROL); + contextPtr->sl2MacCtl = HWREG(contextPtr->sl2Base + CPSW_SL_MACCONTROL); +} + +/** + * \brief Restores the CPSW register context. This can be used while coming + * back from power down mode where CPSW power will be cut down. + * + * \param contextPtr Pointer to the structure where CPSW register context + * need to be restored from. + * + * \return None + * + **/ +void CPSWContextRestore(CPSWCONTEXT *contextPtr) +{ + unsigned int idx; + unsigned int *cppiDest = (unsigned int*)contextPtr->cppiRamBase; + + /* Restore the CPPI RAM contents */ + for(idx = 0; idx < (CPSW_SIZE_CPPI_RAM / 4); idx++, cppiDest++) + { + *cppiDest = contextPtr->cppiRam[idx] ; + } + + HWREG(contextPtr->aleBase + CPSW_ALE_CONTROL) = contextPtr->aleCtrl; + HWREG(contextPtr->aleBase + CPSW_ALE_PORTCTL(0)) = contextPtr->alePortCtl[0]; + HWREG(contextPtr->aleBase + CPSW_ALE_PORTCTL(1)) = contextPtr->alePortCtl[1]; + HWREG(contextPtr->aleBase + CPSW_ALE_PORTCTL(2)) = contextPtr->alePortCtl[2]; + + for(idx = 0; idx < CPSW_MAX_NUM_ALE_ENTRY; idx++) + { + CPSWALETableEntrySet(contextPtr->aleBase, idx, + &(contextPtr->aleEntry[idx * 3])); + } + + HWREG(contextPtr->ssBase + CPSW_SS_STAT_PORT_EN) = contextPtr->ssStatPortEn; + HWREG(contextPtr->port1Base + CPSW_PORT_SA_HI) = contextPtr->port1SaHi; + HWREG(contextPtr->port1Base + CPSW_PORT_SA_LO) = contextPtr->port1SaLo; + HWREG(contextPtr->port2Base + CPSW_PORT_SA_HI) = contextPtr->port2SaHi; + HWREG(contextPtr->port2Base + CPSW_PORT_SA_LO) = contextPtr->port2SaLo; + HWREG(contextPtr->port1Base + CPSW_PORT_TX_IN_CTL) = contextPtr->port1TxInCtl; + HWREG(contextPtr->port2Base + CPSW_PORT_TX_IN_CTL) = contextPtr->port2TxInCtl; + HWREG(contextPtr->port1Base + CPSW_PORT_PORT_VLAN) = contextPtr->port1Vlan; + HWREG(contextPtr->port2Base + CPSW_PORT_PORT_VLAN) = contextPtr->port2Vlan; + HWREG(contextPtr->cpdmaBase + CPSW_CPDMA_RX_FREEBUFFER(0)) = + contextPtr->cpdmaRxFB; + HWREG(contextPtr->cpdmaBase + CPSW_CPDMA_TX_CONTROL) + = contextPtr->cpdmaTxCtl; + HWREG(contextPtr->cpdmaBase + CPSW_CPDMA_RX_CONTROL) + = contextPtr->cpdmaRxCtl; + HWREG(contextPtr->cpdmaBase + CPSW_CPDMA_RX_HDP(0)) + = contextPtr->cpdmaRxHdp; + HWREG(contextPtr->cpdmaBase + CPSW_CPDMA_TX_INTMASK_SET) + = contextPtr->txIntMaskSet; + HWREG(contextPtr->wrBase + CPSW_WR_C_RX_THRESH_EN(0) + 0x04) + = contextPtr->wrCoreIntTxPulse; + HWREG(contextPtr->cpdmaBase + CPSW_CPDMA_RX_INTMASK_SET) + = contextPtr->rxIntMaskSet; + HWREG(contextPtr->wrBase + CPSW_WR_C_RX_THRESH_EN(0) + 0x08) + = contextPtr->wrCoreIntRxPulse; + HWREG(contextPtr->sl1Base + CPSW_SL_MACCONTROL) = contextPtr->sl1MacCtl; + HWREG(contextPtr->sl2Base + CPSW_SL_MACCONTROL) = contextPtr->sl2MacCtl; +} + + diff --git a/cpsw/src/netif/cpsw_bb.c b/cpsw/src/netif/cpsw_bb.c new file mode 100755 index 0000000..a35a6f0 --- /dev/null +++ b/cpsw/src/netif/cpsw_bb.c @@ -0,0 +1,161 @@ +/** + * \file cpsw.c + * + * \brief This file contains functions which configure CPSW instance + */ + +/* +* Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ +*/ +/* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the +* distribution. +* +* Neither the name of Texas Instruments Incorporated nor the names of +* its contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +*/ + +#include "soc_AM335x.h" +#include "hw_control_AM335x.h" +#include "hw_types.h" +#include "beaglebone.h" +#include "hw_cm_per.h" + +/****************************************************************************** +** INTERNAL MACRO DEFINITIONS +******************************************************************************/ +#define CPSW_MII_SEL_MODE (0x00u) +#define CPSW_MDIO_SEL_MODE (0x00u) +#define LEN_MAC_ADDR (0x06u) +#define OFFSET_MAC_ADDR (0x30u) + +/****************************************************************************** +** FUNCTION DEFINITIONS +******************************************************************************/ +/** + * \brief This function selects the CPSW pins for use in MII mode. + * + * \param None + * + * \return None. + * + */ +void CPSWPinMuxSetup(void) +{ + HWREG(SOC_CONTROL_REGS + CONTROL_CONF_MII1_RXERR) = + CONTROL_CONF_MII1_RXERR_CONF_MII1_RXERR_RXACTIVE | CPSW_MII_SEL_MODE; + HWREG(SOC_CONTROL_REGS + CONTROL_CONF_MII1_TXEN) = CPSW_MII_SEL_MODE; + HWREG(SOC_CONTROL_REGS + CONTROL_CONF_MII1_RXDV) = + CONTROL_CONF_MII1_RXDV_CONF_MII1_RXDV_RXACTIVE | CPSW_MII_SEL_MODE; + HWREG(SOC_CONTROL_REGS + CONTROL_CONF_MII1_TXD3) = CPSW_MII_SEL_MODE; + HWREG(SOC_CONTROL_REGS + CONTROL_CONF_MII1_TXD2) = CPSW_MII_SEL_MODE; + HWREG(SOC_CONTROL_REGS + CONTROL_CONF_MII1_TXD1) = CPSW_MII_SEL_MODE; + HWREG(SOC_CONTROL_REGS + CONTROL_CONF_MII1_TXD0) = CPSW_MII_SEL_MODE; + HWREG(SOC_CONTROL_REGS + CONTROL_CONF_MII1_TXCLK) = + CONTROL_CONF_MII1_TXCLK_CONF_MII1_TXCLK_RXACTIVE | CPSW_MII_SEL_MODE; + HWREG(SOC_CONTROL_REGS + CONTROL_CONF_MII1_RXCLK) = + CONTROL_CONF_MII1_RXCLK_CONF_MII1_RXCLK_RXACTIVE | CPSW_MII_SEL_MODE; + HWREG(SOC_CONTROL_REGS + CONTROL_CONF_MII1_RXD3) = + CONTROL_CONF_MII1_RXD3_CONF_MII1_RXD3_RXACTIVE | CPSW_MII_SEL_MODE; + HWREG(SOC_CONTROL_REGS + CONTROL_CONF_MII1_RXD2) = + CONTROL_CONF_MII1_RXD2_CONF_MII1_RXD2_RXACTIVE | CPSW_MII_SEL_MODE; + HWREG(SOC_CONTROL_REGS + CONTROL_CONF_MII1_RXD1) = + CONTROL_CONF_MII1_RXD1_CONF_MII1_RXD1_RXACTIVE | CPSW_MII_SEL_MODE; + HWREG(SOC_CONTROL_REGS + CONTROL_CONF_MII1_RXD0) = + CONTROL_CONF_MII1_RXD0_CONF_MII1_RXD0_RXACTIVE | CPSW_MII_SEL_MODE; + HWREG(SOC_CONTROL_REGS + CONTROL_CONF_MII1_COL) = + CONTROL_CONF_MII1_COL_CONF_MII1_COL_RXACTIVE | CPSW_MII_SEL_MODE; + HWREG(SOC_CONTROL_REGS + CONTROL_CONF_MII1_CRS) = + CONTROL_CONF_MII1_CRS_CONF_MII1_CRS_RXACTIVE | CPSW_MII_SEL_MODE; + HWREG(SOC_CONTROL_REGS + CONTROL_CONF_MDIO_DATA) = + CONTROL_CONF_MDIO_DATA_CONF_MDIO_DATA_RXACTIVE + | CONTROL_CONF_MDIO_DATA_CONF_MDIO_DATA_PUTYPESEL + | CPSW_MDIO_SEL_MODE; + HWREG(SOC_CONTROL_REGS + CONTROL_CONF_MDIO_CLK) = + CONTROL_CONF_MDIO_CLK_CONF_MDIO_CLK_PUTYPESEL | CPSW_MDIO_SEL_MODE; +} + +/** + * \brief Enables CPSW clocks + * + * \param None + * + * \return None. + */ +void CPSWClkEnable(void) +{ + HWREG(SOC_PRCM_REGS + CM_PER_CPGMAC0_CLKCTRL) = + CM_PER_CPGMAC0_CLKCTRL_MODULEMODE_ENABLE; + + while(0 != (HWREG(SOC_PRCM_REGS + CM_PER_CPGMAC0_CLKCTRL) + & CM_PER_CPGMAC0_CLKCTRL_IDLEST)); + + HWREG(SOC_PRCM_REGS + CM_PER_CPSW_CLKSTCTRL) = + CM_PER_CPSW_CLKSTCTRL_CLKTRCTRL_SW_WKUP; + + while(0 == (HWREG(SOC_PRCM_REGS + CM_PER_CPSW_CLKSTCTRL) + & CM_PER_CPSW_CLKSTCTRL_CLKACTIVITY_CPSW_125MHZ_GCLK)); +} + +/** + * \brief This function sets the MII mode for both ports + * + * \param None + * + * \return None. + */ +void EVMPortMIIModeSelect(void) +{ + /* Select MII, Internal Delay mode */ + HWREG(SOC_CONTROL_REGS + CONTROL_GMII_SEL) = 0x00; +} + +/** + * \brief This function returns the MAC address for the EVM + * + * \param addrIdx the MAC address index. + * \param macAddr the Pointer where the MAC address shall be stored + * 'addrIdx' can be either 0 or 1 + * + * \return None. + */ +void EVMMACAddrGet(unsigned int addrIdx, unsigned char *macAddr) +{ + macAddr[0] = (HWREG(SOC_CONTROL_REGS + CONTROL_MAC_ID_LO(addrIdx)) + >> 8) & 0xFF; + macAddr[1] = (HWREG(SOC_CONTROL_REGS + CONTROL_MAC_ID_LO(addrIdx))) + & 0xFF; + macAddr[2] = (HWREG(SOC_CONTROL_REGS + CONTROL_MAC_ID_HI(addrIdx)) + >> 24) & 0xFF; + macAddr[3] = (HWREG(SOC_CONTROL_REGS + CONTROL_MAC_ID_HI(addrIdx)) + >> 16) & 0xFF; + macAddr[4] = (HWREG(SOC_CONTROL_REGS + CONTROL_MAC_ID_HI(addrIdx)) + >> 8) & 0xFF; + macAddr[5] = (HWREG(SOC_CONTROL_REGS + CONTROL_MAC_ID_HI(addrIdx))) + & 0xFF; +} + +/****************************** End Of File *********************************/ diff --git a/cpsw/src/netif/cpswif.c b/cpsw/src/netif/cpswif.c new file mode 100755 index 0000000..9ff45b0 --- /dev/null +++ b/cpsw/src/netif/cpswif.c @@ -0,0 +1,2999 @@ +/** + * @file - cpswif.c + * lwIP Ethernet interface for CPSW port + * + */ + +/** + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels <adam@sics.se> + * + */ + +/** + * Copyright (c) 2010 Texas Instruments Incorporated + * + * This file is dervied from the "ethernetif.c" skeleton Ethernet network + * interface driver for lwIP. + */ +#include <semaphore.h> +#include <bsp.h> +#include <sched.h> + +#include "lwip/opt.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/sys.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "netif/etharp.h" +#include "netif/ppp/pppoe.h" +#include "lwip/err.h" +#include "netif/cpswif.h" +#include "arch/cc.h" + +/* DriverLib Header Files required for this interface driver. */ +#include "cpsw.h" +#include "mdio.h" +#include "delay.h" +#include "phy.h" +#include "cache.h" + +#include "lwiplib.h" + +/* CPPI RAM size in bytes */ +#ifndef SIZE_CPPI_RAM +#define SIZE_CPPI_RAM 0x2000 +#endif + +#define SIZEOF_STRUCT_PBUF LWIP_MEM_ALIGN_SIZE(sizeof(struct pbuf)) + +#define PORT_1 0x0 +#define PORT_2 0x1 +#define PORT_0_MASK 0x1 +#define PORT_1_MASK 0x2 +#define PORT_2_MASK 0x4 +#define HOST_PORT_MASK PORT_0_MASK +#define SLAVE_PORT_MASK(slv_port_num) (1 << slv_port_num) +#define PORT_MASK (0x7) +#define INDV_PORT_MASK(slv_port_num) (1 << slv_port_num) + +#define ENTRY_TYPE 0x30 +#define ENTRY_TYPE_IDX 7 +#define ENTRY_FREE 0 + +/* MDIO input and output frequencies in Hz */ +#define MDIO_FREQ_INPUT 125000000 +#define MDIO_FREQ_OUTPUT 1000000 + +#define CPDMA_BUF_DESC_OWNER 0x20000000 +#define CPDMA_BUF_DESC_SOP 0x80000000 +#define CPDMA_BUF_DESC_EOP 0x40000000 +#define CPDMA_BUF_DESC_EOQ 0x10000000 +#define CPDMA_BUF_DESC_FROM_PORT 0x70000 +#define CPDMA_BUF_DESC_FROM_PORT_SHIFT 16 +#define CPDMA_BUF_DESC_TO_PORT(port) ((port << 16) | 0x100000) +#define CPDMA_BD_LEN_MASK 0xFFFF +#define CPDMA_BD_PKTLEN_MASK 0xFFFF + +#define MAX_TRANSFER_UNIT 1500 +#define PBUF_LEN_MAX 1520 + +#define MIN_PKT_LEN 60 + +/* Define those to better describe the network interface. */ +#define IFNAME0 'e' +#define IFNAME1 'n' + +#define MASK_LOWER_4BITS_BYTE (0x0F) +#define MASK UPPER_4BITS_BYTE (0xF0) + +#define MASK_BROADCAST_ADDR (0xFF) +#define MASK_MULTICAST_ADDR (0x01) + +#define ALE_ENTRY_VLAN 0x20 +#define ALE_ENTRY_VLANUCAST 0x30 +#define ALE_ENTRY_UCAST 0x10 +#define ALE_ENTRY_MCAST 0xD0 +#define ALE_ENTRY_OUI (0x80) +#define ALE_ENTRY_ADDR (0x10) +#define ALE_ENTRY_VLAN_ADDR (0x30) +#define ALE_VLAN_ENTRY_MEMBER_LIST 0 +#define ALE_VLAN_ENTRY_FRC_UNTAG_EGR 3 +#define ALE_VLAN_ENTRY_MCAST_UNREG (1) +#define ALE_VLAN_ENTRY_MCAST_REG (2) +#define ALE_VLAN_ENTRY_ID (3) +#define ALE_VLAN_ID_MASK (0x0FFF) +#define ALE_VLAN_ENTRY_ID_BIT0_BIT7 6 +#define ALE_VLAN_ENTRY_TYPE_ID_BIT8_BIT11 7 +#define ALE_VLAN_ENTRY_TYPE_ID_BIT8_BIT11_ALIGN (0x08) +#define ALE_VLANUCAST_ENTRY_ID_BIT0_BIT7 6 +#define ALE_VLANUCAST_ENTRY_TYPE_ID_BIT8_BIT11 7 +#define ALE_UCAST_ENTRY_TYPE 7 +#define ALE_UCAST_TYPE_MASK (0xC0) +#define ALE_UCAST_TYPE_SHIFT (6) +#define ALE_UCAST_TYPE_PERSISTANT (0x00) +#define ALE_UCAST_TYPE_UNTOUCHED (0x40) +#define ALE_UCAST_TYPE_OUI (0x80) +#define ALE_UCAST_TYPE_TOUCHED (0xC0) +#define ALE_UCAST_ENTRY_DLR_PORT_BLK_SEC 8 +#define ALE_UCAST_ENTRY_DLR_BLK_SEC_MASK (0x03) +#define ALE_UCAST_ENTRY_PORT_SHIFT 2 +#define ALE_MCAST_ENTRY_TYPE_FWD_STATE 7 +#define ALE_MCAST_ENTRY_TYPE_FWD_STATE_SHIFT (6) +#define ALE_MCAST_ENTRY_PORTMASK_SUP 8 +#define ALE_MCAST_ENTRY_SUPER_MASK (0x02) +#define ALE_MCAST_ENTRY_SUPER_SHIFT (1) +#define ALE_MCAST_ENTRY_PORT_MASK (0x1C) +#define ALE_MCAST_ENTRY_PORTMASK_SHIFT 2 + +#define SELECT_10_HALF (1 << 0) +#define SELECT_10_FULL (1 << 1) +#define SELECT_100_HALF (1 << 2) +#define SELECT_100_FULL (1 << 3) +#define SELECT_1000_HALF (1 << 4) +#define SELECT_1000_FULL (1 << 5) + +#define SELECT_SPEED_10 (0) +#define SELECT_SPEED_100 (1) +#define SELECT_SPEED_1000 (2) + +#define SELECT_FORCED (0) +#define SELECT_AUTONEG (1) +#define SELECT_BOTH (2) + +#define SELECT_HALF_DUPLEX (0) +#define SELECT_FULL_DUPLEX (1) + + +#define SEM_DEFAULT_PSHARED (0) +#define SEM_INITIAL_VALUE (0) +#define RX_PRIORITY (0) +#define TX_PRIORITY (0) + +/** + * RX thread stack size. + */ +#define RX_THREAD_STACKSIZE 2048 + + /** + * RX thread stack size. + */ +#define TX_THREAD_STACKSIZE 2048 + +static void rx_thread_function(void* arg); +static void tx_thread_function(void* arg); + +/* TX Buffer descriptor data structure */ +struct cpdma_tx_bd { + volatile struct cpdma_tx_bd *next; + volatile u32_t bufptr; + volatile u32_t bufoff_len; + volatile u32_t flags_pktlen; + + /* helper to know which pbuf this tx bd corresponds to */ + volatile struct pbuf *pbuf; +}cpdma_tx_bd; + +/* RX Buffer descriptor data structure */ +struct cpdma_rx_bd { + volatile struct cpdma_rx_bd *next; + volatile u32_t bufptr; + volatile u32_t bufoff_len; + volatile u32_t flags_pktlen; + + /* helper to know which pbuf this rx bd corresponds to */ + volatile struct pbuf *pbuf; +}cpdma_rx_bd; + +/** + * Helper struct to hold the data used to operate on the receive + * buffer descriptor ring + */ +struct rxch { + /* The head of the bd chain which can be allocated for reception*/ + volatile struct cpdma_rx_bd *free_head; + + /* The head of the bd chain which is receiving data */ + volatile struct cpdma_rx_bd *recv_head; + + /* The tail of the bd chain which is receiving data */ + volatile struct cpdma_rx_bd *recv_tail; + + /* The number of free bd's, which can be allocated for reception */ + volatile u32_t free_num; +}rxch; + +/** + * Helper struct to hold the data used to operate on the transmit + * buffer descriptor ring + */ +struct txch { + /* The bd which is free to send */ + volatile struct cpdma_tx_bd *free_head; + + /* The head of the bd chain, being sent */ + volatile struct cpdma_tx_bd *send_head; + + /* The tail of the bd chain, being sent */ + volatile struct cpdma_tx_bd *send_tail; + + /* The number of free bd's, which can be sent */ + volatile u32_t free_num; +}txch; + +volatile struct cpdma_tx_bd *free_head; + +/** + * Slave port information + */ +struct cpswport{ + u32_t port_base; + u32_t sliver_base; + u32_t phy_addr; + + /* The PHY is capable of GitaBit or Not */ + u32_t phy_gbps; +}cpswport; + +/** + * CPSW instance information + */ +struct cpswinst{ + /* Base addresses */ + u32_t ss_base; + u32_t mdio_base; + u32_t wrpr_base; + u32_t ale_base; + u32_t cpdma_base; + u32_t cppi_ram_base; + u32_t host_port_base; + + /* Slave port information */ + struct cpswport port[MAX_SLAVEPORT_PER_INST]; + + /* The tx/rx channels for the interface */ + struct txch txch; + struct rxch rxch; + sys_thread_t RxThread; /**< RX receive thread data object pointer */ + sem_t rxsem; + sys_thread_t TxThread; /**< RX receive thread data object pointer */ + sem_t txsem; + sys_mutex_t txmtx; +}cpswinst; + +/* Defining set of CPSW base addresses for all the instances */ +static struct cpswinst cpsw_inst_data[MAX_CPSW_INST]; + +/** +* Function to setup the instance parameters inside the interface +* @param cpswif The interface structure pointer +* @return None. +*/ +static void +cpswif_inst_config(struct cpswportif *cpswif) { + u32_t inst_num = cpswif->inst_num; + struct cpswinst *cpswinst = &cpsw_inst_data[inst_num]; + + /** + * Code is added for only instance 0. If more instances + * are there, assign base addresses and phy info here + */ + if(0 == inst_num) { + cpswinst->ss_base = CPSW0_SS_REGS; + cpswinst->mdio_base = CPSW0_MDIO_REGS; + cpswinst->wrpr_base = CPSW0_WR_REGS; + cpswinst->cpdma_base = CPSW0_CPDMA_REGS; + cpswinst->ale_base = CPSW0_ALE_REGS; + cpswinst->cppi_ram_base = CPSW0_CPPI_RAM_REGS; + cpswinst->host_port_base = CPSW0_PORT_0_REGS; + cpswinst->port[PORT_1].port_base = CPSW0_PORT_1_REGS; + cpswinst->port[PORT_1].sliver_base = CPSW0_SLIVER_1_REGS; +#ifdef CPSW0_PORT_1_PHY_ADDR + cpswinst->port[PORT_1].phy_addr = CPSW0_PORT_1_PHY_ADDR; + cpswinst->port[PORT_1].phy_gbps = CPSW0_PORT_1_PHY_GIGABIT; +#endif + cpswinst->port[PORT_2].port_base = CPSW0_PORT_2_REGS; + cpswinst->port[PORT_2].sliver_base = CPSW0_SLIVER_2_REGS; +#ifdef CPSW0_PORT_2_PHY_ADDR + cpswinst->port[PORT_2].phy_addr = CPSW0_PORT_2_PHY_ADDR; + cpswinst->port[PORT_2].phy_gbps = CPSW0_PORT_2_PHY_GIGABIT; +#endif + } +} + +/** + * Gives the index of the ALE entry which is free + * @param cpswinst The CPSW instance structure pointer + * + * @return index of the ALE entry which is free + * ERR_VAL if entry not found + */ +static err_t +cpswif_ale_entry_match_free(struct cpswinst *cpswinst) { + u32_t ale_entry[ALE_ENTRY_NUM_WORDS]; + s32_t idx; + + /* Check which ALE entry is free starting from 0th entry */ + for (idx = 0; idx < MAX_ALE_ENTRIES; idx++) { + CPSWALETableEntryGet(cpswinst->ale_base, idx, ale_entry); + + /* Break if the table entry is free */ + if (((*(((u8_t *)ale_entry) + ENTRY_TYPE_IDX)) + & ENTRY_TYPE) == ENTRY_FREE) { + return idx; + } + } + + return ERR_VAL; +} + +#ifdef CPSW_DUAL_MAC_MODE +/** + * Sets the VLAN and VLAN/UCAST entries in ALE table for Dual Mac mode + * @param cpswinst The CPSW instance structure pointer + * @param port_num The slave port number + * @param eth_addr Ethernet address + * + * @return None + */ +static void +cpswif_port_to_host_vlan_cfg(struct cpswinst *cpswinst, u32_t port_num, + u8_t *eth_addr) +{ + s32_t idx; + u32_t cnt; + u32_t ale_v_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0}; + u32_t ale_vu_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0}; + + idx = cpswif_ale_entry_match_free(cpswinst); + + if(MAX_ALE_ENTRIES == idx) { + return; + } + + /* Set up the VLAN Entry */ + *(((u8_t *)ale_v_entry) + ALE_VLAN_ENTRY_MEMBER_LIST) = + HOST_PORT_MASK | SLAVE_PORT_MASK(port_num); + + /** + * Set the bit fields for entry type and VLAN ID. Set the port + * number as VLAN ID. So only lsb 2 bits of VLAN_ID field will be used. + */ + *(((u8_t *)ale_v_entry) + ALE_VLAN_ENTRY_ID_BIT0_BIT7) = port_num; + *(((u8_t *)ale_v_entry) + ALE_VLAN_ENTRY_TYPE_ID_BIT8_BIT11) = ALE_ENTRY_VLAN; + + *(((u8_t *)ale_v_entry) + ALE_VLAN_ENTRY_FRC_UNTAG_EGR) = + HOST_PORT_MASK | SLAVE_PORT_MASK(port_num); + + /* Set the VLAN entry in the ALE table */ + CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_v_entry); + + idx = cpswif_ale_entry_match_free(cpswinst); + + if(MAX_ALE_ENTRIES == idx) { + return; + } + + /* Set up the VLAN/Unicast Entry */ + for(cnt = 0; cnt < ETHARP_HWADDR_LEN; cnt++) { + *(((u8_t *)ale_vu_entry) + cnt) = eth_addr[ETHARP_HWADDR_LEN - cnt -1]; + } + + *(((u8_t *)ale_vu_entry) + ALE_VLANUCAST_ENTRY_TYPE_ID_BIT8_BIT11) = + ALE_ENTRY_VLANUCAST; + *(((u8_t *)ale_vu_entry) + ALE_VLANUCAST_ENTRY_ID_BIT0_BIT7) = port_num; + + /* Set the VLAN/Unicast entry in the ALE table */ + CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_vu_entry); +} + +#else /* CPSW_DUAL_MAC_MODE */ + +/** + * Sets a unicast entry in the ALE table. + * @param cpswinst The CPSW instance structure pointer + * @param port_num The slave port number + * @param eth_addr Ethernet address + * + * @return None + */ +static void +cpswif_ale_unicastentry_set(struct cpswinst *cpswinst, u32_t port_num, + u8_t *eth_addr) { + volatile u32_t cnt; + volatile s32_t idx; + u32_t ale_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0}; + + for(cnt = 0; cnt < ETHARP_HWADDR_LEN; cnt++) { + *(((u8_t *)ale_entry) + cnt) = eth_addr[ETHARP_HWADDR_LEN - cnt -1]; + } + + *(((u8_t *)ale_entry) + ALE_UCAST_ENTRY_TYPE) = ALE_ENTRY_UCAST; + *(((u8_t *)ale_entry) + ALE_UCAST_ENTRY_DLR_PORT_BLK_SEC) = + (port_num << ALE_UCAST_ENTRY_PORT_SHIFT); + + idx = cpswif_ale_entry_match_free(cpswinst); + + if (idx < MAX_ALE_ENTRIES ) { + CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_entry); + } +} + +/** + * Sets a multicast entry in the ALE table + * @param cpswinst The CPSW instance structure pointer + * @param portmask The port mask for the port number + * @param eth_addr Ethernet Address + * + * @return index of the ALE entry added + * ERR_VAL if table entry is not free + */ +static void +cpswif_ale_multicastentry_set(struct cpswinst *cpswinst, u32_t portmask, + u8_t *eth_addr) +{ + volatile u32_t cnt; + volatile s32_t idx; + u32_t ale_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0}; + + idx = cpswif_ale_entry_match_free(cpswinst); + if (idx < MAX_ALE_ENTRIES ) { + for (cnt = 0; cnt < ETHARP_HWADDR_LEN; cnt++) { + *(((u8_t *)ale_entry) + cnt) = eth_addr[ETHARP_HWADDR_LEN - cnt -1]; + } + + *(((u8_t *)ale_entry) + ALE_MCAST_ENTRY_TYPE_FWD_STATE) = ALE_ENTRY_MCAST; + *(((u8_t *)ale_entry) + ALE_MCAST_ENTRY_PORTMASK_SUP) |= + (portmask << ALE_MCAST_ENTRY_PORTMASK_SHIFT); + + CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_entry); + } +} +#endif /* CPSW_DUAL_MAC_MODE */ + +#ifdef CPSW_SWITCH_CONFIG +/** + * Checks the address is value for given type + * + * @param eth_addr Ethernet Address + * @param addr_type Type of address (Unicast, Multicast, Broadcast) + * + * @return the status + */ +static u32_t +check_valid_addr(u8_t *eth_addr, u32_t addr_type) { + u32_t cnt = 0; + + if (!addr_type) + return TRUE; + + for (cnt = 0; cnt < LEN_MAC_ADDRESS; cnt++) + if ((eth_addr[cnt] & MASK_BROADCAST_ADDR) != MASK_BROADCAST_ADDR) + break; + + if ((cnt == LEN_MAC_ADDRESS) && (addr_type == ADDR_TYPE_BROADCAST)) + return TRUE; + else if (cnt == LEN_MAC_ADDRESS) + return FALSE; + else if (addr_type == ADDR_TYPE_BROADCAST) + return FALSE; + + if (addr_type == ADDR_TYPE_MULTICAST) { + if ((eth_addr[0] & MASK_MULTICAST_ADDR) == MASK_MULTICAST_ADDR) + return TRUE; + } else if (addr_type == ADDR_TYPE_UNICAST) { + if ((eth_addr[0] & MASK_MULTICAST_ADDR) != MASK_MULTICAST_ADDR) + return TRUE; + } + + return FALSE; +} + +/** + * Gives the index of the ALE entry which is untouched ageable + * @param cpswinst The CPSW instance structure pointer + * + * @return index of the ALE entry which is free + * ERR_VAL if entry not found + */ +static err_t +cpswif_ale_entry_match_ageable(struct cpswinst *cpswinst) { + u32_t ale_entry[ALE_ENTRY_NUM_WORDS]; + u32_t type; + s32_t idx; + + /* Check which ALE entry is free starting from 0th entry */ + for (idx = 0; idx < MAX_ALE_ENTRIES; idx++) { + CPSWALETableEntryGet(cpswinst->ale_base, idx, ale_entry); + + type = *(((u8_t *)ale_entry) + ENTRY_TYPE_IDX) & ENTRY_TYPE; + + /* Goto next entry if the ale entry is not valid address */ + if ((type != ALE_ENTRY_ADDR) && (type != ALE_ENTRY_VLAN_ADDR)) + continue; + + if (check_valid_addr((u8_t *)ale_entry, ADDR_TYPE_MULTICAST)) + continue; + + type = *(((u8_t *)ale_entry) + ALE_UCAST_ENTRY_TYPE) & ALE_UCAST_TYPE_MASK; + + if ((type != ALE_UCAST_TYPE_PERSISTANT) && (type != ALE_UCAST_TYPE_OUI)) + return idx; + } + + return ERR_VAL; +} + +/** + * Configure the rate limit for packets transmit of ports + * @param cpswinst The CPSW instance + * @param enable Enable Rate Limit + * @param direction Select TX or RX mode + * @param port_num Port Number + * @param addr_type Type of Address (Broadcast or Multicast) + * @param limit Vale of rate limit to be set + * + * @return None + * + **/ +static void +cpswif_rate_limit(struct cpswinst *cpswinst, u32_t enable, u32_t direction, + u32_t port_num, u32_t addr_type, u32_t limit) { + if (!enable) { + CPSWALERateLimitDisable(cpswinst->ale_base); + } + + if (addr_type == ADDR_TYPE_BROADCAST) { + CPSWALEBroadcastRateLimitSet(cpswinst->ale_base, port_num, limit); + } else if (addr_type == ADDR_TYPE_MULTICAST) { + CPSWALEMulticastRateLimitSet(cpswinst->ale_base, port_num, limit); + } + + if (port_num == 0) { + if (direction) { + CPSWALERateLimitTXMode(cpswinst->ale_base); + } else { + CPSWALERateLimitRXMode(cpswinst->ale_base); + } + } else { + if (direction) { + CPSWALERateLimitRXMode(cpswinst->ale_base); + } else { + CPSWALERateLimitTXMode(cpswinst->ale_base); + } + } + + CPSWALERateLimitEnable(cpswinst->ale_base); +} + +/** + * Gives the index of the ALE entry which match address of VLAN + * @param cpswinst The CPSW instance structure pointer + * @param eth_addr Ethernet address + * @param vid VLAN ID + * + * @return index of the ALE entry which match address of VLAN + * ERR_VAL if entry not found + */ +static err_t +cpswif_ale_entry_match_addr(struct cpswinst *cpswinst, u8_t *eth_addr, + u32_t vid) { + u32_t ale_entry[ALE_ENTRY_NUM_WORDS]; + u32_t type, cnt; + s32_t idx; + + /* Check which ALE entry is free starting from 0th entry */ + for (idx = 0; idx < MAX_ALE_ENTRIES; idx++) { + CPSWALETableEntryGet(cpswinst->ale_base, idx, ale_entry); + + type = *(((u8_t *)ale_entry) + ENTRY_TYPE_IDX) & ENTRY_TYPE; + + /* Goto next entry if the ale entry is not valid address */ + if ((type != ALE_ENTRY_ADDR) && (type != ALE_ENTRY_VLAN_ADDR)) + continue; + + if ((*(((u16_t *)ale_entry) + ALE_VLAN_ENTRY_ID) & ALE_VLAN_ID_MASK) != vid) + continue; + + for (cnt = 0; cnt < ETHARP_HWADDR_LEN; cnt++) { + if (*(((u8_t *)ale_entry) + cnt) != eth_addr[ETHARP_HWADDR_LEN - cnt -1]) + break; + + if (ETHARP_HWADDR_LEN == (cnt + 1)) + return idx; + } + } + + return ERR_VAL; +} + +/** + * Gives the index of the ALE entry which match vlan + * @param cpswinst The CPSW instance structure pointer + * @param vid VLAN ID + * + * @return index of the ALE entry which match vlan + * ERR_VAL if entry not found + */ +static err_t +cpswif_ale_entry_match_vlan(struct cpswinst *cpswinst, u32_t vid) { + u32_t ale_entry[ALE_ENTRY_NUM_WORDS]; + u32_t type; + s32_t idx; + + /* Check which ALE entry is free starting from 0th entry */ + for (idx = 0; idx < MAX_ALE_ENTRIES; idx++) { + CPSWALETableEntryGet(cpswinst->ale_base, idx, ale_entry); + + type = *(((u8_t *)ale_entry) + ENTRY_TYPE_IDX) & ENTRY_TYPE; + + /* Goto next entry if the ale entry is not valid vlan */ + if (type != ALE_ENTRY_VLAN) + continue; + + if ((*(((u16_t *)ale_entry) + ALE_VLAN_ENTRY_ID) & ALE_VLAN_ID_MASK) == vid) + return idx; + } + + return ERR_VAL; +} + +/** + * Adds an unicast entry in the ALE table. + * @param cpswinst The CPSW instance structure pointer + * @param port_num The slave port number + * @param eth_addr Ethernet address + * @param flags Flags Settings + * 0x1 - Secure + * 0x2 - Block + * + * @return index of the ALE entry added + * ERR_VAL if table entry is not free + */ +static err_t +cpswif_ale_unicastentry_add(struct cpswinst *cpswinst, u32_t port_num, + u8_t *eth_addr, u32_t flags, u32_t ucast_type) { + volatile u32_t cnt; + volatile s32_t idx; + u32_t ale_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0}; + + idx = cpswif_ale_entry_match_addr(cpswinst, eth_addr, 0); + + if (ERR_VAL == idx) + idx = cpswif_ale_entry_match_free(cpswinst); + + if (ERR_VAL == idx) + idx = cpswif_ale_entry_match_ageable(cpswinst); + + if (ERR_VAL == idx) + return ERR_VAL; + + for(cnt = 0; cnt < ETHARP_HWADDR_LEN; cnt++) { + *(((u8_t *)ale_entry) + cnt) = eth_addr[ETHARP_HWADDR_LEN - cnt -1]; + } + + *(((u8_t *)ale_entry) + ALE_UCAST_ENTRY_TYPE) = ALE_ENTRY_ADDR | + (ucast_type << ALE_UCAST_TYPE_SHIFT); + *(((u8_t *)ale_entry) + ALE_UCAST_ENTRY_DLR_PORT_BLK_SEC) = + (port_num << ALE_UCAST_ENTRY_PORT_SHIFT) | + (flags & ALE_UCAST_ENTRY_DLR_BLK_SEC_MASK); + + CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_entry); + + return idx; +} + +/** + * Adds an OUI entry in the ALE table. + * @param cpswinst The CPSW instance structure pointer + * @param eth_addr Ethernet address + * + * @return index of the ALE entry added + * ERR_VAL if table entry is not free + */ +static err_t +cpswif_ale_OUI_add(struct cpswinst *cpswinst, u8_t *eth_addr) { + volatile u32_t cnt; + volatile s32_t idx; + u32_t ale_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0}; + + idx = cpswif_ale_entry_match_addr(cpswinst, eth_addr, 0); + + if (ERR_VAL == idx) + idx = cpswif_ale_entry_match_free(cpswinst); + + if (ERR_VAL == idx) + idx = cpswif_ale_entry_match_ageable(cpswinst); + + if (ERR_VAL == idx) + return ERR_VAL; + + for(cnt = 0; cnt < ETHARP_HWADDR_LEN; cnt++) { + *(((u8_t *)ale_entry) + cnt) = eth_addr[ETHARP_HWADDR_LEN - cnt -1]; + } + + *(((u8_t *)ale_entry) + ALE_UCAST_ENTRY_TYPE) = ALE_ENTRY_ADDR | ALE_ENTRY_OUI; + + CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_entry); + + return idx; +} + +/** + * Deletes an unicast entry in the ALE table. + * @param cpswinst The CPSW instance structure pointer + * @param port_num The slave port number + * @param eth_addr Ethernet address + * + * @return index of the ALE entry deleted + * ERR_VAL if table entry is not present + */ +static err_t +cpswif_ale_unicastentry_del(struct cpswinst *cpswinst, u32_t port_num, + u8_t *eth_addr) { + volatile s32_t idx; + u32_t ale_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0}; + + *(((u8_t *)ale_entry) + ALE_UCAST_ENTRY_TYPE) = ENTRY_FREE; + + idx = cpswif_ale_entry_match_addr(cpswinst, eth_addr, 0); + + if (ERR_VAL == idx) + return ERR_VAL; + + CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_entry); + + return idx; +} + +/** + * Adds a multicast entry in the ALE table + * @param cpswinst The CPSW instance structure pointer + * @param portmask The port mask for the port number + * @param eth_addr Ethernet Address + * @param super Supervisory Packet + * @param mcast_st Multicast Forward State + * + * @return index of the ALE entry added + * ERR_VAL if table entry is not free + */ +static err_t +cpswif_ale_multicastentry_add(struct cpswinst *cpswinst, u32_t portmask, + u8_t *eth_addr, u32_t super, u32_t mcast_st) { + volatile s32_t idx; + volatile u32_t cnt; + u32_t ale_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0}; + + idx = cpswif_ale_entry_match_addr(cpswinst, eth_addr, 0); + + if (ERR_VAL == idx) { + idx = cpswif_ale_entry_match_free(cpswinst); + } else { + /* Get the entry in the ALE table */ + CPSWALETableEntryGet(cpswinst->ale_base, idx, ale_entry); + } + + if (ERR_VAL == idx) + idx = cpswif_ale_entry_match_ageable(cpswinst); + + if (ERR_VAL == idx) + return ERR_VAL; + + for (cnt = 0; cnt < ETHARP_HWADDR_LEN; cnt++) { + *(((u8_t *)ale_entry) + cnt) = eth_addr[ETHARP_HWADDR_LEN - cnt -1]; + } + + portmask |= (*(((u8_t *)ale_entry) + ALE_MCAST_ENTRY_PORTMASK_SUP) & + ALE_MCAST_ENTRY_PORT_MASK); + + *(((u8_t *)ale_entry) + ALE_MCAST_ENTRY_TYPE_FWD_STATE) = + (ALE_ENTRY_ADDR | + (mcast_st << ALE_MCAST_ENTRY_TYPE_FWD_STATE_SHIFT)); + *(((u8_t *)ale_entry) + ALE_MCAST_ENTRY_PORTMASK_SUP) &= + ~(PORT_MASK << ALE_MCAST_ENTRY_PORTMASK_SHIFT); + *(((u8_t *)ale_entry) + ALE_MCAST_ENTRY_PORTMASK_SUP) |= + (portmask << ALE_MCAST_ENTRY_PORTMASK_SHIFT) | + ((super << ALE_MCAST_ENTRY_SUPER_SHIFT) & + ALE_MCAST_ENTRY_SUPER_MASK); + + CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_entry); + + return idx; +} + +/** + * Deletes a multicast entry in the ALE table + * @param cpswinst The CPSW instance structure pointer + * @param portmask The port mask for the port number + * @param eth_addr Ethernet Address + * + * @return index of the ALE entry deleted + * ERR_VAL if table entry is not present + */ +static err_t +cpswif_ale_multicastentry_del(struct cpswinst *cpswinst, u32_t portmask, + u8_t *eth_addr) { + volatile s32_t idx; + u32_t ale_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0}; + + idx = cpswif_ale_entry_match_addr(cpswinst, eth_addr, 0); + + if (ERR_VAL == idx) + return ERR_VAL; + + CPSWALETableEntryGet(cpswinst->ale_base, idx, ale_entry); + + if (portmask) { + *(((u8_t *)ale_entry) + ALE_MCAST_ENTRY_PORTMASK_SUP) &= + ~(PORT_MASK << ALE_MCAST_ENTRY_PORTMASK_SHIFT); + *(((u8_t *)ale_entry) + ALE_MCAST_ENTRY_PORTMASK_SUP) = + (portmask << ALE_MCAST_ENTRY_PORTMASK_SHIFT); + } else { + *(((u8_t *)ale_entry) + ALE_MCAST_ENTRY_TYPE_FWD_STATE) = ENTRY_FREE; + } + + CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_entry); + + return idx; +} + +/** + * Adds the VLAN entry in ALE table + * @param cpswinst The CPSW instance structure pointer + * @param vid VLAN ID + * @param port_num The slave port number + * @param untag The force untagged egress + * @param reg_mcast The registered MCAST Flood Mask + * @param unreg_mcast The unregistered MCAST Flood Mask + * + * @return index of the ALE entry added + * ERR_VAL if table entry is not free + */ +static err_t +cpswif_ale_vlan_add(struct cpswinst *cpswinst, u32_t vid, u32_t port_num, + u32_t untag, u32_t reg_mcast, u32_t unreg_mcast) { + s32_t idx; + u32_t ale_v_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0}; + + idx = cpswif_ale_entry_match_vlan(cpswinst, vid); + + if (ERR_VAL == idx) { + idx = cpswif_ale_entry_match_free(cpswinst); + } else { + /* Get the entry in the ALE table */ + CPSWALETableEntryGet(cpswinst->ale_base, idx, ale_v_entry); + } + + if (ERR_VAL == idx) + idx = cpswif_ale_entry_match_ageable(cpswinst); + + if (ERR_VAL == idx) + return ERR_VAL; + + /** + * Set the bit fields for entry type and VLAN ID. + */ + *(((u8_t *)ale_v_entry) + ALE_VLAN_ENTRY_ID_BIT0_BIT7) = vid; + *(((u8_t *)ale_v_entry) + ALE_VLAN_ENTRY_TYPE_ID_BIT8_BIT11) = + ((vid >> ALE_VLAN_ENTRY_TYPE_ID_BIT8_BIT11_ALIGN) & + MASK_LOWER_4BITS_BYTE) | ALE_ENTRY_VLAN; + *(((u8_t *)ale_v_entry) + ALE_VLAN_ENTRY_FRC_UNTAG_EGR) = untag; + *(((u8_t *)ale_v_entry) + ALE_VLAN_ENTRY_MCAST_UNREG) = unreg_mcast; + *(((u8_t *)ale_v_entry) + ALE_VLAN_ENTRY_MCAST_REG) = reg_mcast; + *(((u8_t *)ale_v_entry) + ALE_VLAN_ENTRY_MEMBER_LIST) |= + PORT_MASK & INDV_PORT_MASK(port_num); + + /* Set the VLAN entry in the ALE table */ + CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_v_entry); + + return idx; +} + +/** + * Dlelete the VLAN entry in ALE table + * @param cpswinst The CPSW instance structure pointer + * @param vid VLAN ID + * @param port_num The slave port number + * + * @return index of the ALE entry deleted + * ERR_VAL if table entry is not present + */ +static err_t +cpswif_ale_vlan_del(struct cpswinst *cpswinst, u32_t vid, u32_t port_num) { + s32_t idx; + u32_t mask; + u32_t ale_v_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0}; + + idx = cpswif_ale_entry_match_vlan(cpswinst, vid); + + if (ERR_VAL == idx) + return ERR_VAL; + + CPSWALETableEntryGet(cpswinst->ale_base, idx, ale_v_entry); + + /* Set up the VLAN Entry */ + mask = *(((u8_t *)ale_v_entry) + ALE_VLAN_ENTRY_MEMBER_LIST) | PORT_MASK; + mask &= INDV_PORT_MASK(port_num); + + if (0 == mask) + return ERR_VAL; + + if (1 == mask) { + *(((u8_t *)ale_v_entry) + ALE_MCAST_ENTRY_TYPE_FWD_STATE) = ENTRY_FREE; + } else { + /* Set up the VLAN Entry */ + *(((u8_t *)ale_v_entry) + ALE_VLAN_ENTRY_MEMBER_LIST) &= + ~INDV_PORT_MASK(port_num); + } + + /* Set the VLAN/Unicast entry in the ALE table */ + CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_v_entry); + + return idx; +} + +/** + * Add the VLAN/UCAST entries in ALE table + * @param cpswinst The CPSW instance structure pointer + * @param vid VLAN ID + * @param port_num The slave port number + * @param eth_addr Ethernet address + * @param flags Flags Settings + * 0x1 - Secure + * 0x2 - Block + * + * @return index of the ALE entry added + * ERR_VAL if table entry is not free + */ +static err_t +cpswif_ale_vlan_add_ucast(struct cpswinst *cpswinst, u32_t vid, u32_t port_num, + u8_t *eth_addr, u32_t flags, u32_t ucast_type) { + s32_t idx; + u32_t cnt; + u32_t ale_vu_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0}; + + idx = cpswif_ale_entry_match_addr(cpswinst, eth_addr, vid); + + if (ERR_VAL == idx) + idx = cpswif_ale_entry_match_free(cpswinst); + + if (ERR_VAL == idx) + idx = cpswif_ale_entry_match_ageable(cpswinst); + + if (ERR_VAL == idx) + return ERR_VAL; + + /** + * Set the bit fields for entry type and VLAN ID. + */ + *(((u8_t *)ale_vu_entry) + ALE_VLAN_ENTRY_ID_BIT0_BIT7) = vid; + *(((u8_t *)ale_vu_entry) + ALE_VLAN_ENTRY_TYPE_ID_BIT8_BIT11) = + ((vid >> ALE_VLAN_ENTRY_TYPE_ID_BIT8_BIT11_ALIGN) & + MASK_LOWER_4BITS_BYTE) | ALE_ENTRY_VLAN_ADDR | + (ucast_type << ALE_UCAST_TYPE_SHIFT); + + for (cnt = 0; cnt < ETHARP_HWADDR_LEN; cnt++) { + *(((u8_t *)ale_vu_entry) + cnt) = eth_addr[ETHARP_HWADDR_LEN - cnt -1]; + } + + *(((u8_t *)ale_vu_entry) + ALE_UCAST_ENTRY_DLR_PORT_BLK_SEC) = + (port_num << ALE_UCAST_ENTRY_PORT_SHIFT) | + (flags & ALE_UCAST_ENTRY_DLR_BLK_SEC_MASK); + + /* Set the VLAN entry in the ALE table */ + CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_vu_entry); + + return idx; +} + +/** + * Dlelete the VLAN/UCAST entry in ALE table + * @param cpswinst The CPSW instance structure pointer + * @param vid VLAN ID + * @param port_num The slave port number + * + * @return index of the ALE entry deleted + * ERR_VAL if table entry is not present + */ +static err_t +cpswif_ale_vlan_del_ucast(struct cpswinst *cpswinst, u32_t vid, u32_t port_num, + u8_t *eth_addr) { + volatile s32_t idx; + u32_t ale_vu_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0}; + + *(((u8_t *)ale_vu_entry) + ALE_UCAST_ENTRY_TYPE) = ENTRY_FREE; + + idx = cpswif_ale_entry_match_addr(cpswinst, eth_addr, vid); + + if (ERR_VAL == idx) + return ERR_VAL; + + CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_vu_entry); + + return idx; +} + +/** + * Add the VLAN/MCAST entries in ALE table + * @param cpswinst The CPSW instance structure pointer + * @param vid VLAN ID + * @param port_num The slave port number + * @param eth_addr Ethernet address + * @param super Supervisory Packet + * @param mcast_st Multicast Forward State + * + * @return index of the ALE entry added + * ERR_VAL if table entry is not free + */ +static err_t +cpswif_ale_vlan_add_mcast(struct cpswinst *cpswinst, u32_t vid, u32_t portmask, + u8_t *eth_addr, u32_t super, u32_t mcast_st) { + s32_t idx; + u32_t cnt; + u32_t ale_vm_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0}; + + idx = cpswif_ale_entry_match_addr(cpswinst, eth_addr, vid); + + if (ERR_VAL == idx) { + idx = cpswif_ale_entry_match_free(cpswinst); + } else { + /* Get the entry in the ALE table */ + CPSWALETableEntryGet(cpswinst->ale_base, idx, ale_vm_entry); + } + + if (ERR_VAL == idx) + idx = cpswif_ale_entry_match_ageable(cpswinst); + + if (ERR_VAL == idx) + return ERR_VAL; + + /** + * Set the bit fields for entry type and VLAN ID. + */ + *(((u8_t *)ale_vm_entry) + ALE_VLAN_ENTRY_ID_BIT0_BIT7) = vid; + *(((u8_t *)ale_vm_entry) + ALE_VLAN_ENTRY_TYPE_ID_BIT8_BIT11) = + ((vid >> ALE_VLAN_ENTRY_TYPE_ID_BIT8_BIT11_ALIGN) & + MASK_LOWER_4BITS_BYTE); + + for (cnt = 0; cnt < ETHARP_HWADDR_LEN; cnt++) { + *(((u8_t *)ale_vm_entry) + cnt) = eth_addr[ETHARP_HWADDR_LEN - cnt -1]; + } + + portmask |= (*(((u8_t *)ale_vm_entry) + ALE_MCAST_ENTRY_PORTMASK_SUP) & + ALE_MCAST_ENTRY_PORT_MASK); + + *(((u8_t *)ale_vm_entry) + ALE_MCAST_ENTRY_TYPE_FWD_STATE) |= + (ALE_ENTRY_VLAN_ADDR | + (mcast_st << ALE_MCAST_ENTRY_TYPE_FWD_STATE_SHIFT)); + *(((u8_t *)ale_vm_entry) + ALE_MCAST_ENTRY_PORTMASK_SUP) &= + ~(PORT_MASK << ALE_MCAST_ENTRY_PORTMASK_SHIFT); + *(((u8_t *)ale_vm_entry) + ALE_MCAST_ENTRY_PORTMASK_SUP) |= + (portmask << ALE_MCAST_ENTRY_PORTMASK_SHIFT) | + ((super << ALE_MCAST_ENTRY_SUPER_SHIFT) & + ALE_MCAST_ENTRY_SUPER_MASK); + + /* Set the VLAN entry in the ALE table */ + CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_vm_entry); + + return idx; +} + +/** + * Deletes a vlan multicast entry in the ALE table + * @param cpswinst The CPSW instance structure pointer + * @param vid VLAN ID + * @param portmask The port mask for the port number + * @param eth_addr Ethernet Address + * + * @return index of the ALE entry deleted + * ERR_VAL if table entry is not present + */ +static err_t +cpswif_ale_vlan_del_mcast(struct cpswinst *cpswinst, u32_t vid, u32_t portmask, + u8_t *eth_addr) { + volatile s32_t idx; + u32_t ale_vm_entry[ALE_ENTRY_NUM_WORDS] = {0, 0, 0}; + + idx = cpswif_ale_entry_match_addr(cpswinst, eth_addr, vid); + + if (ERR_VAL == idx) + return ERR_VAL; + + CPSWALETableEntryGet(cpswinst->ale_base, idx, ale_vm_entry); + + if (portmask) { + *(((u8_t *)ale_vm_entry) + ALE_MCAST_ENTRY_PORTMASK_SUP) &= + ~(PORT_MASK << ALE_MCAST_ENTRY_PORTMASK_SHIFT); + *(((u8_t *)ale_vm_entry) + ALE_MCAST_ENTRY_PORTMASK_SUP) = + (portmask << ALE_MCAST_ENTRY_PORTMASK_SHIFT); + } else { + *(((u8_t *)ale_vm_entry) + ALE_MCAST_ENTRY_TYPE_FWD_STATE) = ENTRY_FREE; + } + + CPSWALETableEntrySet(cpswinst->ale_base, idx, ale_vm_entry); + + return idx; +} + +#endif /* CPSW_SWITCH_CONFIG */ + +/** + * AutoNegotiates with phy for link, set it in silver and check for link status. + * @param cpswinst The CPSW instance structure pointer + * @param port_num The slave port number + * @param adv Configuration for advertisement + * SELECT_10_HALF - 10Base Half Duplex + * SELECT_10_FULL - 10Base Full Duplex + * SELECT_100_HALF - 100Base Half Duplex + * SELECT_100_FULL - 100Base Full Duplex + * SELECT_1000_HALF - 1000Base Half Duplex + * SELECT_1000_FULL - 1000Base Full Duplex + * @return ERR_OK If link set up is successful + * others if not successful + */ +static err_t +cpswif_phy_autoneg(struct cpswinst *cpswinst, u32_t port_num, u32_t adv) { + err_t linkstat = ERR_CONN; + u16_t adv_val = 0, partnr_ablty = 0, gbps_partnr_ablty = 0, gig_adv_val = 0; + u32_t aut_neg_cnt = 200, auto_stat, transfer_mode = 0; + + /* Check if ethernet PHY is present or not */ + if (0 == (MDIOPhyAliveStatusGet(cpswinst->mdio_base) + & (1 << cpswinst->port[port_num - 1].phy_addr))) { + LWIP_PRINTF("\n\rNo PHY found at addr %d for Port %d of Instance %d.", + cpswinst->port[port_num - 1].phy_addr, + port_num, 0); + return linkstat; + } + + LWIP_PRINTF("\n\rPHY found at address %d for Port %d of Instance %d.", + cpswinst->port[port_num - 1].phy_addr, + port_num, 0); + + if (SELECT_1000_HALF == adv) { + LWIP_PRINTF("\n\rCPSW doesnot support Half Duplex for Gigabyte..."); + return linkstat; + } + + /* We advertise for 10/100 Mbps both half and full duplex */ + if (adv & SELECT_10_HALF) + adv_val |= PHY_10BT; + if (adv & SELECT_10_FULL) + adv_val |= PHY_10BT_FD; + if (adv & SELECT_100_HALF) + adv_val |= PHY_100BTX; + if (adv & SELECT_100_FULL) + adv_val |= PHY_100BTX_FD; + + gig_adv_val = 0; + partnr_ablty = TRUE; + gbps_partnr_ablty = FALSE; + + /** + * Not all the PHYs can operate at 1000 Mbps. So advertise only + * if the PHY is capable + */ + if (cpswinst->port[port_num -1].phy_gbps) { + LWIP_PRINTF("\n\rPhy supports Gigabyte..."); + if (adv & SELECT_1000_FULL) { + gig_adv_val = PHY_1000BT_FD; + partnr_ablty = TRUE; + gbps_partnr_ablty = TRUE; + } + if (adv & SELECT_1000_HALF) { + LWIP_PRINTF("\n\rCPSW doesnot support Half Duplex for Gigabyte..."); + } + } else { + LWIP_PRINTF("\n\rPhy doesnot support Gigabyte..."); + } + + LWIP_PRINTF("\n\rPerforming Auto-Negotiation..."); + + /** + * Now start Autonegotiation. PHY will talk to its partner + * and give us what the partner can handle + */ + if (PhyAutoNegotiate(cpswinst->mdio_base, + cpswinst->port[port_num -1].phy_addr, + &adv_val, &gig_adv_val) == TRUE) { + while (aut_neg_cnt) { + delay(50); + auto_stat = PhyAutoNegStatusGet(cpswinst->mdio_base, + cpswinst->port[port_num -1].phy_addr); + if (TRUE == auto_stat) { + break; + } + aut_neg_cnt--; + } + + if (0 != aut_neg_cnt) { + linkstat = ERR_OK; + LWIP_PRINTF("\n\rAuto-Negotiation Successful."); + } else { + LWIP_PRINTF("\n\rAuto-Negotiation Not Successful."); + return ERR_CONN; + } + + /* Get what the partner supports */ + PhyPartnerAbilityGet(cpswinst->mdio_base, + cpswinst->port[port_num -1].phy_addr, + &partnr_ablty, &gbps_partnr_ablty); + if (gbps_partnr_ablty & PHY_LINK_PARTNER_1000BT_FD) { + LWIP_PRINTF("\n\rTransfer Mode : 1000 Mbps."); + transfer_mode = CPSW_SLIVER_GIG_FULL_DUPLEX; + } else { + if ((adv_val & partnr_ablty) & PHY_100BTX_FD) { + LWIP_PRINTF("\n\rTransfer Mode : 100 Mbps Full Duplex."); + transfer_mode = CPSW_SLIVER_NON_GIG_FULL_DUPLEX; + } else if ((adv_val & partnr_ablty) & PHY_100BTX) { + LWIP_PRINTF("\n\rTransfer Mode : 100 Mbps Half Duplex."); + transfer_mode = CPSW_SLIVER_NON_GIG_HALF_DUPLEX; + } else if ((adv_val & partnr_ablty) & PHY_10BT_FD) { + LWIP_PRINTF("\n\rTransfer Mode : 10 Mbps Full Duplex."); + transfer_mode = CPSW_SLIVER_INBAND | CPSW_SLIVER_NON_GIG_FULL_DUPLEX; + } else if ((adv_val & partnr_ablty) & PHY_10BT) { + LWIP_PRINTF("\n\rTransfer Mode : 10 Mbps Half Duplex."); + transfer_mode = CPSW_SLIVER_INBAND | CPSW_SLIVER_NON_GIG_HALF_DUPLEX; + } else { + LWIP_PRINTF("\n\rNo Valid Transfer Mode is detected."); + } + } + } else { + LWIP_PRINTF("\n\rAuto-Negotiation Not Successful."); + linkstat = ERR_CONN; + } + + /** + * Set the Sliver with the negotiation results if autonegotiation + * is successful + */ + if (linkstat == ERR_OK) { + CPSWSlTransferModeSet(cpswinst->port[port_num - 1].sliver_base, + transfer_mode); + } + + /* Check if PHY link is there or not */ + if (FALSE == ((PhyLinkStatusGet(cpswinst->mdio_base, + cpswinst->port[port_num - 1].phy_addr, 1000)))) { + LWIP_PRINTF("\n\rPHY link connectivity failed for Port %d of Inst %d.", + port_num, 0); + return linkstat; + } + + LWIP_PRINTF("\n\rPHY link verified for Port %d of Instance %d.", + port_num, 0); + + CPSWSlRGMIIEnable( + cpswinst->port[port_num - 1].sliver_base); + + return linkstat; +} + +/** + * Manually configure phy, set it in silver and check for link status. + * @param cpswinst The CPSW instance structure pointer + * @param port_num The slave port number + * @param speed Configuration for speed + * SELECT_SPEED_1000 - 1000 Mbps + * SELECT_SPEED_100 - 100 Mbps + * SELECT_SPEED_10 - 10 Mbps + * @param duplex Configuration for duplex + * SELECT_HALF_DUPLEX - Half Duplex + * SELECT_FULL_DUPLEX - Full Duplex + * @return ERR_OK If link set up is successful + * others if not successful + */ +static err_t +cpswif_phy_forced(struct cpswinst *cpswinst, u32_t port_num, u32_t speed, + u32_t duplex) { + err_t linkstat = ERR_CONN; + u16_t speed_val = 0, duplex_val = 0; + u32_t frc_stat_cnt = 200, frc_stat = FALSE, transfer_mode = 0; + + /* Check if ethernet PHY is present or not */ + if (0 == (MDIOPhyAliveStatusGet(cpswinst->mdio_base) + & (1 << cpswinst->port[port_num - 1].phy_addr))){ + LWIP_PRINTF("\n\rNo PHY found at addr %d for Port %d of Instance %d.", + cpswinst->port[port_num - 1].phy_addr, + port_num, 0); + return linkstat; + } + + LWIP_PRINTF("\n\rPHY found at address %d for Port %d of Instance %d.", + cpswinst->port[port_num - 1].phy_addr, + port_num, 0); + + /* configure control for speed and duples */ + if (SELECT_SPEED_1000 == speed) + speed_val = PHY_SPEED_1000MBPS; + else if (SELECT_SPEED_100 == speed) + speed_val = PHY_SPEED_100MBPS; + + if (TRUE == duplex) + duplex_val = PHY_FULL_DUPLEX; + + if (SELECT_SPEED_1000 == speed) { + LWIP_PRINTF("\n\rManual Configuration not allowed for Gigabyte..."); + return linkstat; + } + + if (FALSE == PhyReset(cpswinst->mdio_base, + cpswinst->port[port_num - 1].phy_addr)) { + LWIP_PRINTF("\n\rPHY Reset Failed..."); + return linkstat; + } + + if (TRUE == (PhyLinkStatusGet(cpswinst->mdio_base, + cpswinst->port[port_num - 1].phy_addr, 1000))) { + while (frc_stat_cnt) { + delay(50); + /* Check if PHY link is there or not */ + frc_stat = (PhyLinkStatusGet(cpswinst->mdio_base, + cpswinst->port[port_num - 1].phy_addr, 1000)); + + if (TRUE == frc_stat) { + LWIP_PRINTF("\n\rPHY Link is Down."); + break; + } + frc_stat_cnt--; + } + } + + LWIP_PRINTF("\n\rPerforming Manual Configuration..."); + + frc_stat_cnt = 200; + frc_stat = FALSE; + + if (PhyConfigure(cpswinst->mdio_base, cpswinst->port[port_num -1].phy_addr, + speed_val, duplex_val)) { + while (frc_stat_cnt) { + delay(50); + frc_stat = PhyLinkStatusGet(cpswinst->mdio_base, + cpswinst->port[port_num - 1].phy_addr, 1000); + + if (1 == frc_stat) { + break; + } + frc_stat_cnt--; + } + + if (0 != frc_stat_cnt) { + linkstat = ERR_OK; + LWIP_PRINTF("\n\rPhy Configuration Successful."); + LWIP_PRINTF("\n\rPHY link verified for Port %d of Instance %d.", + port_num, 0); + } else { + LWIP_PRINTF("\n\rPhy Configuration Successful."); + LWIP_PRINTF("\n\rPHY link connectivity failed for Port %d of Inst %d.", + port_num, 0); + return ERR_CONN; + } + + if (SELECT_SPEED_1000 == speed) { + LWIP_PRINTF("\n\rTransfer Mode : 1000 Mbps."); + transfer_mode = CPSW_SLIVER_GIG_FULL_DUPLEX; + } else { + if (SELECT_SPEED_10 == speed) { + LWIP_PRINTF("\n\rTransfer Mode : 10 Mbps "); + transfer_mode = CPSW_SLIVER_INBAND; + } else { + LWIP_PRINTF("\n\rTransfer Mode : 100 Mbps "); + } + if (TRUE == duplex) { + LWIP_PRINTF("Full Duplex."); + transfer_mode |= CPSW_SLIVER_NON_GIG_FULL_DUPLEX; + } else { + LWIP_PRINTF("Half Duplex."); + transfer_mode |= CPSW_SLIVER_NON_GIG_HALF_DUPLEX; + } + } + } else { + LWIP_PRINTF("\n\rPhy Configuration Not Successful."); + LWIP_PRINTF("\n\rPHY link connectivity failed for Port %d of Inst %d.", + port_num, 0); + linkstat = ERR_CONN; + } + + /** + * Set the Sliver with the forced phy configuration + */ + CPSWSlTransferModeSet(cpswinst->port[port_num - 1].sliver_base, + transfer_mode); + + CPSWSlRGMIIEnable(cpswinst->port[port_num - 1].sliver_base); + + return linkstat; +} + +/** +* Function to setup the link. AutoNegotiates with the phy for link +* setup and set the CPSW with the result of autonegotiation. +* @param cpswportif The cpsw port interface structure pointer +* @return ERR_OK If link set up is successful +* others if not successful +*/ +static err_t +cpswif_autoneg_config(u32_t inst_num, u32_t port_num) { + struct cpswinst *cpswinst = &cpsw_inst_data[inst_num]; + err_t linkstat = ERR_CONN; + u16_t adv_val, partnr_ablty, gbps_partnr_ablty, gig_adv_val; + u32_t aut_neg_cnt = 200, auto_stat, transfer_mode = 0; + + /* We advertise for 10/100 Mbps both half and full duplex */ + adv_val = (PHY_100BTX | PHY_100BTX_FD | PHY_10BT | PHY_10BT_FD); + + /** + * Not all the PHYs can operate at 1000 Mbps. So advertise only + * if the PHY is capable + */ + if(TRUE == cpswinst->port[port_num -1].phy_gbps) { + gig_adv_val = PHY_1000BT_FD; + partnr_ablty = TRUE; + gbps_partnr_ablty = TRUE; + } else { + gig_adv_val = 0; + partnr_ablty = TRUE; + gbps_partnr_ablty = FALSE; + } + + LWIP_PRINTF("\n\rPerforming Auto-Negotiation..."); + + /** + * Now start Autonegotiation. PHY will talk to its partner + * and give us what the partner can handle + */ + if(PhyAutoNegotiate(cpswinst->mdio_base, + cpswinst->port[port_num -1].phy_addr, + &adv_val, &gig_adv_val) == TRUE) { + while(aut_neg_cnt) { + delay(50); + auto_stat = PhyAutoNegStatusGet(cpswinst->mdio_base, + cpswinst->port[port_num -1].phy_addr); + if(TRUE == auto_stat) { + break; + } + aut_neg_cnt--; + } + + if(0 != aut_neg_cnt) { + linkstat = ERR_OK; + LWIP_PRINTF("\n\rAuto-Negotiation Successful."); + } else { + LWIP_PRINTF("\n\rAuto-Negotiation Not Successful."); + return ERR_CONN; + } + + /* Get what the partner supports */ + PhyPartnerAbilityGet(cpswinst->mdio_base, + cpswinst->port[port_num -1].phy_addr, + &partnr_ablty, &gbps_partnr_ablty); + if(gbps_partnr_ablty & PHY_LINK_PARTNER_1000BT_FD) { + LWIP_PRINTF("\n\rTransfer Mode : 1000 Mbps."); + transfer_mode = CPSW_SLIVER_GIG_FULL_DUPLEX; + } else { + if ((adv_val & partnr_ablty) & PHY_100BTX_FD) { + LWIP_PRINTF("\n\rTransfer Mode : 100 Mbps Full Duplex."); + transfer_mode = CPSW_SLIVER_NON_GIG_FULL_DUPLEX; + } else if ((adv_val & partnr_ablty) & PHY_100BTX) { + LWIP_PRINTF("\n\rTransfer Mode : 100 Mbps Half Duplex."); + transfer_mode = CPSW_SLIVER_NON_GIG_HALF_DUPLEX; + } else if ((adv_val & partnr_ablty) & PHY_10BT_FD) { + LWIP_PRINTF("\n\rTransfer Mode : 10 Mbps Full Duplex."); + transfer_mode = CPSW_SLIVER_INBAND | CPSW_SLIVER_NON_GIG_FULL_DUPLEX; + } else if ((adv_val & partnr_ablty) & PHY_10BT) { + LWIP_PRINTF("\n\rTransfer Mode : 10 Mbps Half Duplex."); + transfer_mode = CPSW_SLIVER_INBAND | CPSW_SLIVER_NON_GIG_HALF_DUPLEX; + } else { + LWIP_PRINTF("\n\rNo Valid Transfer Mode is detected."); + } + } + } else { + LWIP_PRINTF("\n\rAuto-Negotiation Not Successful."); + linkstat = ERR_CONN; + } + + /** + * Set the Sliver with the negotiation results if autonegotiation + * is successful + */ + if(linkstat == ERR_OK) { + CPSWSlTransferModeSet(cpswinst->port[port_num - 1].sliver_base, + transfer_mode); + } + + return linkstat; +} + +/** + * This function allocates the rx buffer descriptors ring. The function + * internally calls pbuf_alloc() and allocates the pbufs to the rx buffer + * descriptors. + * + * @param cpswinst The CPSW instance structure pointer + * @return None + */ +static void +cpswif_rxbd_alloc(struct cpswinst *cpswinst) { + struct rxch *rxch = &(cpswinst->rxch); + struct pbuf *p; + volatile struct cpdma_rx_bd *curr_bd, *last_bd, *recv_tail, *recv_head; + u32_t saved_free_num; + + /* Start from the free head of the chain */ + curr_bd = rxch->free_head; + + /* Note down the current positions */ + recv_head = rxch->free_head; + recv_tail = rxch->recv_tail; + saved_free_num = rxch->free_num; + last_bd = rxch->recv_tail; + + while(rxch->free_num) { + /** + * Try to get a pbuf of max. length. This shall be cache line aligned if + * cache is enabled. + */ + p = pbuf_alloc(PBUF_RAW, PBUF_LEN_MAX, PBUF_POOL); + + /** + * Allocate bd's if p is not NULL. This allocation doesnt support + * pbuf chaining. + */ + if(p != NULL) { +#ifdef LWIP_CACHE_ENABLED + /** + * Clean the pbuf structure info. This is needed to prevent losing + * pbuf structure info when we invalidate the pbuf on rx interrupt + */ + CacheDataCleanBuff((u32_t)(p), (u32_t)(SIZEOF_STRUCT_PBUF)); +#endif + curr_bd->bufptr = (u32_t)(p->payload); + curr_bd->bufoff_len = p->len; + curr_bd->flags_pktlen = CPDMA_BUF_DESC_OWNER; + + /* Save the pbuf */ + curr_bd->pbuf = p; + last_bd = curr_bd; + curr_bd = curr_bd->next; + rxch->free_num--; + } else { + break; + } + } + + if(saved_free_num == rxch->free_num) { + /* No bd's were allocated. Go back. */ + return; + } + + rxch->recv_tail = last_bd; + + /** + * If the entire ring is traversed, curr_bd will be NULL. In that case, + * write the Rx HDP in order to continue reception + */ + if(NULL != curr_bd) { + rxch->free_head = curr_bd; + } else { + CPSWCPDMARxHdrDescPtrWrite(cpswinst->cpdma_base, (u32_t)(recv_head), 0); + } + + recv_tail->next = recv_head; + + /* Close the ring to prevent overwriting of pbuf data */ + last_bd->next = NULL; + + /** + * Check if the reception has ended. If the EOQ flag is set, the NULL + * Pointer is already taken by the DMA engine. So we need to write the + * RX HDP with the next descriptor. + */ + if(recv_tail->flags_pktlen & CPDMA_BUF_DESC_EOQ) { + CPSWCPDMARxHdrDescPtrWrite(cpswinst->cpdma_base, (u32_t)(recv_head), 0); + } +} + +/** + * This function does the actual transmission of the packet. The packet is + * contained in the pbuf that is passed to the function. This pbuf might be + * chained. That is, one pbuf can span more than one tx buffer descriptors + * + * @param netif the network interface state for this ethernetif + * @param pbuf the pbuf which is to be sent over EMAC + * @return status ERR_OK, if transmit was successful + * ERR_MEM, if no memory available + */ +static err_t +cpswif_transmit(struct netif *netif, struct pbuf *pbuf) { + struct pbuf *q; + struct txch *txch; + volatile struct cpdma_tx_bd *curr_bd, *bd_to_send, *bd_end; + struct cpswportif *cpswif = netif->state; + u32_t inst_num = cpswif->inst_num; + struct cpswinst *cpswinst = &cpsw_inst_data[inst_num]; + +#ifdef CPSW_DUAL_MAC_MODE + u32_t port_num = cpswif->port_num; +#endif + + txch = &(cpswinst->txch); + + /* Do not send if there are no enough free bd's */ + if(pbuf_clen(pbuf) > txch->free_num) { + return ERR_MEM; + } + + /* Get the buffer descriptor which is free to transmit */ + curr_bd = txch->free_head; + + bd_to_send = txch->free_head; + + /* Update the total packet length */ + curr_bd->flags_pktlen = pbuf->tot_len; + + /* Indicate the start of the packet */ + curr_bd->flags_pktlen |= (CPDMA_BUF_DESC_SOP | CPDMA_BUF_DESC_OWNER); + +#ifdef CPSW_DUAL_MAC_MODE + /* Indicate to which port the packet has to be sent */ + curr_bd->flags_pktlen |= CPDMA_BUF_DESC_TO_PORT(port_num); +#endif + + /* Copy pbuf information into TX buffer descriptors */ + for(q = pbuf; q != NULL; q = q->next) { +#ifdef LWIP_CACHE_ENABLED + /** + * Make sure that the payload is written to memory. Clean + * the portion of cache to make it coherent with the memory. + */ + CacheDataCleanBuff((u32_t)(q->payload), (u32_t)(q->len)); +#endif + /* Intialize the buffer pointer and length */ + curr_bd->bufptr = (u32_t)(q->payload); + curr_bd->bufoff_len = (q->len) & CPDMA_BD_LEN_MASK; + bd_end = curr_bd; + curr_bd->pbuf = pbuf; + curr_bd = curr_bd->next; + + /* Decrement free bds, since one is consumed */ + txch->free_num--; + } + /* Indicate the end of the packet */ + bd_end->next = NULL; + bd_end->flags_pktlen |= CPDMA_BUF_DESC_EOP; + bd_end->flags_pktlen &= ~CPDMA_BUF_DESC_EOQ; + + txch->free_head = curr_bd; + + /* For the first time, write the HDP with the filled bd */ + if(txch->send_tail == NULL) { + CPSWCPDMATxHdrDescPtrWrite(cpswinst->cpdma_base, + (u32_t)(bd_to_send), 0); + } else { + /** + * Chain the bd's. If the DMA engine, already reached the end of the chain, + * the EOQ will be set. In that case, the HDP shall be written again. + */ + curr_bd = txch->send_tail; + curr_bd->next = bd_to_send; + + if(curr_bd->flags_pktlen & CPDMA_BUF_DESC_EOQ) { + /* Write the Header Descriptor Pointer and start DMA */ + CPSWCPDMATxHdrDescPtrWrite(cpswinst->cpdma_base, + (u32_t)(bd_to_send), 0); + } + } + + txch->send_tail = bd_end; + + return ERR_OK; +} + +/** + * This function will send a packet through the emac if the channel is + * available. Otherwise, the packet will be queued in a pbuf queue. + * + * @param netif The lwip network interface structure for this ethernetif + * @param p The MAC packet to send (e.g. IP packet including + * MAC addresses and type) + * @return ERR_OK if the packet could be sent + * an err_t value if the packet couldn't be sent + * + */ +static err_t +cpswif_output(struct netif *netif, struct pbuf *p) { + err_t stat; + struct pbuf *q; + struct cpswportif *cpswif = netif->state; + u32_t inst_num = cpswif->inst_num; + struct cpswinst *cpswinst = &cpsw_inst_data[inst_num]; + SYS_ARCH_DECL_PROTECT(lev); + + /** + * This entire function must run within a "critical section" to preserve + * the integrity of the transmit pbuf queue. + */ + + sys_mutex_lock(&cpswinst->txmtx); + SYS_ARCH_PROTECT(lev); + + /** + * Adjust the packet length if less than minimum required. + */ + if(p->tot_len < MIN_PKT_LEN) { + p->tot_len = MIN_PKT_LEN; + + for(q = p; q->next != NULL; q = q->next) { + q->next->tot_len = q->tot_len - q->len; + } + + /* Adjust the length of the last pbuf. (contents - don't care) */ + q->len = q->tot_len; + } + + /** + * Bump the reference count on the pbuf to prevent it from being + * freed till we are done with it. + */ + pbuf_ref(p); + + /* call the actual transmit function */ + stat = cpswif_transmit(netif, p); + + /* Return to prior interrupt state and return. */ + SYS_ARCH_UNPROTECT(lev); + sys_mutex_unlock(&cpswinst->txmtx); + + return stat; +} + +/** + * Configures PHY link for a port + * @param cpswif The CPSW interface structure pointer + * @param slv_port_num The slave port number + * + * @return ERR_OK if link configurations are successful + * an error status if failed + */ +static err_t +cpswif_phylink_config(struct cpswportif * cpswif, u32_t slv_port_num) { + struct cpswinst *cpswinst = &cpsw_inst_data[cpswif->inst_num]; + err_t err; + + /* Check if ethernet PHY is present or not */ + if(0 == (MDIOPhyAliveStatusGet(cpswinst->mdio_base) + & (1 << cpswinst->port[slv_port_num - 1].phy_addr))){ + LWIP_PRINTF("\n\rNo PHY found at address %d for Port %d of Instance %d.", + cpswinst->port[slv_port_num - 1].phy_addr, slv_port_num, + cpswif->inst_num); + return ERR_CONN; + } + + LWIP_PRINTF("\n\rPHY found at address %d for Port %d of Instance %d.", + cpswinst->port[slv_port_num - 1].phy_addr, slv_port_num, + cpswif->inst_num); + + /** + * PHY is alive. So autonegotiate and get the speed and duplex + * parameters, set it in the sliver + */ + err = (err_t)(cpswif_autoneg_config(cpswif->inst_num, slv_port_num)); + + /* Check if PHY link is there or not */ + if(FALSE == ((PhyLinkStatusGet(cpswinst->mdio_base, + cpswinst->port[slv_port_num - 1].phy_addr, 1000)))) { + LWIP_PRINTF("\n\rPHY link connectivity failed for Port %d of Instance %d.", + slv_port_num, cpswif->inst_num); + return ERR_CONN; + } + + LWIP_PRINTF("\n\rPHY link verified for Port %d of Instance %d.", + slv_port_num, cpswif->inst_num); + + CPSWSlRGMIIEnable(cpswinst->port[slv_port_num - 1].sliver_base); + + return err; +} + +/** + * Initializes the CPSW port + * @param netif The cpsw interface + * + * @return ERR_OK if port initialization is successful + * an error status if failed + */ +static err_t +cpswif_port_init(struct netif *netif) { + struct cpswportif *cpswif = (struct cpswportif*)(netif->state); + u32_t temp; + err_t err; + +#ifdef CPSW_DUAL_MAC_MODE + struct cpswinst *cpswinst = &cpsw_inst_data[cpswif->inst_num]; + u32_t curr_port = cpswif->port_num; +#endif + sem_init(&cpswinst->rxsem, SEM_DEFAULT_PSHARED, SEM_INITIAL_VALUE); + + cpswinst->RxThread = sys_thread_new("receive_thread", rx_thread_function, netif, RX_THREAD_STACKSIZE, RX_PRIORITY); + LWIP_ASSERT("RxThread creation error", (cpswinst->RxThread)); + + sem_init(&cpswinst->txsem, SEM_DEFAULT_PSHARED, SEM_INITIAL_VALUE); + + cpswinst->TxThread = sys_thread_new("transmit_thread", tx_thread_function, netif, TX_THREAD_STACKSIZE, TX_PRIORITY); + LWIP_ASSERT("TxThread creation error", (cpswinst->TxThread)); + + err = sys_mutex_new(&cpswinst->txmtx); + LWIP_ASSERT("TXLockMutex creation error", (err == ERR_OK)); + + /* set MAC hardware address length */ + netif->hwaddr_len = ETHARP_HWADDR_LEN; + + /* set MAC hardware address */ + for(temp = 0; temp < ETHARP_HWADDR_LEN; temp++) { + netif->hwaddr[temp] = cpswif->eth_addr[temp]; + } + + /* maximum transfer unit */ + netif->mtu = MAX_TRANSFER_UNIT; + + /* device capabilities */ + /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */ + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP; + +#ifdef CPSW_DUAL_MAC_MODE + /* Set the ethernet address for the port */ + CPSWPortSrcAddrSet(cpswinst->port[curr_port - 1].port_base, + (u8_t *)(&(cpswif->eth_addr))); + + /** + * For Dual Mac mode, configure port0 and port1 for one VLAN ID; + * port0 and port2 for a different VLAN ID. Here we choose the + * port number as VLAN ID. + */ + CPSWPortVLANConfig(cpswinst->port[curr_port - 1].port_base, curr_port, 0, 0); + + cpswif_port_to_host_vlan_cfg(cpswinst, curr_port, + (u8_t *)(&(cpswif->eth_addr))); + + err = cpswif_phylink_config(cpswif, curr_port); + +#else + err = cpswif_phylink_config(cpswif, 1); + err = err & (cpswif_phylink_config(cpswif, 2)); + +#endif + + return err; +} + +/** + * This function intializes the CPDMA. + * The CPPI RAM will be initialized for transmit and receive + * buffer descriptor rings. + * + * @param cpswinst The CPSW instance structure pointer + * @return None + */ +static void +cpswif_cpdma_init(struct cpswinst *cpswinst) { + u32_t num_bd; + volatile struct cpdma_tx_bd *curr_txbd, *last_txbd; + volatile struct cpdma_rx_bd *curr_rxbd, *last_rxbd; + struct txch *txch; + struct rxch *rxch; + + txch = &(cpswinst->txch); + + /* Initialize the CPDMA memory. Only Channel 0 is supported */ + txch->free_head = (volatile struct cpdma_tx_bd*)(cpswinst->cppi_ram_base); + txch->send_head = txch->free_head; + txch->send_tail = NULL; + + /* Allocate half of the CPPI RAM for TX buffer descriptors */ + num_bd = (SIZE_CPPI_RAM >> 1) / sizeof(cpdma_tx_bd); + + /* All buffer descriptors are free to send */ + txch->free_num = num_bd; + + curr_txbd = txch->free_head; + + /* Initialize all the TX buffer descriptors ring */ + while(num_bd) { + curr_txbd->next = curr_txbd + 1; + curr_txbd->flags_pktlen = 0; + last_txbd = curr_txbd; + curr_txbd = curr_txbd->next; + num_bd--; + } + last_txbd->next = txch->free_head; + + /* Initialize the descriptors for the RX channel */ + rxch = &(cpswinst->rxch); + rxch->free_head = (volatile struct cpdma_rx_bd*)(cpswinst->cppi_ram_base + + (SIZE_CPPI_RAM >> 1)); + + /* Allocate half of the CPPI RAM available for RX buffer dscriptors */ + num_bd = (SIZE_CPPI_RAM >> 1) / sizeof(cpdma_rx_bd); + rxch->free_num = num_bd; + + curr_rxbd = rxch->free_head; + + /* Create the rx ring of buffer descriptors */ + while(num_bd) { + curr_rxbd->next = curr_rxbd + 1; + curr_rxbd->flags_pktlen = CPDMA_BUF_DESC_OWNER; + last_rxbd = curr_rxbd; + curr_rxbd = curr_rxbd->next; + num_bd--; + } + + last_rxbd->next = rxch->free_head; + + /* We are going to receive starting from the free head */ + rxch->recv_head = rxch->free_head; + rxch->recv_tail = last_rxbd; + cpswif_rxbd_alloc(cpswinst); + + /* close the ring */ + last_rxbd->next = NULL; + + CPSWCPDMARxHdrDescPtrWrite(cpswinst->cpdma_base, (u32_t)(rxch->recv_head), 0); +} + +/** + * In this function, the hardware should be initialized. + * Called from cpswif_init(). + * + * @param cpswportif The CPSW port interface structure pointer + * @return None + */ +static void +cpswif_inst_init(struct cpswportif *cpswif){ + u32_t inst_num = cpswif->inst_num; + struct cpswinst *cpswinst = &cpsw_inst_data[inst_num]; + + /* Reset the different modules */ + CPSWSSReset(cpswinst->ss_base); + CPSWWrReset(cpswinst->wrpr_base); + CPSWSlReset(cpswinst->port[PORT_1].sliver_base); + CPSWSlReset(cpswinst->port[PORT_2].sliver_base); + CPSWCPDMAReset(cpswinst->cpdma_base); + + /* Initialize MDIO */ + MDIOInit(cpswinst->mdio_base, MDIO_FREQ_INPUT, MDIO_FREQ_OUTPUT); + delay(1); + + CPSWALEInit(cpswinst->ale_base); + + /* Set the port 0, 1 and 2 states to FORWARD */ + CPSWALEPortStateSet(cpswinst->ale_base, 0, CPSW_ALE_PORT_STATE_FWD); + CPSWALEPortStateSet(cpswinst->ale_base, 1, CPSW_ALE_PORT_STATE_FWD); + CPSWALEPortStateSet(cpswinst->ale_base, 2, CPSW_ALE_PORT_STATE_FWD); + +#ifdef CPSW_DUAL_MAC_MODE + /* For Dual Mac Mode, Configure for VLAN Aware Mode */ + CPSWALEVLANAwareSet(cpswinst->ale_base); + CPSWHostPortDualMacModeSet(cpswinst->host_port_base); + +#else /*CPSW_DUAL_MAC_MODE */ + /* For normal CPSW switch mode, set multicast entry. */ + u8_t bcast_addr[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + cpswif_ale_multicastentry_set(cpswinst, + PORT_0_MASK | PORT_1_MASK | PORT_2_MASK, + bcast_addr); + cpswif_ale_unicastentry_set(cpswinst, 0, + (u8_t *)(&(cpswif->eth_addr))); + + /* Set the ethernet address for both the ports */ + CPSWPortSrcAddrSet(cpswinst->port[0].port_base, + (u8_t *)(&(cpswif->eth_addr))); + CPSWPortSrcAddrSet(cpswinst->port[1].port_base, + (u8_t *)(&(cpswif->eth_addr))); + +#endif /*CPSW_DUAL_MAC_MODE */ + + /* Enable the statistics. Lets see in case we come across any issues */ + CPSWStatisticsEnable(cpswinst->ss_base); + + /* Initialize the buffer descriptors for CPDMA */ + cpswif_cpdma_init(cpswinst); + + /* Acknowledge receive and transmit interrupts for proper interrupt pulsing*/ + CPSWCPDMAEndOfIntVectorWrite(cpswinst->cpdma_base, CPSW_EOI_TX_PULSE); + CPSWCPDMAEndOfIntVectorWrite(cpswinst->cpdma_base, CPSW_EOI_RX_PULSE); + + /* Enable the transmission and reception */ + CPSWCPDMATxEnable(cpswinst->cpdma_base); + CPSWCPDMARxEnable(cpswinst->cpdma_base); + + /* Enable the interrupts for channel 0 and for control core 0 */ + CPSWCPDMATxIntEnable(cpswinst->cpdma_base, 0); + CPSWWrCoreIntEnable(cpswinst->wrpr_base, 0, 0, CPSW_CORE_INT_TX_PULSE); + + CPSWCPDMARxIntEnable(cpswinst->cpdma_base, 0); + CPSWWrCoreIntEnable(cpswinst->wrpr_base, 0, 0, CPSW_CORE_INT_RX_PULSE); +} + +/** + * Should be called at the beginning of the program to set up the + * network interface. It calls the functions cpswif_inst_init() and + * cpswif_port_init() to do low level initializations + * + * @param netif The lwip network interface structure for this ethernetif + * @return ERR_OK If the interface is initialized + * any other err_t on error + */ +err_t +cpswif_init(struct netif *netif) +{ + struct cpswportif *cpswif = (struct cpswportif*)(netif->state); + static u32_t inst_init_flag = 0; + u32_t inst_num = cpswif->inst_num; + +#if LWIP_NETIF_HOSTNAME + /* Initialize interface hostname */ + netif->hostname = "lwip"; +#endif /* LWIP_NETIF_HOSTNAME */ + + /** + * Initialize the snmp variables and counters inside the struct netif. + * The last argument should be replaced with your link speed, in units + * of bits per second. + */ + NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, 10000000); + + /* let us use the interface number to identify netif */ +#ifdef CPSW_DUAL_MAC_MODE + netif->num = (u8_t)(((cpswif->inst_num * MAX_SLAVEPORT_PER_INST) + + cpswif->port_num - 1) & 0xFF); +#else + netif->num = (u8_t)(cpswif->inst_num); +#endif + + /** + * We directly use etharp_output() here to save a function call. + * You can instead declare your own function an call etharp_output() + * from it if you have to do some checks before sending (e.g. if link + * is available...) + */ + netif->output = etharp_output; + netif->linkoutput = cpswif_output; + + /** + * Initialize an instance only once. Port initialization will be + * done separately. + */ + if(((inst_init_flag >> inst_num) & 0x01) == 0) { + cpswif_inst_config(cpswif); + cpswif_inst_init(cpswif); + inst_init_flag |= (1 << inst_num); + } + + if(cpswif_port_init(netif) != ERR_OK) { + return ERR_CONN; + } + + return ERR_OK; +} + +/** + * Handler for Receive interrupt. Packet processing is done in this + * interrupt handler itself. + * + * @param inst_num the instance for which interrupt was generated + * @param netif_arr the address of the array of netifs + * @return none + */ +void +cpswif_rx_inthandler(const u32_t inst_num) { + struct cpswinst *const cpswinst = &cpsw_inst_data[inst_num]; + sem_t* const rxsem = &cpswinst->rxsem; + const u32_t cpdma_base = cpswinst->cpdma_base; + unsigned int curr_bd; + + sem_post(rxsem); + + /* Get the bd which contains the earliest filled data */ + curr_bd = CPSWCPDMARxCPRead(cpdma_base, 0); + /* Acknowledge that this packet is processed */ + CPSWCPDMARxCPWrite(cpdma_base, 0, (unsigned int)curr_bd); + CPSWCPDMAEndOfIntVectorWrite(cpdma_base, CPSW_EOI_RX_PULSE); +} + +static void +process_input(const u32_t inst_num, struct netif *const netif_arr) { + struct cpswinst *cpswinst = &cpsw_inst_data[inst_num]; + struct rxch *rxch; + volatile struct cpdma_rx_bd *curr_bd; + volatile struct pbuf *pbuf; + u32_t tot_len, if_num; + +#ifdef CPSW_DUAL_MAC_MODE + u32_t from_port; +#endif + + /* Get the rx channel pointer */ + rxch = &(cpswinst->rxch); + + /* Get the bd which contains the earliest filled data */ + curr_bd = rxch->recv_head; + + /** + * Process the receive buffer descriptors. When the DMA completes + * reception, OWNERSHIP flag will be cleared. + */ + while((curr_bd->flags_pktlen & CPDMA_BUF_DESC_OWNER) + != CPDMA_BUF_DESC_OWNER) { + +#ifdef CPSW_DUAL_MAC_MODE + /** + * From which slave port the packet came from ? + * We will use this to decide to which netif the packet + * is to be forwarded to. + */ + from_port = ((curr_bd->flags_pktlen) & CPDMA_BUF_DESC_FROM_PORT) + >> CPDMA_BUF_DESC_FROM_PORT_SHIFT; +#endif + + /* Get the total length of the packet */ + tot_len = (curr_bd->flags_pktlen) & CPDMA_BD_PKTLEN_MASK; + + /* Get the pbuf which is associated with the current bd */ + pbuf = curr_bd->pbuf; +#ifdef LWIP_CACHE_ENABLED + /** + * Invalidate the cache lines of the pbuf including payload. Because + * the memory contents got changed by DMA. + */ + CacheDataInvalidateBuff((u32_t)pbuf, (PBUF_LEN_MAX + SIZEOF_STRUCT_PBUF)); +#endif + + /* Update the len and tot_len fields for the pbuf in the chain */ + pbuf->len = (u16_t)(tot_len); + pbuf->tot_len = (u16_t)(tot_len); + + curr_bd->flags_pktlen = CPDMA_BUF_DESC_OWNER; + + /* Adjust the link statistics */ + LINK_STATS_INC(link.recv); + +#ifdef CPSW_DUAL_MAC_MODE + if_num = (inst_num * MAX_SLAVEPORT_PER_INST) + from_port - 1; +#else + if_num = inst_num; +#endif + struct netif * netif = netif_arr + if_num; + /* Process the packet */ + if(netif->input((struct pbuf *)pbuf, netif) != ERR_OK) { + /* Adjust the link statistics */ + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + } + + curr_bd = curr_bd->next; + + /* One more buffer descriptor is free now */ + rxch->free_num++; + + /** + * If the DMA engine took the NULL pointer, we dont have any bd to + * process until new bd's are allocated. + */ + if(curr_bd == NULL) { + rxch->recv_head = rxch->free_head; + break; + } + rxch->recv_head = curr_bd; + } + + /* We got some bd's freed; Allocate them */ + cpswif_rxbd_alloc(cpswinst); +} + +static void +rx_thread_function(void* arg) { +struct netif *const netif = arg; +struct cpswportif * const cpswif = netif->state; +const u32_t inst_num = cpswif->inst_num; +struct cpswinst *const cpswinst = &cpsw_inst_data[inst_num]; +sem_t *const rxsem = &cpswinst->rxsem; +while (1) { + sem_wait(rxsem); + process_input(inst_num, netif); + sched_yield(); + } +} + +/** + * Handler for CPSW Transmit interrupt + * + * @param netif the lwip network interface structure for this ethernetif + * @return none + */ +void +cpswif_tx_inthandler(const u32_t inst_num) { + struct cpswinst *const cpswinst = &cpsw_inst_data[inst_num]; + sem_t* const txsem = &cpswinst->txsem; + const u32_t cpdma_base = cpswinst->cpdma_base; + unsigned int curr_bd; + + sem_post(txsem); + + /* Get the bd which contains the earliest filled data */ + curr_bd = CPSWCPDMATxCPRead(cpdma_base, 0); + /* Acknowledge that this packet is processed */ + CPSWCPDMATxCPWrite(cpdma_base, 0, (unsigned int)curr_bd); + CPSWCPDMAEndOfIntVectorWrite(cpdma_base, CPSW_EOI_TX_PULSE); +} + +static void process_tx_end(const u32_t inst_num) +{ + struct txch *txch; + struct cpswinst *cpswinst = &cpsw_inst_data[inst_num]; + volatile struct cpdma_tx_bd *curr_bd, *send_head; + volatile u32_t cnt = 0xFFFF; + + txch = &(cpswinst->txch); + + send_head = txch->send_head; + + curr_bd = send_head; + + /* Check for correct start of packet */ + while((curr_bd->flags_pktlen) & CPDMA_BUF_DESC_SOP) { + + /* Make sure that the transmission is over */ + while(((curr_bd->flags_pktlen & CPDMA_BUF_DESC_OWNER) + == CPDMA_BUF_DESC_OWNER) && ((--cnt) != 0)); + + /* If CPDMA failed to transmit, give it a chance once more */ + if(0 == cnt) { + CPSWCPDMATxHdrDescPtrWrite(cpswinst->cpdma_base, + (u32_t)(curr_bd), 0); + return; + } + + /* One buffer descriptor is free now */ + txch->free_num++; + + /* Traverse till the end of packet is reached */ + while(((curr_bd->flags_pktlen) & CPDMA_BUF_DESC_EOP) != CPDMA_BUF_DESC_EOP) { + curr_bd = curr_bd->next; + + /* As this bd is not the end, its free now */ + txch->free_num++; + + if(txch->free_num == (SIZE_CPPI_RAM >> 1) / sizeof(cpdma_tx_bd)) { + break; + } + } + + send_head->flags_pktlen &= ~(CPDMA_BUF_DESC_SOP); + curr_bd->flags_pktlen &= ~(CPDMA_BUF_DESC_EOP); + + /** + * If there are no more data transmitted, the next interrupt + * shall happen with the pbuf associated with the free_head + */ + if(curr_bd->next == NULL) { + txch->send_head = txch->free_head; + } else { + txch->send_head = curr_bd->next; + } + + pbuf_free((struct pbuf *)curr_bd->pbuf); + + LINK_STATS_INC(link.xmit); + + send_head = txch->send_head; + curr_bd = send_head; + } +} + +static void +tx_thread_function(void* arg) { +struct netif * const netif = arg; +struct cpswportif * const cpswif = netif->state; +const u32_t inst_num = cpswif->inst_num; +struct cpswinst *const cpswinst = &cpsw_inst_data[inst_num]; +sem_t* const txsem = &cpswinst->txsem; +sys_mutex_t* const txmtx = &cpswinst->txmtx; +while (1) { + /* Wait for receive task to wakeup */ + sem_wait(txsem); + sys_mutex_lock(txmtx); + process_tx_end(inst_num); + sys_mutex_unlock(txmtx); + sched_yield(); + } +} + +/** + * Gets the netif status + * + * @param netif The netif whoes status to be checked + * @return The netif status + */ +u32_t +cpswif_netif_status(struct netif *netif) { + return ((u32_t)(netif_is_up(netif))); +} + +/** + * Returns the link status + * + * @param inst_num The instance number of the module + * @param slv_port_num The slave port number for the module + * + * @return the link status + */ +u32_t +cpswif_link_status(u32_t inst_num, u32_t slv_port_num) { + + struct cpswinst *cpswinst = &cpsw_inst_data[inst_num]; + + return (PhyLinkStatusGet(cpswinst->mdio_base, + cpswinst->port[slv_port_num - 1].phy_addr, 3)); +} + +/** + * Checks the value is in the range of min and max + * + * @param vlaue Value + * @param min Minimum Value + * @param max Maximum Value + * + * @return the status + */ +static u32_t +check_valid(u32_t value, u32_t min, u32_t max) { + if ((min <= value) && (value <= max)) + return TRUE; + else + return FALSE; +} + +/* + * Executes following CPSW Configutarions + * Switch Configuration (CPSW_SWITCH_CONFIG has to be defined) + * 1 - Add a multicast entry + * 2 - Add a unicast entry + * 3 - Add a OUI entry + * 4 - Search address in entry list + * 5 - Delete a multicast entry + * 6 - Delete a unicast entry + * 7 - Adds a vlan entry + * 8 - Search vlan in entry list + * 9 - Delete vlan + * 10 - Configure Port Vlan (ID, CFI, PRI) + * 11 - Age Out the Untouched entries of ALE Table + * 12 - Print Dump of Switch + * 13 - Print Dump of Switch Config + * 14 - ALE VLAN Aware Config + * 15 - Configure Rate Limit for TX or RX + * 16 - Enable Engress Check + * 17 - Set port unknown VLAN info + * 18 - Enable MAC Auth + * 19 - Configure Port State + * Phy Configuration + * 1 - Configure PHY of a port + * + * @param cpsw_switch_config parameters required for configuration + * + * @return None +*/ +void +cpsw_switch_configuration(struct cpsw_config *cpsw_config) { + struct cpswinst *cpswinst = &cpsw_inst_data[cpsw_config->cpsw_inst]; + struct cpsw_phy_param *cpsw_phy_param = cpsw_config->phy_param; +#ifdef CPSW_SWITCH_CONFIG + struct cpsw_switch_param *cpsw_switch_param = cpsw_config->switch_param; + s32_t ret; +#endif /* CPSW_SWITCH_CONFIG */ + + switch (cpsw_config->cmd) { +#ifdef CPSW_SWITCH_CONFIG + case CONFIG_SWITCH_ADD_MULTICAST: + { + if (!check_valid_addr(cpsw_switch_param->addr, ADDR_TYPE_MULTICAST)) { + cpsw_config->ret = ERR_ADDR; + break; + } + + if (!check_valid(cpsw_switch_param->vid, MIN_VLANID, MAX_VLANID)) { + cpsw_config->ret = ERR_VLANID; + break; + } + + if (!check_valid(cpsw_switch_param->port_mask, MIN_PORT_MASK, + MAX_PORT_MASK)) { + cpsw_config->ret = ERR_PORT_MASK; + break; + } + + if (!check_valid(cpsw_switch_param->super, MIN_SUPER, MAX_SUPER)) { + cpsw_config->ret = ERR_SUPER; + break; + } + + if (!check_valid(cpsw_switch_param->fwd_state, MIN_MCAST_FWD_STATE, + MAX_MCAST_FWD_STATE)) { + cpsw_config->ret = ERR_MCAST_FWD_STATE; + break; + } + + if (cpsw_switch_param->vid == 0) + ret = cpswif_ale_multicastentry_add(cpswinst, + cpsw_switch_param->port_mask, + cpsw_switch_param->addr, + cpsw_switch_param->super, + cpsw_switch_param->fwd_state); + else + ret = cpswif_ale_vlan_add_mcast(cpswinst, cpsw_switch_param->vid, + cpsw_switch_param->port_mask, + cpsw_switch_param->addr, + cpsw_switch_param->super, + cpsw_switch_param->fwd_state); + + if (ret == ERR_VAL) + cpsw_config->ret = ERR_FAIL; + else + cpsw_config->ret = ret; + + break; + } + + case CONFIG_SWITCH_ADD_UNICAST: + { + if (!check_valid_addr(cpsw_switch_param->addr, ADDR_TYPE_UNICAST)) { + cpsw_config->ret = ERR_ADDR; + break; + } + + if (!check_valid(cpsw_switch_param->vid, MIN_VLANID, MAX_VLANID)) { + cpsw_config->ret = ERR_VLANID; + break; + } + + if (!check_valid(cpsw_switch_param->port_num, MIN_PORT, MAX_PORT)) { + cpsw_config->ret = ERR_PORT; + break; + } + + if (!check_valid(cpsw_switch_param->ucast_flags, MIN_UCAST_FLAGS, + MAX_UCAST_FLAGS)) { + cpsw_config->ret = ERR_UCAST_FLAGS; + break; + } + + if (!check_valid(cpsw_switch_param->ucast_type, MIN_UCAST_FLAGS, + MAX_UCAST_FLAGS)) { + cpsw_config->ret = ERR_UCAST_FLAGS; + break; + } + + if (cpsw_switch_param->vid == 0) + ret = cpswif_ale_unicastentry_add(cpswinst, cpsw_switch_param->port_num, + cpsw_switch_param->addr, + cpsw_switch_param->ucast_flags, + cpsw_switch_param->ucast_type); + else + ret = cpswif_ale_vlan_add_ucast(cpswinst, cpsw_switch_param->vid, + cpsw_switch_param->port_num, + cpsw_switch_param->addr, + cpsw_switch_param->ucast_flags, + cpsw_switch_param->ucast_type); + + if (ret == ERR_VAL) + cpsw_config->ret = ERR_FAIL; + else + cpsw_config->ret = ret; + + break; + } + + case CONFIG_SWITCH_ADD_OUI: + { + if (!check_valid_addr(cpsw_switch_param->addr, ADDR_TYPE_UNICAST)) { + cpsw_config->ret = ERR_ADDR; + break; + } + + ret = cpswif_ale_OUI_add(cpswinst, cpsw_switch_param->addr); + + if (ret == ERR_VAL) + cpsw_config->ret = ERR_FAIL; + else + cpsw_config->ret = ret; + + break; + } + + case CONFIG_SWITCH_FIND_ADDR: + { + if (!check_valid_addr(cpsw_switch_param->addr, 0)) { + cpsw_config->ret = ERR_ADDR; + break; + } + + if (!check_valid(cpsw_switch_param->vid, MIN_VLANID, MAX_VLANID)) { + cpsw_config->ret = ERR_VLANID; + break; + } + + ret = cpswif_ale_entry_match_addr(cpswinst, cpsw_switch_param->addr, + cpsw_switch_param->vid); + + if (ret == ERR_VAL) + cpsw_config->ret = ERR_FAIL; + else + cpsw_config->ret = ret; + + break; + } + + case CONFIG_SWITCH_DEL_MULTICAST: + { + if (!check_valid_addr(cpsw_switch_param->addr, ADDR_TYPE_MULTICAST)) { + cpsw_config->ret = ERR_ADDR; + break; + } + + if (!check_valid(cpsw_switch_param->vid, MIN_VLANID, MAX_VLANID)) { + cpsw_config->ret = ERR_VLANID; + break; + } + + if (!check_valid(cpsw_switch_param->port_mask, MIN_PORT_MASK, + MAX_PORT_MASK)) { + cpsw_config->ret = ERR_PORT_MASK; + break; + } + + if (cpsw_switch_param->vid == 0) + ret = cpswif_ale_multicastentry_del(cpswinst, cpsw_switch_param->port_mask, + cpsw_switch_param->addr); + else + ret = cpswif_ale_vlan_del_mcast(cpswinst, cpsw_switch_param->vid, + cpsw_switch_param->port_mask, + cpsw_switch_param->addr); + + if (ret == ERR_VAL) + cpsw_config->ret = ERR_FAIL; + else + cpsw_config->ret = ret; + + break; + } + + case CONFIG_SWITCH_DEL_UNICAST: + { + if (!check_valid_addr(cpsw_switch_param->addr, ADDR_TYPE_UNICAST)) { + cpsw_config->ret = ERR_ADDR; + break; + } + + if (!check_valid(cpsw_switch_param->vid, MIN_VLANID, MAX_VLANID)) { + cpsw_config->ret = ERR_VLANID; + break; + } + + if (!check_valid(cpsw_switch_param->port_num, MIN_PORT, MAX_PORT)) { + cpsw_config->ret = ERR_PORT; + break; + } + + if (cpsw_switch_param->vid == 0) + ret = cpswif_ale_unicastentry_del(cpswinst, cpsw_switch_param->port_num, + cpsw_switch_param->addr); + else + ret = cpswif_ale_vlan_del_ucast(cpswinst, cpsw_switch_param->vid, + cpsw_switch_param->port_num, + cpsw_switch_param->addr); + + if (ret == ERR_VAL) + cpsw_config->ret = ERR_FAIL; + else + cpsw_config->ret = ret; + + break; + } + + case CONFIG_SWITCH_ADD_VLAN: + { + if (!check_valid(cpsw_switch_param->vid, MIN_VLANID, MAX_VLANID)) { + cpsw_config->ret = ERR_VLANID; + break; + } + + if (!check_valid(cpsw_switch_param->port_num, MIN_PORT, MAX_PORT)) { + cpsw_config->ret = ERR_PORT; + break; + } + + if (!check_valid(cpsw_switch_param->reg_multi, MIN_VLAN_MCAST_REG, + MAX_VLAN_MCAST_REG)) { + cpsw_config->ret = ERR_VLAN_MCAST_REG; + break; + } + + if (!check_valid(cpsw_switch_param->unreg_multi, MIN_VLAN_MCAST_UNREG, + MAX_VLAN_MCAST_UNREG)) { + cpsw_config->ret = ERR_VLAN_MCAST_UNREG; + break; + } + + if (!check_valid(cpsw_switch_param->untag_port, MIN_VLAN_UNTAG, + MAX_VLAN_UNTAG)) { + cpsw_config->ret = ERR_VLAN_MCAST_UNTAG; + break; + } + + ret = cpswif_ale_vlan_add(cpswinst, cpsw_switch_param->vid, + cpsw_switch_param->port_num, + cpsw_switch_param->untag_port, + cpsw_switch_param->reg_multi, + cpsw_switch_param->unreg_multi); + + if (ret == ERR_VAL) + cpsw_config->ret = ERR_FAIL; + else + cpsw_config->ret = ret; + + break; + } + + case CONFIG_SWITCH_FIND_VLAN: + { + if (!check_valid(cpsw_switch_param->vid, MIN_VLANID, MAX_VLANID)) { + cpsw_config->ret = ERR_VLANID; + break; + } + + ret = cpswif_ale_entry_match_vlan(cpswinst, cpsw_switch_param->vid); + + if (ret == ERR_VAL) + cpsw_config->ret = ERR_FAIL; + else + cpsw_config->ret = ret; + + break; + } + + case CONFIG_SWITCH_DEL_VLAN: + { + if (!check_valid(cpsw_switch_param->vid, MIN_VLANID, MAX_VLANID)) { + cpsw_config->ret = ERR_VLANID; + break; + } + + if (!check_valid(cpsw_switch_param->port_num, MIN_PORT, MAX_PORT)) { + cpsw_config->ret = ERR_PORT; + break; + } + + ret = cpswif_ale_vlan_del(cpswinst, cpsw_switch_param->vid, + cpsw_switch_param->port_num); + + if (ret == ERR_VAL) + cpsw_config->ret = ERR_FAIL; + else + cpsw_config->ret = ret; + + break; + } + + case CONFIG_SWITCH_PORT_VLAN_CONFIG: + { + if (!check_valid(cpsw_switch_param->vid, MIN_VLANID, MAX_VLANID)) { + cpsw_config->ret = ERR_VLANID; + break; + } + + if (!check_valid(cpsw_switch_param->port_num, MIN_PORT, MAX_PORT)) { + cpsw_config->ret = ERR_PORT; + break; + } + + if (!check_valid(cpsw_switch_param->cfi_port, MIN_CFI, MAX_CFI)) { + cpsw_config->ret = ERR_CFI; + break; + } + + if (!check_valid(cpsw_switch_param->prio_port, MIN_PRI, MAX_PRI)) { + cpsw_config->ret = ERR_PRI; + break; + } + + if (cpsw_switch_param->port_num == 0) + CPSWPortVLANConfig(cpswinst->host_port_base, cpsw_switch_param->vid, + cpsw_switch_param->cfi_port, + cpsw_switch_param->prio_port); + else if (cpsw_switch_param->port_num <= 2) + CPSWPortVLANConfig( + cpswinst->port[cpsw_switch_param->port_num - 1].port_base, + cpsw_switch_param->vid, + cpsw_switch_param->cfi_port, + cpsw_switch_param->prio_port); + + cpsw_config->ret = ERR_PASS; + break; + } + + case CONFIG_SWITCH_AGEOUT: + { + CPSWALEAgeOut(cpswinst->ale_base); + cpsw_config->ret = ERR_PASS; + break; + } + + case CONFIG_SWITCH_DUMP: + { + u32_t cnt = 0; + + for(cnt = 0; cnt <= MAX_ALE_ENTRIES; cnt++) + { + CPSWALETableEntryGet(cpswinst->ale_base, cnt, + cpsw_config->buf[cnt]); + } + + cpsw_config->ret = ERR_PASS; + break; + } + + case CONFIG_SWITCH_CONFIG_DUMP: + { + + if (!check_valid(cpsw_switch_param->ale_tbl_index, MIN_ALE_ENTRY_IDX, + MAX_ALE_ENTRY_IDX)) { + cpsw_config->ret = ERR_ALE_ENTRY_IDX; + break; + } + + CPSWALETableEntryGet(cpswinst->ale_base, cpsw_switch_param->ale_tbl_index, + cpsw_config->ale_entry); + cpsw_config->ret = ERR_PASS; + break; + } + + case CONFIG_SWITCH_VLANAWARE: + { + if (!check_valid(cpsw_switch_param->vlan_aware, MIN_VLANAWARE, + MAX_VLANAWARE)) { + cpsw_config->ret = ERR_VLANAWARE; + break; + } + + if (!check_valid(cpsw_switch_param->drop_packet, MIN_ALE_VLANAWARE, + MAX_ALE_VLANAWARE)) { + cpsw_config->ret = ERR_ALE_VLANAWARE; + break; + } + + if (!cpsw_switch_param->vlan_aware) + CPSWVLANAwareDisable(cpswinst->ale_base); + else + CPSWVLANAwareEnable(cpswinst->ale_base); + + if (!cpsw_switch_param->drop_packet) + CPSWALEVLANAwareClear(cpswinst->ale_base); + else + CPSWALEVLANAwareSet(cpswinst->ale_base); + + cpsw_config->ret = ERR_PASS; + break; + } + + case CONFIG_SWITCH_RATELIMIT: + { + if (!check_valid(cpsw_switch_param->enable, MIN_ENABLE, MAX_ENABLE)) { + cpsw_config->ret = ERR_ENABLE; + break; + } + + if (!check_valid(cpsw_switch_param->direction, MIN_DIRECTION, + MAX_DIRECTION)) { + cpsw_config->ret = ERR_DIRECTION; + break; + } + + if (!check_valid(cpsw_switch_param->port_num, MIN_PORT, MAX_PORT)) { + cpsw_config->ret = ERR_PORT; + break; + } + + if (!check_valid(cpsw_switch_param->addr_type, MIN_ADDR_TYPE, MAX_ADDR_TYPE)) { + cpsw_config->ret = ERR_ADDR_TYPE; + break; + } + + if (!check_valid(cpsw_switch_param->limit, MIN_LIMIT, MAX_LIMIT)) { + cpsw_config->ret = ERR_LIMIT; + break; + } + + cpswif_rate_limit(cpswinst, cpsw_switch_param->enable, + cpsw_switch_param->direction, + cpsw_switch_param->port_num, cpsw_switch_param->addr_type, + cpsw_switch_param->limit); + cpsw_config->ret = ERR_PASS; + break; + } + + case CONFIG_SWITCH_VID_INGRESS_CHECK: + { + if (!check_valid(cpsw_switch_param->port_num, MIN_PORT, MAX_PORT)) { + cpsw_config->ret = ERR_PORT; + break; + } + + CPSWALEVIDIngressCheckSet(cpswinst->ale_base, cpsw_switch_param->port_num); + cpsw_config->ret = ERR_PASS; + break; + } + + case CONFIG_SWITCH_ADD_UNKNOWN_VLAN_INFO: + { + if (!check_valid(cpsw_switch_param->unknown_vlan, MIN_UNKNOWN_VLAN, + MAX_UNKNOWN_VLAN)) { + cpsw_config->ret = ERR_UNKNOWN_VLAN; + break; + } + + if (cpsw_switch_param->unknown_vlan == ALE_PORT_UNTAGGED_EGRESS) { + if (!check_valid(cpsw_switch_param->untag_port, MIN_VLAN_UNTAG, + MAX_VLAN_UNTAG)) { + cpsw_config->ret = ERR_VLAN_MCAST_UNTAG; + break; + } + + CPSWALEUnknownUntaggedEgressSet(cpswinst->ale_base, + cpsw_switch_param->untag_port); + } else if (cpsw_switch_param->unknown_vlan == + ALE_PORT_UNKNOWN_REG_MCAST_FLOOD) { + if (!check_valid(cpsw_switch_param->reg_multi, MIN_VLAN_MCAST_REG, + MAX_VLAN_MCAST_REG)) { + cpsw_config->ret = ERR_VLAN_MCAST_REG; + break; + } + + CPSWALEUnknownRegFloodMaskSet(cpswinst->ale_base, + cpsw_switch_param->reg_multi); + } else if (cpsw_switch_param->unknown_vlan == + ALE_PORT_UNKNOWN_UNREG_MCAST_FLOOD) { + if (!check_valid(cpsw_switch_param->unreg_multi, MIN_VLAN_MCAST_UNREG, + MAX_VLAN_MCAST_UNREG)) { + cpsw_config->ret = ERR_VLAN_MCAST_UNREG; + break; + } + + CPSWALEUnknownUnRegFloodMaskSet(cpswinst->ale_base, + cpsw_switch_param->unreg_multi); + } else if (cpsw_switch_param->unknown_vlan == + ALE_PORT_UNKNOWN_VLAN_MEMBER) { + if (!check_valid(cpsw_switch_param->port_mask, MIN_PORT_MASK, + MIN_PORT_MASK)) { + cpsw_config->ret = ERR_VLAN_MCAST_UNREG; + break; + } + + CPSWALEUnknownMemberListSet(cpswinst->ale_base, + cpsw_switch_param->port_mask); + } + + cpsw_config->ret = ERR_PASS; + break; + } + + case CONFIG_SWITCH_MACAUTH: + { + if (!check_valid(cpsw_switch_param->mac_auth, MIN_MAC_AUTH, MAX_MAC_AUTH)) { + cpsw_config->ret = ERR_MAC_AUTH; + break; + } + + if (!cpsw_switch_param->mac_auth) + CPSWALEAUTHModeClear(cpswinst->ale_base); + else + CPSWALEAUTHModeSet(cpswinst->ale_base); + + cpsw_config->ret = ERR_PASS; + break; + } + + case CONFIG_SWITCH_PORT_STATE: + { + if (!check_valid(cpsw_switch_param->port_num, MIN_PORT, MAX_PORT)) { + cpsw_config->ret = ERR_PORT; + break; + } + + if (!check_valid(cpsw_switch_param->port_state, MIN_PORT_STATE, + MAX_PORT_STATE)) { + cpsw_config->ret = ERR_PORT_STATE; + break; + } + + CPSWALEPortStateSet(cpswinst->ale_base, cpsw_switch_param->port_num, + cpsw_switch_param->port_state); + cpsw_config->ret = ERR_PASS; + break; + } +#endif /* CPSW_SWITCH_CONFIG */ + + case CONFIG_SWITCH_SET_PORT_CONFIG: + { + if (!check_valid(cpsw_phy_param->slv_port_num, MIN_SLV_PORT, + MAX_SLV_PORT)) { + cpsw_config->ret = ERR_SLV_PORT; + break; + } + + if (!check_valid(cpsw_phy_param->autoneg, MIN_AUTONEG, MAX_AUTONEG)) { + cpsw_config->ret = ERR_AUTONEG; + break; + } + + if (TRUE == cpsw_phy_param->autoneg) { + if (!check_valid(cpsw_phy_param->config, MIN_PHY_CONFIG, + MAX_PHY_CONFIG)) { + cpsw_config->ret = ERR_PHY_CONFIG; + break; + } + } else { + if (!check_valid(cpsw_phy_param->speed, MIN_SPEED, MAX_SPEED)) { + cpsw_config->ret = ERR_SPEED; + break; + } + + if (!check_valid(cpsw_phy_param->duplex, MIN_DUPLEX, MAX_DUPLEX)) { + cpsw_config->ret = ERR_DUPLEX; + break; + } + } + + if (cpsw_phy_param->autoneg) + cpswif_phy_autoneg(cpswinst, cpsw_phy_param->slv_port_num, + cpsw_phy_param->config); + else + cpswif_phy_forced(cpswinst, cpsw_phy_param->slv_port_num, + cpsw_phy_param->speed, + cpsw_phy_param->duplex); + + cpsw_config->ret = ERR_PASS; + break; + } + + default: + cpsw_config->ret = ERR_INVAL; + break; + } +} diff --git a/cpsw/src/netif/delay.c b/cpsw/src/netif/delay.c new file mode 100644 index 0000000..0f1b2a7 --- /dev/null +++ b/cpsw/src/netif/delay.c @@ -0,0 +1,6 @@ +#include <unistd.h> + +void delay(unsigned int ms) +{ + usleep(ms*1000); +}
\ No newline at end of file diff --git a/cpsw/src/netif/mdio.c b/cpsw/src/netif/mdio.c new file mode 100755 index 0000000..9c6c18e --- /dev/null +++ b/cpsw/src/netif/mdio.c @@ -0,0 +1,209 @@ +/** + * \file mdio.c + * + * \brief MDIO APIs. + * + * This file contains the device abstraction layer APIs for MDIO. + */ + +/* +* Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ +*/ +/* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the +* distribution. +* +* Neither the name of Texas Instruments Incorporated nor the names of +* its contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +*/ + +/* HW Macros and Peripheral Defines */ +#include "hw_types.h" +#include "hw_mdio.h" + +/* Driver APIs */ +#include "mdio.h" + +/******************************************************************************* +* INTERNAL MACRO DEFINITIONS +*******************************************************************************/ +#define PHY_REG_MASK (0x1Fu) +#define PHY_ADDR_MASK (0x1Fu) +#define PHY_DATA_MASK (0xFFFFu) +#define PHY_REG_SHIFT (21u) +#define PHY_ADDR_SHIFT (16u) + +/******************************************************************************* +* API FUNCTION DEFINITIONS +*******************************************************************************/ + +/** + * \brief Reads a PHY register using MDIO. + * + * \param baseAddr Base Address of the MDIO Module Registers. + * \param phyAddr PHY Adress. + * \param regNum Register Number to be read. + * \param dataPtr Pointer where the read value shall be written. + * + * \return status of the read \n + * TRUE - read is successful.\n + * FALSE - read is not acknowledged properly. + * + **/ +unsigned int MDIOPhyRegRead(unsigned int baseAddr, unsigned int phyAddr, + unsigned int regNum, volatile unsigned short *dataPtr) +{ + /* Wait till transaction completion if any */ + while(HWREG(baseAddr + MDIO_USERACCESS0) & MDIO_USERACCESS0_GO); + + HWREG(baseAddr + MDIO_USERACCESS0) + = (MDIO_USERACCESS0_READ | MDIO_USERACCESS0_GO + |((regNum & PHY_REG_MASK) << PHY_REG_SHIFT) + |((phyAddr & PHY_ADDR_MASK) << PHY_ADDR_SHIFT)); + + /* wait for command completion */ + while(HWREG(baseAddr + MDIO_USERACCESS0) & MDIO_USERACCESS0_GO); + + /* Store the data if the read is acknowledged */ + if((HWREG(baseAddr + MDIO_USERACCESS0)) & MDIO_USERACCESS0_ACK) + { + *dataPtr = (unsigned short)((HWREG(baseAddr + MDIO_USERACCESS0)) + & PHY_DATA_MASK); + return TRUE; + } + + return FALSE; +} + +/** + * \brief Writes a PHY register using MDIO. + * + * \param baseAddr Base Address of the MDIO Module Registers. + * \param phyAddr PHY Adress. + * \param regNum Register Number to be read. + * \param RegVal Value to be written. + * + * \return None + * + **/ +void MDIOPhyRegWrite(unsigned int baseAddr, unsigned int phyAddr, + unsigned int regNum, unsigned short RegVal) +{ + /* Wait till transaction completion if any */ + while(HWREG(baseAddr + MDIO_USERACCESS0) & MDIO_USERACCESS0_GO); + + HWREG(baseAddr + MDIO_USERACCESS0) + = (MDIO_USERACCESS0_WRITE | MDIO_USERACCESS0_GO + |((regNum & PHY_REG_MASK) << PHY_REG_SHIFT) + |((phyAddr & PHY_ADDR_MASK) << PHY_ADDR_SHIFT) + | RegVal); + + /* wait for command completion*/ + while(HWREG(baseAddr + MDIO_USERACCESS0) & MDIO_USERACCESS0_GO); +} +/** + * \brief Reads the alive status of all PHY connected to this MDIO. + * The bit correponding to the PHY address will be set if the PHY + * is alive. + * + * \param baseAddr Base Address of the MDIO Module Registers. + * + * \return MDIO alive register state + * + **/ +unsigned int MDIOPhyAliveStatusGet(unsigned int baseAddr) +{ + return (HWREG(baseAddr + MDIO_ALIVE)); +} + +/** + * \brief Reads the link status of all PHY connected to this MDIO. + * The bit correponding to the PHY address will be set if the PHY + * link is active. + * + * \param baseAddr Base Address of the MDIO Module Registers. + * + * \return MDIO link register state + * + **/ +unsigned int MDIOPhyLinkStatusGet(unsigned int baseAddr) +{ + return (HWREG(baseAddr + MDIO_LINK)); +} + +/** + * \brief Initializes the MDIO peripheral. This enables the MDIO state + * machine, uses standard pre-amble and set the clock divider value. + * + * \param baseAddr Base Address of the MDIO Module Registers. + * \param mdioInputFreq The clock input to the MDIO module + * \param mdioOutputFreq The clock output required on the MDIO bus + * \return None + * + **/ +void MDIOInit(unsigned int baseAddr, unsigned int mdioInputFreq, + unsigned int mdioOutputFreq) +{ + unsigned int clkDiv = (mdioInputFreq/mdioOutputFreq) - 1; + + HWREG(baseAddr + MDIO_CONTROL) = ((clkDiv & MDIO_CONTROL_CLKDIV) + | MDIO_CONTROL_ENABLE + | MDIO_CONTROL_PREAMBLE + | MDIO_CONTROL_FAULTENB); +} + +/** + * \brief Saves the MDIO register context. Note that only MDIO control + * register context is saved here. + * + * \param baseAddr Base Address of the MDIO Module Registers. + * \param contextPtr Pointer to the structure where MDIO context + * needs to be saved. + * \return None + * + **/ +void MDIOContextSave(unsigned int baseAddr, MDIOCONTEXT *contextPtr) +{ + contextPtr->mdioCtrl = HWREG(baseAddr + MDIO_CONTROL); +} + +/** + * \brief Restores the MDIO register context. Note that only MDIO control + * register context is restored here. Hence enough delay shall be + * given after this API + * + * \param baseAddr Base Address of the MDIO Module Registers. + * \param contextPtr Pointer to the structure where MDIO context + * needs to be restored from + * \return None + * + **/ +void MDIOContextRestore(unsigned int baseAddr, MDIOCONTEXT *contextPtr) +{ + HWREG(baseAddr + MDIO_CONTROL) = contextPtr->mdioCtrl; +} + +/***************************** End Of File ***********************************/ diff --git a/cpsw/src/netif/mmu.c b/cpsw/src/netif/mmu.c new file mode 100755 index 0000000..8015541 --- /dev/null +++ b/cpsw/src/netif/mmu.c @@ -0,0 +1,184 @@ +/** + * \file mmu.c + * + * \brief APIs for configuring MMU + * + * This file contains the APIs for configuring ARMv7a MMU. +*/ + +/* +* Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ +*/ +/* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the +* distribution. +* +* Neither the name of Texas Instruments Incorporated nor the names of +* its contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +*/ + + +#include "mmu.h" +#include "cp15.h" + +/***************************************************************************** +** INTERNAL MACRO DEFINITIONS +******************************************************************************/ +#define MMU_PAGETABLE_ENTRY_FAULT (0x00) +#define MMU_PAGEBOUND_SHIFT (20) +#define MMU_PG_SUPSECT_SIZE_SHIFT (14) +#define MMU_PGADDR_MASK (0xFFF00000) +#define MMU_PGTYPE_MASK (0x00040002) + +/***************************************************************************** +** FUNCTION DEFINITIONS +******************************************************************************/ +/** + * \brief Initializes the Page Table with fault entries and configures CP15 + * registers required for MMU. The Page Table passed is the master + * page table containing 4096 words, which will be defined by the + * application. + * + * \param masterPt Master Page Table Base Address + * + * \return None. + * + * \Note The StarterWare support for MMU needs only master page table + * configuration. Only a single level paging is supported. Also, only + * TTB0 will be used for page table walking. + **/ +void MMUInit(unsigned int *masterPt) +{ + unsigned int idx; + + /* Invalidate the TLB entries */ + CP15TlbInvalidate(); + + /* Set domain access rights */ + CP15DomainAccessClientSet(); + + /* Disable TEX remapping, Access Flag usage and alignment check */ + CP15ControlFeatureDisable( CP15_CONTROL_TEXREMAP + | CP15_CONTROL_ACCESSFLAG + | CP15_CONTROL_ALIGN_CHCK + | CP15_CONTROL_MMU); + + /* Configure the TTB Control register to use only TTB0 */ + CP15TtbCtlTtb0Config(); + + /* Se the master page table with fault entries */ + for(idx = MMU_PAGETABLE_NUM_ENTRY; idx !=0; idx--) + { + *masterPt++ = MMU_PAGETABLE_ENTRY_FAULT; + } +} + +/** + * \brief Maps a specific region for Virtual Address to Physical Address + * conversion. This API actually updates the corresponding page table + * entries. The mapping for any region is such that Virtual Address + * = Physical Address. \n + * Any region can be mapped as per the attributes given. Regions + * can be specified with Memory Type, Inner/Outer Cache settings, + * Security settings and Access Permissions. + * + * \param region Memory Region to be mapped. This shall be a structure + * pointer of Type REGION *. The structure is detailed in + * mmu.h file. \n + * + * Example Configuration: \n + * A 512MB RAM memory region starting at address 0x80000000 can be + * configured as shown below. The memory is to be cacheable, with + * Inner Cache - Write Through Write Allocate and Outer Cache - + * Write Back Write Allocate attributes. \n + * + * REGION regionRam = { MMU_PGTYPE_SECTION, \n + * 0x80000000, \n + * 512, \n + * MMU_MEMTYPE_NORMAL_SHAREABLE + * (MMU_CACHE_WT_NOWA, MMU_CACHE_WB_WA),\n + * MMU_REGION_NON_SECURE, \n + * MMU_AP_PRV_RW_USR_RW, \n + * (unsigned int*)pageTable}; + * + * \return None. + * + * \Note The regions specify the desired cache policies. However, enabling + * of cache at all desired levels shall be done separately. + **/ +void MMUMemRegionMap(REGION *region) +{ + unsigned int *ptEntryPtr; + unsigned int ptEntry; + int idx; + + /* Get the first entry in the page table to set */ + ptEntryPtr = region->masterPtPtr + + (region->startAddr >> MMU_PAGEBOUND_SHIFT); + + /* Set the pointer to the last entry */ + ptEntryPtr += (region->numPages - 1); + + /* Get the start Address MSB 3 nibbles. Ignore extended address */ + ptEntry = (region->startAddr & region->pgType) & MMU_PGADDR_MASK; + + /* + ** Update the page table entry with memory attributes and + ** Access Permissions and Security. + ** All the regions will be marked as global. + */ + ptEntry |= ((MMU_PGTYPE_MASK & region->pgType) + | region->accsCtrl | region->memAttrib + | region->secureType); + + /* Set the entries in the page table for the region attributes */ + for(idx = (region->numPages - 1); idx >= 0; idx--) + { + *ptEntryPtr-- = ptEntry + (idx << MMU_PAGEBOUND_SHIFT) ; + } +} + +/** + * \brief Updates the Translation Table Base with the address of Master Page + * Table and enables MMU. + * + * \param masterPt Master Page Table Base Address + * + * \return None. + * + * \Note Only TTB0 is used for page table walking. + **/ +void MMUEnable(unsigned int *masterPt) +{ + /* Set TTB0 register */ + CP15Ttb0Set((unsigned int)masterPt); + + /* Enable MMU */ + CP15MMUEnable(); +} + +/***************************** End Of File ***********************************/ + diff --git a/cpsw/src/netif/phy.c b/cpsw/src/netif/phy.c new file mode 100755 index 0000000..1ce9494 --- /dev/null +++ b/cpsw/src/netif/phy.c @@ -0,0 +1,402 @@ +/** + * \file phy.c + * + * \brief APIs for configuring ethernet PHYs + * + * This file contains the device abstraction APIs for ethernet PHYs. + */ + +/* +* Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ +*/ +/* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the +* distribution. +* +* Neither the name of Texas Instruments Incorporated nor the names of +* its contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +*/ + +#include "hw_types.h" +#include "mdio.h" +#include "phy.h" + +#define PHY_ADV_VAL_MASK (0x01e0) +#define PHY_GIG_ADV_VAL_MASK (0x0300) + +/******************************************************************************* +* API FUNCTION DEFINITIONS +*******************************************************************************/ +/** + * \brief Reads the PHY ID. + * + * \param mdioBaseAddr Base Address of the MDIO Module Registers. + * \param phyAddr PHY Adress. + * + * \return 32 bit PHY ID (ID1:ID2) + * + **/ +unsigned int PhyIDGet(unsigned int mdioBaseAddr, unsigned int phyAddr) +{ + unsigned int id = 0; + unsigned short data; + + /* read the ID1 register */ + MDIOPhyRegRead(mdioBaseAddr, phyAddr, PHY_ID1, &data); + + /* update the ID1 value */ + id = data << PHY_ID_SHIFT; + + /* read the ID2 register */ + MDIOPhyRegRead(mdioBaseAddr, phyAddr, PHY_ID2, &data); + + /* update the ID2 value */ + id |= data; + + /* return the ID in ID1:ID2 format */ + return id; +} + +/** + * \brief Reads a register from the the PHY + * + * \param mdioBaseAddr Base Address of the MDIO Module Registers. + * \param phyAddr PHY Adress. + * \param regIdx Index of the register to be read + * \param regValAdr address where value of the register will be written + * + * \return status of the read + * + **/ +unsigned int PhyRegRead(unsigned int mdioBaseAddr, unsigned int phyAddr, + unsigned int regIdx, unsigned short *regValAdr) +{ + return (MDIOPhyRegRead(mdioBaseAddr, phyAddr, regIdx, regValAdr)); +} + +/** + * \brief Writes a register with the input + * + * \param mdioBaseAddr Base Address of the MDIO Module Registers. + * \param phyAddr PHY Adress. + * \param regIdx Index of the register to be read + * \param regValAdr value to be written + * + * \return None + * + **/ +void PhyRegWrite(unsigned int mdioBaseAddr, unsigned int phyAddr, + unsigned int regIdx, unsigned short regVal) +{ + MDIOPhyRegWrite(mdioBaseAddr, phyAddr, regIdx, regVal); +} + +/** + * \brief Enables Loop Back mode + * + * \param mdioBaseAddr Base Address of the MDIO Module Registers. + * \param phyAddr PHY Adress. + * + * \return status after enabling. \n + * TRUE if loop back is enabled \n + * FALSE if not able to enable + * + **/ +unsigned int PhyLoopBackEnable(unsigned int mdioBaseAddr, unsigned int phyAddr) +{ + unsigned short data; + + if(MDIOPhyRegRead(mdioBaseAddr, phyAddr, PHY_BCR, &data) != TRUE ) + { + return FALSE; + } + + data |= PHY_LPBK_ENABLE; + + /* Enable loop back */ + MDIOPhyRegWrite(mdioBaseAddr, phyAddr, PHY_BCR, data); + + return TRUE; +} + +/** + * \brief Disables Loop Back mode + * + * \param mdioBaseAddr Base Address of the MDIO Module Registers. + * \param phyAddr PHY Adress. + * + * \return status after enabling. \n + * TRUE if loop back is disabled \n + * FALSE if not able to disable + * + **/ +unsigned int PhyLoopBackDisable(unsigned int mdioBaseAddr, unsigned int phyAddr) +{ + unsigned short data; + + if(MDIOPhyRegRead(mdioBaseAddr, phyAddr, PHY_BCR, &data) != TRUE ) + { + return FALSE; + } + + data &= ~(PHY_LPBK_ENABLE); + + /* Disable loop back */ + MDIOPhyRegWrite(mdioBaseAddr, phyAddr, PHY_BCR, data); + + return TRUE; +} + +/** + * \brief Resets the PHY + * + * \param mdioBaseAddr Base Address of the MDIO Module Registers. + * \param phyAddr PHY Adress. + * \param speed Speed to be enabled + * \param duplexMode Duplex Mode + * + * \return status after configuring \n + * TRUE if configuration successful + * FALSE if configuration failed + * + **/ +unsigned int PhyReset(unsigned int mdioBaseAddr, unsigned int phyAddr) +{ + unsigned short data; + + data = PHY_SOFTRESET; + + /* Reset the phy */ + MDIOPhyRegWrite(mdioBaseAddr, phyAddr, PHY_BCR, data); + + /* wait till the reset bit is auto cleared */ + while(data & PHY_SOFTRESET) + { + /* Read the reset */ + if(MDIOPhyRegRead(mdioBaseAddr, phyAddr, PHY_BCR, &data) != TRUE) + { + return FALSE; + } + } + + return TRUE; +} + +/** + * \brief Configures the PHY for a given speed and duplex mode. + * + * \param mdioBaseAddr Base Address of the MDIO Module Registers. + * \param phyAddr PHY Adress. + * \param speed Speed to be enabled + * \param duplexMode Duplex Mode + * + * \return status after configuring \n + * TRUE if configuration successful + * FALSE if configuration failed + * + **/ +unsigned int PhyConfigure(unsigned int mdioBaseAddr, unsigned int phyAddr, + unsigned short speed, unsigned short duplexMode) +{ + /* Set the configurations */ + MDIOPhyRegWrite(mdioBaseAddr, phyAddr, PHY_BCR, (speed | duplexMode)); + + return TRUE; +} + +/** + * \brief This function ask the phy device to start auto negotiation. + * + * + * \param mdioBaseAddr Base Address of the MDIO Module Registers. + * \param phyAddr PHY Adress. + * \param advVal Autonegotiation advertisement value + * \param gigAdvVal Gigabit capability advertisement value + * advVal can take the following any OR combination of the values \n + * PHY_100BTX - 100BaseTX \n + * PHY_100BTX_FD - Full duplex capabilty for 100BaseTX \n + * PHY_10BT - 10BaseT \n + * PHY_10BT_FD - Full duplex capability for 10BaseT \n + * gigAdvVal can take one of the following values \n + * PHY_NO_1000BT - No 1000Base-T capability\n + * PHY_1000BT_FD - Full duplex capabilty for 1000 Base-T \n + * PHY_1000BT_HD - Half duplex capabilty for 1000 Base-T \n + * FALSE - It is passed as an argument if phy dosen't support + * Giga bit capability + * + * \return status after autonegotiation \n + * TRUE if autonegotiation started + * FALSE if autonegotiation not started + * + **/ +unsigned int PhyAutoNegotiate(unsigned int mdioBaseAddr, unsigned int phyAddr, + unsigned short *advPtr, unsigned short *gigAdvPtr) +{ + volatile unsigned short data; + volatile unsigned short anar; + + if(MDIOPhyRegRead(mdioBaseAddr, phyAddr, PHY_BCR, &data) != TRUE ) + { + return FALSE; + } + + data |= PHY_AUTONEG_ENABLE; + + if (*gigAdvPtr != 0) + { + /* Set phy for gigabit speed */ + data &= PHY_SPEED_MASK; + data |= PHY_SPEED_1000MBPS; + } + + /* Enable Auto Negotiation */ + MDIOPhyRegWrite(mdioBaseAddr, phyAddr, PHY_BCR, data); + + if(MDIOPhyRegRead(mdioBaseAddr, phyAddr, PHY_BCR, &data) != TRUE ) + { + return FALSE; + } + + /* Write Auto Negotiation capabilities */ + MDIOPhyRegRead(mdioBaseAddr, phyAddr, PHY_AUTONEG_ADV, &anar); + anar &= ~PHY_ADV_VAL_MASK; + MDIOPhyRegWrite(mdioBaseAddr, phyAddr, PHY_AUTONEG_ADV, (anar |(*advPtr))); + + /* Write Auto Negotiation Gigabyte capabilities */ + anar = 0; + MDIOPhyRegRead(mdioBaseAddr, phyAddr, PHY_1000BT_CONTROL, &anar); + anar &= ~PHY_GIG_ADV_VAL_MASK; + MDIOPhyRegWrite(mdioBaseAddr, phyAddr, PHY_1000BT_CONTROL, + (anar |(*gigAdvPtr))); + + data |= PHY_AUTONEG_RESTART; + + /* Start Auto Negotiation */ + MDIOPhyRegWrite(mdioBaseAddr, phyAddr, PHY_BCR, data); + + return TRUE; +} + +/** + * \brief Returns the status of Auto Negotiation completion. + * + * \param mdioBaseAddr Base Address of the MDIO Module Registers. + * \param phyAddr PHY Adress. + * + * \return Auto negotiation completion status \n + * TRUE if auto negotiation is completed + * FALSE if auto negotiation is not completed + **/ +unsigned int PhyAutoNegStatusGet(unsigned int mdioBaseAddr, unsigned int phyAddr) +{ + volatile unsigned short data; + + MDIOPhyRegRead(mdioBaseAddr, phyAddr, PHY_BSR, &data); + + /* Auto negotiation completion status */ + if(PHY_AUTONEG_COMPLETE == (data & (PHY_AUTONEG_STATUS))) + { + return TRUE; + } + + return FALSE; +} + +/** + * \brief Reads the Link Partner Ability register of the PHY. + * + * \param mdioBaseAddr Base Address of the MDIO Module Registers. + * \param phyAddr PHY Adress. + * \param ptnerAblty Pointer to which partner ability will be written. + * \param gbpsPtnerAblty Pointer to which Giga bit capability will be written. + * + * gbpsPtnerAblty can take following Macros.\n + * + * TRUE - It is passed as argument if phy supports Giga bit capability.\n + * FALSE - It is passed as argument if phy dosen't supports Giga bit + * capability.\n + * + * \return status after reading \n + * TRUE if reading successful + * FALSE if reading failed + **/ +unsigned int PhyPartnerAbilityGet(unsigned int mdioBaseAddr, + unsigned int phyAddr, + unsigned short *ptnerAblty, + unsigned short *gbpsPtnerAblty) +{ + unsigned int status; + + status = MDIOPhyRegRead(mdioBaseAddr, phyAddr, PHY_LINK_PARTNER_ABLTY, + ptnerAblty); + + if (*gbpsPtnerAblty != 0) + { + status = status | MDIOPhyRegRead(mdioBaseAddr, phyAddr, PHY_1000BT_STATUS, + gbpsPtnerAblty); + } + + return status; +} + +/** + * \brief Reads the link status of the PHY. + * + * \param mdioBaseAddr Base Address of the MDIO Module Registers. + * \param phyAddr PHY Adress. + * \param retries The number of retries before indicating down status + * + * \return link status after reading \n + * TRUE if link is up + * FALSE if link is down \n + * + * \note This reads both the basic status register of the PHY and the + * link register of MDIO for double check + **/ +unsigned int PhyLinkStatusGet(unsigned int mdioBaseAddr, + unsigned int phyAddr, + volatile unsigned int retries) +{ + volatile unsigned short linkStatus; + + retries++; + while (retries) + { + /* First read the BSR of the PHY */ + MDIOPhyRegRead(mdioBaseAddr, phyAddr, PHY_BSR, &linkStatus); + + if(linkStatus & PHY_LINK_STATUS) + { + return TRUE; + } + + retries--; + } + + return FALSE; +} + +/**************************** End Of File ***********************************/ |