mirror of https://github.com/ARMmbed/mbed-os.git
LPC546XX: Add ENET support
Signed-off-by: Mahadevan Mahesh <Mahesh.Mahadevan@nxp.com>pull/5609/head
parent
70e7b40468
commit
f2d2ed44cd
|
@ -0,0 +1,261 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "fsl_iocon.h"
|
||||
|
||||
#define IOCON_PIO_DIGITAL_EN 0x0100u /*!<@brief Enables digital function */
|
||||
#define IOCON_PIO_FUNC0 0x00u /*!<@brief Selects pin function 0 */
|
||||
#define IOCON_PIO_FUNC1 0x01u /*!<@brief Selects pin function 1 */
|
||||
#define IOCON_PIO_FUNC6 0x06u /*!<@brief Selects pin function 6 */
|
||||
#define IOCON_PIO_FUNC7 0x07u /*!<@brief Selects pin function 7 */
|
||||
#define IOCON_PIO_INPFILT_OFF 0x0200u /*!<@brief Input filter disabled */
|
||||
#define IOCON_PIO_INV_DI 0x00u /*!<@brief Input function is not inverted */
|
||||
#define IOCON_PIO_MODE_INACT 0x00u /*!<@brief No addition pin function */
|
||||
#define IOCON_PIO_MODE_PULLUP 0x20u /*!<@brief Selects pull-up function */
|
||||
#define IOCON_PIO_OPENDRAIN_DI 0x00u /*!<@brief Open drain is disabled */
|
||||
#define IOCON_PIO_SLEW_FAST 0x0400u /*!<@brief Fast mode, slew rate control is disabled */
|
||||
#define IOCON_PIO_SLEW_STANDARD 0x00u /*!<@brief Standard mode, output slew rate control is enabled */
|
||||
|
||||
/*******************************************************************************
|
||||
* Code
|
||||
******************************************************************************/
|
||||
void lpc546xx_init_eth_hardware(void)
|
||||
{
|
||||
CLOCK_EnableClock(kCLOCK_InputMux);
|
||||
|
||||
/* Enables the clock for the IOCON block. 0 = Disable; 1 = Enable.: 0x01u */
|
||||
CLOCK_EnableClock(kCLOCK_Iocon);
|
||||
|
||||
const uint32_t port0_pin10_config = (/* Pin is configured as SWO */
|
||||
IOCON_PIO_FUNC6 |
|
||||
/* No addition pin function */
|
||||
IOCON_PIO_MODE_INACT |
|
||||
/* Input function is not inverted */
|
||||
IOCON_PIO_INV_DI |
|
||||
/* Enables digital function */
|
||||
IOCON_PIO_DIGITAL_EN |
|
||||
/* Input filter disabled */
|
||||
IOCON_PIO_INPFILT_OFF |
|
||||
/* Open drain is disabled */
|
||||
IOCON_PIO_OPENDRAIN_DI);
|
||||
/* PORT0 PIN10 (coords: P2) is configured as SWO */
|
||||
IOCON_PinMuxSet(IOCON, 0U, 10U, port0_pin10_config);
|
||||
|
||||
const uint32_t port0_pin17_config = (/* Pin is configured as ENET_TXD1 */
|
||||
IOCON_PIO_FUNC7 |
|
||||
/* No addition pin function */
|
||||
IOCON_PIO_MODE_INACT |
|
||||
/* Input function is not inverted */
|
||||
IOCON_PIO_INV_DI |
|
||||
/* Enables digital function */
|
||||
IOCON_PIO_DIGITAL_EN |
|
||||
/* Input filter disabled */
|
||||
IOCON_PIO_INPFILT_OFF |
|
||||
/* Standard mode, output slew rate control is enabled */
|
||||
IOCON_PIO_SLEW_STANDARD |
|
||||
/* Open drain is disabled */
|
||||
IOCON_PIO_OPENDRAIN_DI);
|
||||
/* PORT0 PIN17 (coords: E14) is configured as ENET_TXD1 */
|
||||
IOCON_PinMuxSet(IOCON, 0U, 17U, port0_pin17_config);
|
||||
|
||||
const uint32_t port0_pin29_config = (/* Pin is configured as FC0_RXD_SDA_MOSI */
|
||||
IOCON_PIO_FUNC1 |
|
||||
/* No addition pin function */
|
||||
IOCON_PIO_MODE_INACT |
|
||||
/* Input function is not inverted */
|
||||
IOCON_PIO_INV_DI |
|
||||
/* Enables digital function */
|
||||
IOCON_PIO_DIGITAL_EN |
|
||||
/* Input filter disabled */
|
||||
IOCON_PIO_INPFILT_OFF |
|
||||
/* Fast mode, slew rate control is disabled */
|
||||
IOCON_PIO_SLEW_FAST |
|
||||
/* Open drain is disabled */
|
||||
IOCON_PIO_OPENDRAIN_DI);
|
||||
/* PORT0 PIN29 (coords: B13) is configured as FC0_RXD_SDA_MOSI */
|
||||
IOCON_PinMuxSet(IOCON, 0U, 29U, port0_pin29_config);
|
||||
|
||||
const uint32_t port0_pin30_config = (/* Pin is configured as FC0_TXD_SCL_MISO */
|
||||
IOCON_PIO_FUNC1 |
|
||||
/* No addition pin function */
|
||||
IOCON_PIO_MODE_INACT |
|
||||
/* Input function is not inverted */
|
||||
IOCON_PIO_INV_DI |
|
||||
/* Enables digital function */
|
||||
IOCON_PIO_DIGITAL_EN |
|
||||
/* Input filter disabled */
|
||||
IOCON_PIO_INPFILT_OFF |
|
||||
/* Fast mode, slew rate control is disabled */
|
||||
IOCON_PIO_SLEW_FAST |
|
||||
/* Open drain is disabled */
|
||||
IOCON_PIO_OPENDRAIN_DI);
|
||||
/* PORT0 PIN30 (coords: A2) is configured as FC0_TXD_SCL_MISO */
|
||||
IOCON_PinMuxSet(IOCON, 0U, 30U, port0_pin30_config);
|
||||
|
||||
const uint32_t port2_pin26_config = (/* Pin is configured as PIO2_26 */
|
||||
IOCON_PIO_FUNC0 |
|
||||
/* Selects pull-up function */
|
||||
IOCON_PIO_MODE_PULLUP |
|
||||
/* Input function is not inverted */
|
||||
IOCON_PIO_INV_DI |
|
||||
/* Enables digital function */
|
||||
IOCON_PIO_DIGITAL_EN |
|
||||
/* Input filter disabled */
|
||||
IOCON_PIO_INPFILT_OFF |
|
||||
/* Standard mode, output slew rate control is enabled */
|
||||
IOCON_PIO_SLEW_STANDARD |
|
||||
/* Open drain is disabled */
|
||||
IOCON_PIO_OPENDRAIN_DI);
|
||||
/* PORT2 PIN26 (coords: H11) is configured as PIO2_26 */
|
||||
IOCON_PinMuxSet(IOCON, 2U, 26U, port2_pin26_config);
|
||||
|
||||
const uint32_t port4_pin10_config = (/* Pin is configured as ENET_RX_DV */
|
||||
IOCON_PIO_FUNC1 |
|
||||
/* No addition pin function */
|
||||
IOCON_PIO_MODE_INACT |
|
||||
/* Input function is not inverted */
|
||||
IOCON_PIO_INV_DI |
|
||||
/* Enables digital function */
|
||||
IOCON_PIO_DIGITAL_EN |
|
||||
/* Input filter disabled */
|
||||
IOCON_PIO_INPFILT_OFF |
|
||||
/* Standard mode, output slew rate control is enabled */
|
||||
IOCON_PIO_SLEW_STANDARD |
|
||||
/* Open drain is disabled */
|
||||
IOCON_PIO_OPENDRAIN_DI);
|
||||
/* PORT4 PIN10 (coords: B9) is configured as ENET_RX_DV */
|
||||
IOCON_PinMuxSet(IOCON, 4U, 10U, port4_pin10_config);
|
||||
|
||||
const uint32_t port4_pin11_config = (/* Pin is configured as ENET_RXD0 */
|
||||
IOCON_PIO_FUNC1 |
|
||||
/* No addition pin function */
|
||||
IOCON_PIO_MODE_INACT |
|
||||
/* Input function is not inverted */
|
||||
IOCON_PIO_INV_DI |
|
||||
/* Enables digital function */
|
||||
IOCON_PIO_DIGITAL_EN |
|
||||
/* Input filter disabled */
|
||||
IOCON_PIO_INPFILT_OFF |
|
||||
/* Standard mode, output slew rate control is enabled */
|
||||
IOCON_PIO_SLEW_STANDARD |
|
||||
/* Open drain is disabled */
|
||||
IOCON_PIO_OPENDRAIN_DI);
|
||||
/* PORT4 PIN11 (coords: A9) is configured as ENET_RXD0 */
|
||||
IOCON_PinMuxSet(IOCON, 4U, 11U, port4_pin11_config);
|
||||
|
||||
const uint32_t port4_pin12_config = (/* Pin is configured as ENET_RXD1 */
|
||||
IOCON_PIO_FUNC1 |
|
||||
/* No addition pin function */
|
||||
IOCON_PIO_MODE_INACT |
|
||||
/* Input function is not inverted */
|
||||
IOCON_PIO_INV_DI |
|
||||
/* Enables digital function */
|
||||
IOCON_PIO_DIGITAL_EN |
|
||||
/* Input filter disabled */
|
||||
IOCON_PIO_INPFILT_OFF |
|
||||
/* Standard mode, output slew rate control is enabled */
|
||||
IOCON_PIO_SLEW_STANDARD |
|
||||
/* Open drain is disabled */
|
||||
IOCON_PIO_OPENDRAIN_DI);
|
||||
/* PORT4 PIN12 (coords: A6) is configured as ENET_RXD1 */
|
||||
IOCON_PinMuxSet(IOCON, 4U, 12U, port4_pin12_config);
|
||||
|
||||
const uint32_t port4_pin13_config = (/* Pin is configured as ENET_TX_EN */
|
||||
IOCON_PIO_FUNC1 |
|
||||
/* No addition pin function */
|
||||
IOCON_PIO_MODE_INACT |
|
||||
/* Input function is not inverted */
|
||||
IOCON_PIO_INV_DI |
|
||||
/* Enables digital function */
|
||||
IOCON_PIO_DIGITAL_EN |
|
||||
/* Input filter disabled */
|
||||
IOCON_PIO_INPFILT_OFF |
|
||||
/* Standard mode, output slew rate control is enabled */
|
||||
IOCON_PIO_SLEW_STANDARD |
|
||||
/* Open drain is disabled */
|
||||
IOCON_PIO_OPENDRAIN_DI);
|
||||
/* PORT4 PIN13 (coords: B6) is configured as ENET_TX_EN */
|
||||
IOCON_PinMuxSet(IOCON, 4U, 13U, port4_pin13_config);
|
||||
|
||||
const uint32_t port4_pin14_config = (/* Pin is configured as ENET_RX_CLK */
|
||||
IOCON_PIO_FUNC1 |
|
||||
/* No addition pin function */
|
||||
IOCON_PIO_MODE_INACT |
|
||||
/* Input function is not inverted */
|
||||
IOCON_PIO_INV_DI |
|
||||
/* Enables digital function */
|
||||
IOCON_PIO_DIGITAL_EN |
|
||||
/* Input filter disabled */
|
||||
IOCON_PIO_INPFILT_OFF |
|
||||
/* Standard mode, output slew rate control is enabled */
|
||||
IOCON_PIO_SLEW_STANDARD |
|
||||
/* Open drain is disabled */
|
||||
IOCON_PIO_OPENDRAIN_DI);
|
||||
/* PORT4 PIN14 (coords: B5) is configured as ENET_RX_CLK */
|
||||
IOCON_PinMuxSet(IOCON, 4U, 14U, port4_pin14_config);
|
||||
|
||||
const uint32_t port4_pin15_config = (/* Pin is configured as ENET_MDC */
|
||||
IOCON_PIO_FUNC1 |
|
||||
/* No addition pin function */
|
||||
IOCON_PIO_MODE_INACT |
|
||||
/* Input function is not inverted */
|
||||
IOCON_PIO_INV_DI |
|
||||
/* Enables digital function */
|
||||
IOCON_PIO_DIGITAL_EN |
|
||||
/* Input filter disabled */
|
||||
IOCON_PIO_INPFILT_OFF |
|
||||
/* Standard mode, output slew rate control is enabled */
|
||||
IOCON_PIO_SLEW_STANDARD |
|
||||
/* Open drain is disabled */
|
||||
IOCON_PIO_OPENDRAIN_DI);
|
||||
/* PORT4 PIN15 (coords: A4) is configured as ENET_MDC */
|
||||
IOCON_PinMuxSet(IOCON, 4U, 15U, port4_pin15_config);
|
||||
|
||||
const uint32_t port4_pin16_config = (/* Pin is configured as ENET_MDIO */
|
||||
IOCON_PIO_FUNC1 |
|
||||
/* No addition pin function */
|
||||
IOCON_PIO_MODE_INACT |
|
||||
/* Input function is not inverted */
|
||||
IOCON_PIO_INV_DI |
|
||||
/* Enables digital function */
|
||||
IOCON_PIO_DIGITAL_EN |
|
||||
/* Input filter disabled */
|
||||
IOCON_PIO_INPFILT_OFF |
|
||||
/* Standard mode, output slew rate control is enabled */
|
||||
IOCON_PIO_SLEW_STANDARD |
|
||||
/* Open drain is disabled */
|
||||
IOCON_PIO_OPENDRAIN_DI);
|
||||
/* PORT4 PIN16 (coords: C4) is configured as ENET_MDIO */
|
||||
IOCON_PinMuxSet(IOCON, 4U, 16U, port4_pin16_config);
|
||||
|
||||
const uint32_t port4_pin8_config = (/* Pin is configured as ENET_TXD0 */
|
||||
IOCON_PIO_FUNC1 |
|
||||
/* No addition pin function */
|
||||
IOCON_PIO_MODE_INACT |
|
||||
/* Input function is not inverted */
|
||||
IOCON_PIO_INV_DI |
|
||||
/* Enables digital function */
|
||||
IOCON_PIO_DIGITAL_EN |
|
||||
/* Input filter disabled */
|
||||
IOCON_PIO_INPFILT_OFF |
|
||||
/* Standard mode, output slew rate control is enabled */
|
||||
IOCON_PIO_SLEW_STANDARD |
|
||||
/* Open drain is disabled */
|
||||
IOCON_PIO_OPENDRAIN_DI);
|
||||
/* PORT4 PIN8 (coords: B14) is configured as ENET_TXD0 */
|
||||
IOCON_PinMuxSet(IOCON, 4U, 8U, port4_pin8_config);
|
||||
}
|
||||
|
|
@ -0,0 +1,692 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
#include "lwip/sys.h"
|
||||
#include "lwip/def.h"
|
||||
#include "lwip/mem.h"
|
||||
#include "lwip/pbuf.h"
|
||||
#include "lwip/stats.h"
|
||||
#include "lwip/snmp.h"
|
||||
#include "lwip/tcpip.h"
|
||||
#include "lwip/ethip6.h"
|
||||
#include "netif/etharp.h"
|
||||
#include "netif/ppp/pppoe.h"
|
||||
|
||||
#include "eth_arch.h"
|
||||
#include "sys_arch.h"
|
||||
|
||||
#include "fsl_phy.h"
|
||||
#include "lpc546xx_emac_config.h"
|
||||
#include <string.h>
|
||||
|
||||
#include "mbed_interface.h"
|
||||
|
||||
#if LWIP_ARP || LWIP_ETHERNET
|
||||
|
||||
enet_handle_t g_handle;
|
||||
// RX packet buffer pointers
|
||||
struct pbuf *rx_buff[ENET_RX_RING_LEN];
|
||||
// TX packet buffer pointers
|
||||
uint8_t *tx_buff[ENET_TX_RING_LEN];
|
||||
// RX packet payload pointers
|
||||
uint32_t rx_ptr[ENET_RX_RING_LEN];
|
||||
|
||||
/** \brief Debug output formatter lock define
|
||||
*
|
||||
* When using FreeRTOS and with LWIP_DEBUG enabled, enabling this
|
||||
* define will allow RX debug messages to not interleave with the
|
||||
* TX messages (so they are actually readable). Not enabling this
|
||||
* define when the system is under load will cause the output to
|
||||
* be unreadable. There is a small tradeoff in performance for this
|
||||
* so use it only for debug. */
|
||||
//#define LOCK_RX_THREAD
|
||||
|
||||
/* LPC546XX EMAC driver data structure */
|
||||
struct lpc_enetdata {
|
||||
struct netif *netif; /**< Reference back to LWIP parent netif */
|
||||
osThreadId_t thread; /**< Processing thread */
|
||||
sys_mutex_t TXLockMutex; /**< TX critical section mutex */
|
||||
sys_sem_t xTXDCountSem; /**< TX free buffer counting semaphore */
|
||||
};
|
||||
|
||||
static struct lpc_enetdata lpc_enetdata;
|
||||
|
||||
/* \brief Flags for worker thread */
|
||||
#define FLAG_TX 1
|
||||
#define FLAG_RX 2
|
||||
|
||||
/** \brief Driver thread priority */
|
||||
#define THREAD_PRIORITY (osPriorityNormal)
|
||||
|
||||
#ifdef LWIP_DEBUG
|
||||
#define THREAD_STACKSIZE (DEFAULT_THREAD_STACKSIZE * 5)
|
||||
#else
|
||||
#define THREAD_STACKSIZE DEFAULT_THREAD_STACKSIZE
|
||||
#endif
|
||||
|
||||
static void lpc546xx_phy_task(void *data);
|
||||
static void lpc546xx_packet_rx(struct lpc_enetdata *lpc_enet);
|
||||
static void lpc546xx_packet_tx(struct lpc_enetdata *lpc_enet);
|
||||
|
||||
#define PHY_TASK_PERIOD_MS 200
|
||||
|
||||
/********************************************************************************
|
||||
* Buffer management
|
||||
********************************************************************************/
|
||||
/*
|
||||
* This function will queue a new receive buffer
|
||||
*/
|
||||
static void update_read_buffer(enet_rx_bd_struct_t *rxDesc, uint8_t *buf)
|
||||
{
|
||||
enet_rx_bd_ring_t *rxBdRing = (enet_rx_bd_ring_t *)&g_handle.rxBdRing[0];
|
||||
uint32_t control = ENET_RXDESCRIP_RD_OWN_MASK | ENET_RXDESCRIP_RD_BUFF1VALID_MASK | ENET_RXDESCRIP_RD_IOC_MASK;
|
||||
uint32_t index = rxBdRing->rxGenIdx;
|
||||
/* Increase the index. */
|
||||
index++;
|
||||
if (index >= ENET_RX_RING_LEN) {
|
||||
index = 0;
|
||||
}
|
||||
|
||||
rxBdRing->rxGenIdx = index;
|
||||
if (buf != NULL) {
|
||||
rxDesc->buff1Addr = (uint32_t)buf;
|
||||
}
|
||||
|
||||
rxDesc->buff2Addr = 0;
|
||||
rxDesc->reserved = 0;
|
||||
rxDesc->control = control;
|
||||
}
|
||||
|
||||
/** \brief Free TX buffers that are complete
|
||||
*
|
||||
* \param[in] lpc_enet Pointer to driver data structure
|
||||
*/
|
||||
static void lpc546xx_tx_reclaim(struct lpc_enetdata *lpc_enet)
|
||||
{
|
||||
static uint8_t consume_index = 0;
|
||||
enet_tx_bd_ring_t *txBdRing = (enet_tx_bd_ring_t *)&g_handle.txBdRing[0];
|
||||
|
||||
while (consume_index != txBdRing->txConsumIdx) {
|
||||
/* Free the transmit buffer */
|
||||
free(tx_buff[consume_index]);
|
||||
tx_buff[consume_index] = NULL;
|
||||
|
||||
osSemaphoreRelease(lpc_enetdata.xTXDCountSem.id);
|
||||
|
||||
consume_index++;
|
||||
if (consume_index >= ENET_TX_RING_LEN) {
|
||||
consume_index = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************************
|
||||
* Internal data
|
||||
********************************************************************************/
|
||||
#define ENET_BuffSizeAlign(n) ENET_ALIGN(n, ENET_BUFF_ALIGNMENT)
|
||||
#define ENET_ALIGN(x,align) ((unsigned int)((x) + ((align)-1)) & (unsigned int)(~(unsigned int)((align)- 1)))
|
||||
extern void lpc546xx_init_eth_hardware(void);
|
||||
|
||||
/** \brief Allocates a pbuf and returns the data from the incoming packet.
|
||||
*
|
||||
* \param[in] netif the lwip network interface structure for this lpc_enetif
|
||||
* \return a pbuf filled with the received packet (including MAC header)
|
||||
* NULL on memory error
|
||||
*/
|
||||
static struct pbuf *lpc546xx_low_level_input(struct netif *netif)
|
||||
{
|
||||
enet_rx_bd_ring_t *rxBdRing = (enet_rx_bd_ring_t *)&g_handle.rxBdRing[0];
|
||||
enet_rx_bd_struct_t *bdPtr = rxBdRing->rxBdBase + rxBdRing->rxGenIdx;
|
||||
#ifdef LOCK_RX_THREAD
|
||||
struct lpc_enetdata *lpc_enet = netif->state;
|
||||
#endif
|
||||
struct pbuf *p = NULL;
|
||||
struct pbuf *temp_rxbuf = NULL;
|
||||
u32_t length = 0;
|
||||
|
||||
/* Determine if a frame has been received */
|
||||
if ((bdPtr->control & ENET_RXDESCRIP_WR_OWN_MASK) != 0) {
|
||||
return p;
|
||||
}
|
||||
|
||||
#ifdef LOCK_RX_THREAD
|
||||
/* Get exclusive access */
|
||||
sys_mutex_lock(&lpc_enet->TXLockMutex);
|
||||
#endif
|
||||
|
||||
/* Determine if the received frame has an error */
|
||||
if ((bdPtr->control & ENET_RXDESCRIP_WR_ERRSUM_MASK) != 0) {
|
||||
LINK_STATS_INC(link.err);
|
||||
LINK_STATS_INC(link.drop);
|
||||
/* Re-use the same buffer in case of error */
|
||||
update_read_buffer(bdPtr, NULL);
|
||||
} else {
|
||||
if (bdPtr->control & ENET_RXDESCRIP_WR_LD_MASK) {
|
||||
length = (bdPtr->control & ENET_RXDESCRIP_WR_PACKETLEN_MASK);
|
||||
} else {
|
||||
length = rxBdRing->rxBuffSizeAlign;
|
||||
}
|
||||
|
||||
/* Zero-copy */
|
||||
p = rx_buff[rxBdRing->rxGenIdx];
|
||||
p->len = length;
|
||||
|
||||
/* Attempt to queue new buffer */
|
||||
temp_rxbuf = pbuf_alloc(PBUF_RAW, ENET_ALIGN(ENET_ETH_MAX_FLEN, ENET_BUFF_ALIGNMENT), PBUF_RAM);
|
||||
if (NULL == temp_rxbuf) {
|
||||
/* Drop frame (out of memory) */
|
||||
LINK_STATS_INC(link.drop);
|
||||
|
||||
LWIP_DEBUGF(UDP_LPC_EMAC | LWIP_DBG_TRACE,
|
||||
("lpc546xx_low_level_input: Packet index %d dropped for OOM\n", rxBdRing->rxGenIdx));
|
||||
|
||||
/* Re-queue the same buffer */
|
||||
update_read_buffer(bdPtr, NULL);
|
||||
|
||||
#ifdef LOCK_RX_THREAD
|
||||
sys_mutex_unlock(&lpc_enet->TXLockMutex);
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rx_buff[rxBdRing->rxGenIdx] = temp_rxbuf;
|
||||
rx_ptr[rxBdRing->rxGenIdx] = (uint32_t)rx_buff[rxBdRing->rxGenIdx]->payload;
|
||||
|
||||
LWIP_DEBUGF(UDP_LPC_EMAC | LWIP_DBG_TRACE,
|
||||
("lpc_low_level_input: Packet received: %p, size %"PRIu32" (index=%d)\n", p, length, rxBdRing->rxGenIdx));
|
||||
|
||||
update_read_buffer(bdPtr, rx_buff[rxBdRing->rxGenIdx]->payload);
|
||||
|
||||
/* Save size */
|
||||
p->tot_len = (u16_t)length;
|
||||
LINK_STATS_INC(link.recv);
|
||||
}
|
||||
|
||||
#ifdef LOCK_RX_THREAD
|
||||
sys_mutex_unlock(&lpc_enet->TXLockMutex);
|
||||
#endif
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/** \brief Attempt to read a packet from the EMAC interface.
|
||||
*
|
||||
* \param[in] netif the lwip network interface structure
|
||||
*/
|
||||
void lpc546xx_enetif_input(struct netif *netif)
|
||||
{
|
||||
struct pbuf *p;
|
||||
|
||||
/* move received packet into a new pbuf */
|
||||
p = lpc546xx_low_level_input(netif);
|
||||
if (p == NULL)
|
||||
return;
|
||||
|
||||
/* pass all packets to ethernet_input, which decides what packets it supports */
|
||||
if (netif->input(p, netif) != ERR_OK) {
|
||||
LWIP_DEBUGF(NETIF_DEBUG, ("lpc546xx_enetif_input: input error\n"));
|
||||
/* Free buffer */
|
||||
pbuf_free(p);
|
||||
}
|
||||
}
|
||||
|
||||
/** \brief Worker thread.
|
||||
*
|
||||
* Woken by thread flags to receive packets or clean up transmit
|
||||
*
|
||||
* \param[in] pvParameters pointer to the interface data
|
||||
*/
|
||||
static void lpc546xx_emac_thread(void* pvParameters)
|
||||
{
|
||||
struct lpc_enetdata *lpc_enet = pvParameters;
|
||||
|
||||
for (;;) {
|
||||
uint32_t flags = osThreadFlagsWait(FLAG_RX | FLAG_TX, osFlagsWaitAny, PHY_TASK_PERIOD_MS);
|
||||
if (flags == osFlagsErrorTimeout) {
|
||||
// Rather than calling strictly every period, we call when idle
|
||||
// for that period - hopefully good enough. We run this task
|
||||
// from lwIP's thread rather than our RX/TX thread, as PHY reads can
|
||||
// be slow, and we don't want them to interfere with data pumping.
|
||||
// This is analogous to the way the PHY polling works in the Nanostack
|
||||
// version of the driver
|
||||
tcpip_callback_with_block(lpc546xx_phy_task, lpc_enet->netif, 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
LWIP_ASSERT("osThreadFlagsWait error", !(flags & osFlagsError));
|
||||
if (flags & FLAG_RX) {
|
||||
lpc546xx_packet_rx(lpc_enet);
|
||||
}
|
||||
|
||||
if (flags & FLAG_TX) {
|
||||
lpc546xx_packet_tx(lpc_enet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \brief Low level output of a packet. Never call this from an
|
||||
* interrupt context, as it may block until TX descriptors
|
||||
* become available.
|
||||
*
|
||||
* \param[in] netif the lwip network interface structure for this lpc_enetif
|
||||
* \param[in] p the MAC packet to send (e.g. IP packet including MAC addresses and type)
|
||||
* \return ERR_OK if the packet could be sent or an err_t value if the packet couldn't be sent
|
||||
*/
|
||||
static err_t lpc546xx_low_level_output(struct netif *netif, struct pbuf *p)
|
||||
{
|
||||
struct lpc_enetdata *lpc_enet = netif->state;
|
||||
struct pbuf *q;
|
||||
uint8_t *psend = NULL, *dst;
|
||||
enet_tx_bd_ring_t *txBdRing = (enet_tx_bd_ring_t *)&g_handle.txBdRing[0];
|
||||
uint32_t index = txBdRing->txGenIdx;
|
||||
|
||||
psend = (uint8_t *)calloc(1, ENET_ALIGN(p->tot_len, ENET_BUFF_ALIGNMENT));
|
||||
if (NULL == psend)
|
||||
return ERR_MEM;
|
||||
|
||||
if (p->len == p->tot_len) {
|
||||
MEMCPY(psend, p->payload, p->len);
|
||||
} else {
|
||||
for (q = p, dst = psend; q != NULL; q = q->next) {
|
||||
MEMCPY(dst, q->payload, q->len);
|
||||
dst += q->len;
|
||||
}
|
||||
}
|
||||
/* Check if a descriptor is available for the transfer. */
|
||||
osStatus_t stat = osSemaphoreAcquire(lpc_enet->xTXDCountSem.id, 0);
|
||||
if (stat != osOK)
|
||||
return ERR_BUF;
|
||||
|
||||
/* Save the buffer so that it can be freed when transmit is done */
|
||||
tx_buff[index] = psend;
|
||||
|
||||
if (ENET_SendFrame(ENET, &g_handle, psend, p->tot_len) != kStatus_Success) {
|
||||
free(psend);
|
||||
tx_buff[index] = NULL;
|
||||
return ERR_IF;
|
||||
}
|
||||
|
||||
LINK_STATS_INC(link.xmit);
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/** \brief Ethernet receive interrupt handler
|
||||
*
|
||||
* This function handles the receive interrupt of LPC546XX.
|
||||
*/
|
||||
void enet_mac_rx_isr()
|
||||
{
|
||||
osThreadFlagsSet(lpc_enetdata.thread, FLAG_RX);
|
||||
}
|
||||
|
||||
void enet_mac_tx_isr()
|
||||
{
|
||||
osThreadFlagsSet(lpc_enetdata.thread, FLAG_TX);
|
||||
}
|
||||
|
||||
void ethernet_callback(ENET_Type *base, enet_handle_t *handle, enet_event_t event, uint8_t channel, void *param)
|
||||
{
|
||||
switch (event)
|
||||
{
|
||||
case kENET_RxIntEvent:
|
||||
enet_mac_rx_isr();
|
||||
break;
|
||||
case kENET_TxIntEvent:
|
||||
enet_mac_tx_isr();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if NO_SYS == 0
|
||||
|
||||
/** \brief Packet reception task
|
||||
*
|
||||
* This task is called when a packet is received. It will
|
||||
* pass the packet to the LWIP core.
|
||||
*
|
||||
* \param[in] lpc_enet pointer to the interface data
|
||||
*/
|
||||
static void lpc546xx_packet_rx(struct lpc_enetdata *lpc_enet)
|
||||
{
|
||||
enet_rx_bd_ring_t *rxBdRing = (enet_rx_bd_ring_t *)&g_handle.rxBdRing[0];
|
||||
enet_rx_bd_struct_t *bdPtr = rxBdRing->rxBdBase + rxBdRing->rxGenIdx;
|
||||
bool suspend = false;
|
||||
|
||||
/* Suspend and command for rx. */
|
||||
if (ENET->DMA_CH[0].DMA_CHX_STAT & ENET_DMA_CH_DMA_CHX_STAT_RBU_MASK) {
|
||||
suspend = true;
|
||||
}
|
||||
|
||||
while ((bdPtr->control & ENET_RXDESCRIP_WR_OWN_MASK) == 0) {
|
||||
lpc546xx_enetif_input(lpc_enet->netif);
|
||||
/* rxGenIdx gets updated, gets the next receive buffer descriptor */
|
||||
bdPtr = rxBdRing->rxBdBase + rxBdRing->rxGenIdx;
|
||||
}
|
||||
|
||||
/* Set command for rx when it is suspend. */
|
||||
if (suspend)
|
||||
{
|
||||
ENET->DMA_CH[0].DMA_CHX_RXDESC_TAIL_PTR = ENET->DMA_CH[0].DMA_CHX_RXDESC_TAIL_PTR;
|
||||
}
|
||||
}
|
||||
|
||||
/** \brief Transmit cleanup task
|
||||
*
|
||||
* This task is called when a transmit interrupt occurs and
|
||||
* reclaims the pbuf and descriptor used for the packet once
|
||||
* the packet has been transferred.
|
||||
*
|
||||
* \param[in] lpc_enet pointer to the interface data
|
||||
*/
|
||||
static void lpc546xx_packet_tx(struct lpc_enetdata *lpc_enet)
|
||||
{
|
||||
lpc546xx_tx_reclaim(lpc_enet);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/** \brief Low level init of the MAC and PHY.
|
||||
*
|
||||
* \param[in] netif Pointer to LWIP netif structure
|
||||
*/
|
||||
static err_t low_level_init(struct netif *netif)
|
||||
{
|
||||
status_t status;
|
||||
uint8_t i;
|
||||
uint32_t refClock;
|
||||
phy_speed_t phy_speed;
|
||||
phy_duplex_t phy_duplex;
|
||||
uint32_t phyAddr = 0;
|
||||
bool link = false;
|
||||
enet_config_t config;
|
||||
uint32_t timeout = 0xFFFU;
|
||||
|
||||
lpc546xx_init_eth_hardware();
|
||||
|
||||
refClock = CLOCK_GetFreq(kCLOCK_CoreSysClk);
|
||||
|
||||
status = PHY_Init(ENET, phyAddr, refClock);
|
||||
if (status != kStatus_Success) {
|
||||
return ERR_IF;
|
||||
}
|
||||
|
||||
ENET_GetDefaultConfig(&config);
|
||||
config.multiqueueCfg = NULL;
|
||||
|
||||
while ((!link) && timeout) {
|
||||
PHY_GetLinkStatus(ENET, phyAddr, &link);
|
||||
if (link) {
|
||||
/* Get link information from PHY */
|
||||
PHY_GetLinkSpeedDuplex(ENET, phyAddr, &phy_speed, &phy_duplex);
|
||||
|
||||
/* Change the MII speed and duplex for actual link status. */
|
||||
config.miiSpeed = (enet_mii_speed_t)phy_speed;
|
||||
config.miiDuplex = (enet_mii_duplex_t)phy_duplex;
|
||||
}
|
||||
timeout--;
|
||||
}
|
||||
|
||||
if (!link) {
|
||||
return ERR_CONN;
|
||||
}
|
||||
|
||||
AT_NONCACHEABLE_SECTION_ALIGN(static enet_rx_bd_struct_t rx_desc_start_addr[ENET_RX_RING_LEN], ENET_BUFF_ALIGNMENT);
|
||||
AT_NONCACHEABLE_SECTION_ALIGN(static enet_tx_bd_struct_t tx_desc_start_addr[ENET_TX_RING_LEN], ENET_BUFF_ALIGNMENT);
|
||||
|
||||
/* Create buffers for each receive BD */
|
||||
for (i = 0; i < ENET_RX_RING_LEN; i++) {
|
||||
rx_buff[i] = pbuf_alloc(PBUF_RAW, ENET_ALIGN(ENET_ETH_MAX_FLEN, ENET_BUFF_ALIGNMENT), PBUF_RAM);
|
||||
if (NULL == rx_buff[i])
|
||||
return ERR_MEM;
|
||||
|
||||
rx_ptr[i] = (uint32_t)rx_buff[i]->payload;
|
||||
}
|
||||
|
||||
/* prepare the buffer configuration. */
|
||||
enet_buffer_config_t buffCfg = {
|
||||
ENET_RX_RING_LEN,
|
||||
ENET_TX_RING_LEN,
|
||||
&tx_desc_start_addr[0],
|
||||
&tx_desc_start_addr[0],
|
||||
&rx_desc_start_addr[0],
|
||||
&rx_desc_start_addr[ENET_RX_RING_LEN],
|
||||
rx_ptr,
|
||||
ENET_ALIGN(ENET_ETH_MAX_FLEN, ENET_BUFF_ALIGNMENT),
|
||||
};
|
||||
|
||||
ENET_Init(ENET, &config, netif->hwaddr, refClock);
|
||||
|
||||
/* Enable the tx & rx interrupt. */
|
||||
ENET_EnableInterrupts(ENET, kENET_DmaTx | kENET_DmaRx);
|
||||
|
||||
/* Create the handler. */
|
||||
ENET_CreateHandler(ENET, &g_handle, &config, &buffCfg, ethernet_callback, netif);
|
||||
|
||||
/* Initialize Descriptor. */
|
||||
ENET_DescriptorInit(ENET, &config, &buffCfg);
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is the ethernet IPv4 packet send function. It calls
|
||||
* etharp_output after checking link status.
|
||||
*
|
||||
* \param[in] netif the lwip network interface structure for this lpc_enetif
|
||||
* \param[in] q Pointer to pbug to send
|
||||
* \param[in] ipaddr IP address
|
||||
* \return ERR_OK or error code
|
||||
*/
|
||||
#if LWIP_IPV4
|
||||
err_t lpc546xx_etharp_output_ipv4(struct netif *netif, struct pbuf *q,
|
||||
const ip4_addr_t *ipaddr)
|
||||
{
|
||||
/* Only send packet is link is up */
|
||||
if (netif->flags & NETIF_FLAG_LINK_UP)
|
||||
return etharp_output(netif, q, ipaddr);
|
||||
|
||||
return ERR_CONN;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This function is the ethernet IPv6 packet send function. It calls
|
||||
* etharp_output after checking link status.
|
||||
*
|
||||
* \param[in] netif the lwip network interface structure for this lpc_enetif
|
||||
* \param[in] q Pointer to pbug to send
|
||||
* \param[in] ipaddr IP address
|
||||
* \return ERR_OK or error code
|
||||
*/
|
||||
#if LWIP_IPV6
|
||||
err_t lpc546xx_etharp_output_ipv6(struct netif *netif, struct pbuf *q,
|
||||
const ip6_addr_t *ipaddr)
|
||||
{
|
||||
/* Only send packet is link is up */s
|
||||
if (netif->flags & NETIF_FLAG_LINK_UP)
|
||||
return ethip6_output(netif, q, ipaddr);
|
||||
|
||||
return ERR_CONN;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*******************************************************************************
|
||||
* PHY task: monitor link
|
||||
*******************************************************************************/
|
||||
|
||||
#define STATE_UNKNOWN (-1)
|
||||
|
||||
typedef struct {
|
||||
int connected;
|
||||
phy_speed_t speed;
|
||||
phy_duplex_t duplex;
|
||||
} PHY_STATE;
|
||||
|
||||
int phy_link_status(void) {
|
||||
bool connection_status;
|
||||
uint32_t phyAddr = 0;
|
||||
|
||||
PHY_GetLinkStatus(ENET, phyAddr, &connection_status);
|
||||
return (int)connection_status;
|
||||
}
|
||||
|
||||
static void lpc546xx_phy_task(void *data) {
|
||||
struct netif *netif = (struct netif*)data;
|
||||
static PHY_STATE prev_state = {STATE_UNKNOWN, (phy_speed_t)STATE_UNKNOWN, (phy_duplex_t)STATE_UNKNOWN};
|
||||
uint32_t phyAddr = 0;
|
||||
// Get current status
|
||||
PHY_STATE crt_state;
|
||||
bool connection_status;
|
||||
|
||||
PHY_GetLinkStatus(ENET, phyAddr, &connection_status);
|
||||
crt_state.connected = connection_status;
|
||||
// Get the actual PHY link speed
|
||||
PHY_GetLinkSpeedDuplex(ENET, phyAddr, &crt_state.speed, &crt_state.duplex);
|
||||
|
||||
// Compare with previous state
|
||||
if (crt_state.connected != prev_state.connected) {
|
||||
// We're called from lwIP's tcpip thread, so can call link functions directly
|
||||
if (crt_state.connected) {
|
||||
netif_set_link_up(netif);
|
||||
} else {
|
||||
netif_set_link_down(netif);
|
||||
}
|
||||
}
|
||||
|
||||
if (crt_state.speed != prev_state.speed) {
|
||||
uint32_t fes = ENET->MAC_CONFIG;
|
||||
fes &= ~ENET_MAC_CONFIG_FES_MASK;
|
||||
if (prev_state.speed != (phy_speed_t)STATE_UNKNOWN) {
|
||||
fes |= ENET_MAC_CONFIG_FES(!crt_state.speed);
|
||||
} else {
|
||||
fes |= ENET_MAC_CONFIG_FES(crt_state.speed);
|
||||
}
|
||||
|
||||
ENET->MAC_CONFIG = fes;
|
||||
}
|
||||
|
||||
prev_state = crt_state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should be called at the beginning of the program to set up the
|
||||
* network interface.
|
||||
*
|
||||
* This function should be passed as a parameter to netif_add().
|
||||
*
|
||||
* @param[in] netif the lwip network interface structure for this lpc_enetif
|
||||
* @return ERR_OK if the loopif is initialized
|
||||
* ERR_MEM if private data couldn't be allocated
|
||||
* any other err_t on error
|
||||
*/
|
||||
err_t eth_arch_enetif_init(struct netif *netif)
|
||||
{
|
||||
err_t err;
|
||||
|
||||
LWIP_ASSERT("netif != NULL", (netif != NULL));
|
||||
|
||||
lpc_enetdata.netif = netif;
|
||||
|
||||
/* set MAC hardware address */
|
||||
#if (MBED_MAC_ADDRESS_SUM != MBED_MAC_ADDR_INTERFACE)
|
||||
netif->hwaddr[0] = MBED_MAC_ADDR_0;
|
||||
netif->hwaddr[1] = MBED_MAC_ADDR_1;
|
||||
netif->hwaddr[2] = MBED_MAC_ADDR_2;
|
||||
netif->hwaddr[3] = MBED_MAC_ADDR_3;
|
||||
netif->hwaddr[4] = MBED_MAC_ADDR_4;
|
||||
netif->hwaddr[5] = MBED_MAC_ADDR_5;
|
||||
#else
|
||||
mbed_mac_address((char *)netif->hwaddr);
|
||||
#endif
|
||||
|
||||
netif->hwaddr_len = ETH_HWADDR_LEN;
|
||||
|
||||
/* maximum transfer unit */
|
||||
netif->mtu = 1500;
|
||||
|
||||
/* device capabilities */
|
||||
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET;
|
||||
#ifdef LWIP_IGMP
|
||||
netif->flags |= NETIF_FLAG_IGMP;
|
||||
#endif
|
||||
#if LWIP_IPV6_MLD
|
||||
netif->flags |= NETIF_FLAG_MLD6;
|
||||
#endif
|
||||
|
||||
/* Initialize the hardware */
|
||||
netif->state = &lpc_enetdata;
|
||||
err = low_level_init(netif);
|
||||
if (err != ERR_OK)
|
||||
return err;
|
||||
|
||||
#if LWIP_NETIF_HOSTNAME
|
||||
/* Initialize interface hostname */
|
||||
netif->hostname = "lwiplpc546xx";
|
||||
#endif /* LWIP_NETIF_HOSTNAME */
|
||||
|
||||
netif->name[0] = 'e';
|
||||
netif->name[1] = 'n';
|
||||
|
||||
#if LWIP_IPV4
|
||||
netif->output = lpc546xx_etharp_output_ipv4;
|
||||
#endif
|
||||
#if LWIP_IPV6
|
||||
netif->output_ip6 = lpc546xx_etharp_output_ipv6;
|
||||
#endif
|
||||
|
||||
netif->linkoutput = lpc546xx_low_level_output;
|
||||
|
||||
/* CMSIS-RTOS, start tasks */
|
||||
#if NO_SYS == 0
|
||||
memset(&lpc_enetdata.xTXDCountSem.data, 0, sizeof(lpc_enetdata.xTXDCountSem.data));
|
||||
lpc_enetdata.xTXDCountSem.attr.cb_mem = &lpc_enetdata.xTXDCountSem.data;
|
||||
lpc_enetdata.xTXDCountSem.attr.cb_size = sizeof(lpc_enetdata.xTXDCountSem.data);
|
||||
lpc_enetdata.xTXDCountSem.id = osSemaphoreNew(ENET_TX_RING_LEN, ENET_TX_RING_LEN, &lpc_enetdata.xTXDCountSem.attr);
|
||||
|
||||
LWIP_ASSERT("xTXDCountSem creation error", (lpc_enetdata.xTXDCountSem.id != NULL));
|
||||
|
||||
err = sys_mutex_new(&lpc_enetdata.TXLockMutex);
|
||||
LWIP_ASSERT("TXLockMutex creation error", (err == ERR_OK));
|
||||
|
||||
/* Allow the PHY task to detect the initial link state and set up the proper flags */
|
||||
tcpip_callback_with_block(lpc546xx_phy_task, netif, 1);
|
||||
osDelay(10);
|
||||
|
||||
/* Worker thread */
|
||||
lpc_enetdata.thread = sys_thread_new("lpc546xx_emac_thread", lpc546xx_emac_thread, netif->state, THREAD_STACKSIZE, THREAD_PRIORITY)->id;
|
||||
#endif
|
||||
|
||||
/* Active TX/RX. */
|
||||
ENET_StartRxTx(ENET, 1, 1);
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
void eth_arch_enable_interrupts(void) {
|
||||
|
||||
}
|
||||
|
||||
void eth_arch_disable_interrupts(void) {
|
||||
|
||||
}
|
||||
|
||||
#endif /* LWIP_ARP || LWIP_ETHERNET */
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#ifndef LPC546XX_EMAC_CONFIG_H__
|
||||
#define LPC546XX_EMAC_CONFIG_H__
|
||||
|
||||
#include "fsl_enet.h"
|
||||
|
||||
#define ENET_RX_RING_LEN (16)
|
||||
#define ENET_TX_RING_LEN (8)
|
||||
|
||||
#define ENET_ETH_MAX_FLEN (ENET_FRAME_MAX_FRAMELEN)
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int phy_link_status(void);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // #define LPC546XX_EMAC_CONFIG_H__
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
/* Copyright (C) 2012 mbed.org, MIT License
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef LWIPOPTS_CONF_H
|
||||
#define LWIPOPTS_CONF_H
|
||||
|
||||
#include "lpc546xx_emac_config.h"
|
||||
|
||||
#define LWIP_TRANSPORT_ETHERNET 1
|
||||
#define ETH_PAD_SIZE 0
|
||||
|
||||
#define MEM_SIZE (ENET_RX_RING_LEN * (ENET_ETH_MAX_FLEN + ENET_BUFF_ALIGNMENT) + ENET_TX_RING_LEN * ENET_ETH_MAX_FLEN)
|
||||
|
||||
#endif
|
|
@ -0,0 +1,234 @@
|
|||
/**********************************************************************
|
||||
*
|
||||
* Filename: crc.c
|
||||
*
|
||||
* Description: Slow and fast implementations of the CRC standards.
|
||||
*
|
||||
* Notes: The parameters for each supported CRC standard are
|
||||
* defined in the header file crc.h. The implementations
|
||||
* here should stand up to further additions to that list.
|
||||
*
|
||||
*
|
||||
* Copyright (c) 2000 by Michael Barr. This software is placed into
|
||||
* the public domain and may be used for any purpose. However, this
|
||||
* notice must not be changed or removed and no warranty is either
|
||||
* expressed or implied by its publication or distribution.
|
||||
**********************************************************************/
|
||||
|
||||
#include "crc.h"
|
||||
|
||||
|
||||
/*
|
||||
* Derive parameters from the standard-specific parameters in crc.h.
|
||||
*/
|
||||
#define WIDTH (8 * sizeof(crc))
|
||||
#define TOPBIT (1 << (WIDTH - 1))
|
||||
|
||||
#if (REFLECT_DATA == TRUE)
|
||||
#undef REFLECT_DATA
|
||||
#define REFLECT_DATA(X) ((unsigned char) reflect((X), 8))
|
||||
#else
|
||||
#undef REFLECT_DATA
|
||||
#define REFLECT_DATA(X) (X)
|
||||
#endif
|
||||
|
||||
#if (REFLECT_REMAINDER == TRUE)
|
||||
#undef REFLECT_REMAINDER
|
||||
#define REFLECT_REMAINDER(X) ((crc) reflect((X), WIDTH))
|
||||
#else
|
||||
#undef REFLECT_REMAINDER
|
||||
#define REFLECT_REMAINDER(X) (X)
|
||||
#endif
|
||||
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* Function: reflect()
|
||||
*
|
||||
* Description: Reorder the bits of a binary sequence, by reflecting
|
||||
* them about the middle position.
|
||||
*
|
||||
* Notes: No checking is done that nBits <= 32.
|
||||
*
|
||||
* Returns: The reflection of the original data.
|
||||
*
|
||||
*********************************************************************/
|
||||
static unsigned long
|
||||
reflect(unsigned long data, unsigned char nBits)
|
||||
{
|
||||
unsigned long reflection = 0x00000000;
|
||||
unsigned char bit;
|
||||
|
||||
/*
|
||||
* Reflect the data about the center bit.
|
||||
*/
|
||||
for (bit = 0; bit < nBits; ++bit)
|
||||
{
|
||||
/*
|
||||
* If the LSB bit is set, set the reflection of it.
|
||||
*/
|
||||
if (data & 0x01)
|
||||
{
|
||||
reflection |= (1 << ((nBits - 1) - bit));
|
||||
}
|
||||
|
||||
data = (data >> 1);
|
||||
}
|
||||
|
||||
return (reflection);
|
||||
|
||||
} /* reflect() */
|
||||
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* Function: crcSlow()
|
||||
*
|
||||
* Description: Compute the CRC of a given message.
|
||||
*
|
||||
* Notes:
|
||||
*
|
||||
* Returns: The CRC of the message.
|
||||
*
|
||||
*********************************************************************/
|
||||
crc
|
||||
crcSlow(unsigned char const message[], int nBytes)
|
||||
{
|
||||
crc remainder = INITIAL_REMAINDER;
|
||||
int byte;
|
||||
unsigned char bit;
|
||||
|
||||
|
||||
/*
|
||||
* Perform modulo-2 division, a byte at a time.
|
||||
*/
|
||||
for (byte = 0; byte < nBytes; ++byte)
|
||||
{
|
||||
/*
|
||||
* Bring the next byte into the remainder.
|
||||
*/
|
||||
remainder ^= (REFLECT_DATA(message[byte]) << (WIDTH - 8));
|
||||
|
||||
/*
|
||||
* Perform modulo-2 division, a bit at a time.
|
||||
*/
|
||||
for (bit = 8; bit > 0; --bit)
|
||||
{
|
||||
/*
|
||||
* Try to divide the current data bit.
|
||||
*/
|
||||
if (remainder & TOPBIT)
|
||||
{
|
||||
remainder = (remainder << 1) ^ POLYNOMIAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
remainder = (remainder << 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The final remainder is the CRC result.
|
||||
*/
|
||||
return (REFLECT_REMAINDER(remainder) ^ FINAL_XOR_VALUE);
|
||||
|
||||
} /* crcSlow() */
|
||||
|
||||
|
||||
crc crcTable[256];
|
||||
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* Function: crcInit()
|
||||
*
|
||||
* Description: Populate the partial CRC lookup table.
|
||||
*
|
||||
* Notes: This function must be rerun any time the CRC standard
|
||||
* is changed. If desired, it can be run "offline" and
|
||||
* the table results stored in an embedded system's ROM.
|
||||
*
|
||||
* Returns: None defined.
|
||||
*
|
||||
*********************************************************************/
|
||||
void
|
||||
crcInit(void)
|
||||
{
|
||||
crc remainder;
|
||||
int dividend;
|
||||
unsigned char bit;
|
||||
|
||||
|
||||
/*
|
||||
* Compute the remainder of each possible dividend.
|
||||
*/
|
||||
for (dividend = 0; dividend < 256; ++dividend)
|
||||
{
|
||||
/*
|
||||
* Start with the dividend followed by zeros.
|
||||
*/
|
||||
remainder = dividend << (WIDTH - 8);
|
||||
|
||||
/*
|
||||
* Perform modulo-2 division, a bit at a time.
|
||||
*/
|
||||
for (bit = 8; bit > 0; --bit)
|
||||
{
|
||||
/*
|
||||
* Try to divide the current data bit.
|
||||
*/
|
||||
if (remainder & TOPBIT)
|
||||
{
|
||||
remainder = (remainder << 1) ^ POLYNOMIAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
remainder = (remainder << 1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Store the result into the table.
|
||||
*/
|
||||
crcTable[dividend] = remainder;
|
||||
}
|
||||
|
||||
} /* crcInit() */
|
||||
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* Function: crcFast()
|
||||
*
|
||||
* Description: Compute the CRC of a given message.
|
||||
*
|
||||
* Notes: crcInit() must be called first.
|
||||
*
|
||||
* Returns: The CRC of the message.
|
||||
*
|
||||
*********************************************************************/
|
||||
crc
|
||||
crcFast(unsigned char const message[], int nBytes)
|
||||
{
|
||||
crc remainder = INITIAL_REMAINDER;
|
||||
unsigned char data;
|
||||
int byte;
|
||||
|
||||
|
||||
/*
|
||||
* Divide the message by the polynomial, a byte at a time.
|
||||
*/
|
||||
for (byte = 0; byte < nBytes; ++byte)
|
||||
{
|
||||
data = REFLECT_DATA(message[byte]) ^ (remainder >> (WIDTH - 8));
|
||||
remainder = crcTable[data] ^ (remainder << 8);
|
||||
}
|
||||
|
||||
/*
|
||||
* The final remainder is the CRC.
|
||||
*/
|
||||
return (REFLECT_REMAINDER(remainder) ^ FINAL_XOR_VALUE);
|
||||
|
||||
} /* crcFast() */
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
/**********************************************************************
|
||||
*
|
||||
* Filename: crc.h
|
||||
*
|
||||
* Description: A header file describing the various CRC standards.
|
||||
*
|
||||
* Notes:
|
||||
*
|
||||
*
|
||||
* Copyright (c) 2000 by Michael Barr. This software is placed into
|
||||
* the public domain and may be used for any purpose. However, this
|
||||
* notice must not be changed or removed and no warranty is either
|
||||
* expressed or implied by its publication or distribution.
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef _crc_h
|
||||
#define _crc_h
|
||||
|
||||
|
||||
#define FALSE 0
|
||||
#define TRUE !FALSE
|
||||
|
||||
/*
|
||||
* Select the CRC standard from the list that follows.
|
||||
*/
|
||||
#define CRC16
|
||||
|
||||
|
||||
#if defined(CRC_CCITT)
|
||||
|
||||
typedef unsigned short crc;
|
||||
|
||||
#define CRC_NAME "CRC-CCITT"
|
||||
#define POLYNOMIAL 0x1021
|
||||
#define INITIAL_REMAINDER 0xFFFF
|
||||
#define FINAL_XOR_VALUE 0x0000
|
||||
#define REFLECT_DATA FALSE
|
||||
#define REFLECT_REMAINDER FALSE
|
||||
#define CHECK_VALUE 0x29B1
|
||||
|
||||
#elif defined(CRC16)
|
||||
|
||||
typedef unsigned short crc;
|
||||
|
||||
#define CRC_NAME "CRC-16"
|
||||
#define POLYNOMIAL 0x8005
|
||||
#define INITIAL_REMAINDER 0x0000
|
||||
#define FINAL_XOR_VALUE 0x0000
|
||||
#define REFLECT_DATA TRUE
|
||||
#define REFLECT_REMAINDER TRUE
|
||||
#define CHECK_VALUE 0xBB3D
|
||||
|
||||
#elif defined(CRC32)
|
||||
|
||||
typedef unsigned long crc;
|
||||
|
||||
#define CRC_NAME "CRC-32"
|
||||
#define POLYNOMIAL 0x04C11DB7
|
||||
#define INITIAL_REMAINDER 0xFFFFFFFF
|
||||
#define FINAL_XOR_VALUE 0xFFFFFFFF
|
||||
#define REFLECT_DATA TRUE
|
||||
#define REFLECT_REMAINDER TRUE
|
||||
#define CHECK_VALUE 0xCBF43926
|
||||
|
||||
#else
|
||||
|
||||
#error "One of CRC_CCITT, CRC16, or CRC32 must be #define'd."
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
void crcInit(void);
|
||||
crc crcSlow(unsigned char const message[], int nBytes);
|
||||
crc crcFast(unsigned char const message[], int nBytes);
|
||||
|
||||
|
||||
#endif /* _crc_h */
|
|
@ -0,0 +1,278 @@
|
|||
/*
|
||||
* Copyright (c) 2016, Freescale Semiconductor, Inc.
|
||||
* Copyright 2016-2017 NXP
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* o Redistributions of source code must retain the above copyright notice, this list
|
||||
* of conditions and the following disclaimer.
|
||||
*
|
||||
* o 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.
|
||||
*
|
||||
* o Neither the name of the copyright holder 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 HOLDER 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 "fsl_phy.h"
|
||||
|
||||
/*******************************************************************************
|
||||
* Definitions
|
||||
******************************************************************************/
|
||||
|
||||
/*! @brief Defines the timeout macro. */
|
||||
#define PHY_TIMEOUT_COUNT 0xFFFFFFU
|
||||
|
||||
/*******************************************************************************
|
||||
* Prototypes
|
||||
******************************************************************************/
|
||||
|
||||
/*!
|
||||
* @brief Get the ENET instance from peripheral base address.
|
||||
*
|
||||
* @param base ENET peripheral base address.
|
||||
* @return ENET instance.
|
||||
*/
|
||||
extern uint32_t ENET_GetInstance(ENET_Type *base);
|
||||
|
||||
/*******************************************************************************
|
||||
* Variables
|
||||
******************************************************************************/
|
||||
|
||||
#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
|
||||
/*! @brief Pointers to enet clocks for each instance. */
|
||||
#if defined(FSL_FEATURE_SOC_ENET_COUNT) && (FSL_FEATURE_SOC_ENET_COUNT > 0)
|
||||
extern clock_ip_name_t s_enetClock[FSL_FEATURE_SOC_ENET_COUNT];
|
||||
#elif defined(FSL_FEATURE_SOC_LPC_ENET_COUNT) && (FSL_FEATURE_SOC_LPC_ENET_COUNT > 0)
|
||||
extern clock_ip_name_t s_enetClock[FSL_FEATURE_SOC_LPC_ENET_COUNT];
|
||||
#endif
|
||||
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
|
||||
|
||||
/*******************************************************************************
|
||||
* Code
|
||||
******************************************************************************/
|
||||
|
||||
status_t PHY_Init(ENET_Type *base, uint32_t phyAddr, uint32_t srcClock_Hz)
|
||||
{
|
||||
uint32_t reg;
|
||||
uint32_t idReg = 0;
|
||||
uint32_t delay = PHY_TIMEOUT_COUNT;
|
||||
uint32_t instance = ENET_GetInstance(base);
|
||||
bool status = false;
|
||||
|
||||
#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
|
||||
/* Set SMI first. */
|
||||
CLOCK_EnableClock(s_enetClock[instance]);
|
||||
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
|
||||
|
||||
#if defined(FSL_FEATURE_SOC_ENET_COUNT) && (FSL_FEATURE_SOC_ENET_COUNT > 0)
|
||||
ENET_SetSMI(base, srcClock_Hz, false);
|
||||
#elif defined(FSL_FEATURE_SOC_LPC_ENET_COUNT) && (FSL_FEATURE_SOC_LPC_ENET_COUNT > 0)
|
||||
ENET_SetSMI(base);
|
||||
#endif
|
||||
/* Initialization after PHY stars to work. */
|
||||
while ((idReg != PHY_CONTROL_ID1) && (delay != 0))
|
||||
{
|
||||
PHY_Read(base, phyAddr, PHY_ID1_REG, &idReg);
|
||||
delay --;
|
||||
}
|
||||
|
||||
if (!delay)
|
||||
{
|
||||
return kStatus_Fail;
|
||||
}
|
||||
delay = PHY_TIMEOUT_COUNT;
|
||||
|
||||
/* Reset PHY and wait until completion. */
|
||||
PHY_Write(base, phyAddr, PHY_BASICCONTROL_REG, PHY_BCTL_RESET_MASK);
|
||||
do
|
||||
{
|
||||
PHY_Read(base, phyAddr, PHY_BASICCONTROL_REG, ®);
|
||||
} while (delay-- && reg & PHY_BCTL_RESET_MASK);
|
||||
|
||||
if (!delay)
|
||||
{
|
||||
return kStatus_Fail;
|
||||
}
|
||||
|
||||
/* Set the ability. */
|
||||
PHY_Write(base, phyAddr, PHY_AUTONEG_ADVERTISE_REG, (PHY_ALL_CAPABLE_MASK | 0x1U));
|
||||
|
||||
/* Start Auto negotiation and wait until auto negotiation completion */
|
||||
PHY_Write(base, phyAddr, PHY_BASICCONTROL_REG, (PHY_BCTL_AUTONEG_MASK | PHY_BCTL_RESTART_AUTONEG_MASK));
|
||||
delay = PHY_TIMEOUT_COUNT;
|
||||
do
|
||||
{
|
||||
PHY_Read(base, phyAddr, PHY_SEPCIAL_CONTROL_REG, ®);
|
||||
delay --;
|
||||
} while (delay && ((reg & PHY_SPECIALCTL_AUTONEGDONE_MASK) == 0));
|
||||
|
||||
if (!delay)
|
||||
{
|
||||
return kStatus_Fail;
|
||||
}
|
||||
|
||||
/* Waiting a moment for phy stable. */
|
||||
for (delay = 0; delay < PHY_TIMEOUT_COUNT; delay++)
|
||||
{
|
||||
__ASM("nop");
|
||||
PHY_GetLinkStatus(base, phyAddr, &status);
|
||||
if (status)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return kStatus_Success;
|
||||
}
|
||||
|
||||
status_t PHY_Write(ENET_Type *base, uint32_t phyAddr, uint32_t phyReg, uint32_t data)
|
||||
{
|
||||
#if defined(FSL_FEATURE_SOC_ENET_COUNT) && (FSL_FEATURE_SOC_ENET_COUNT > 0)
|
||||
uint32_t counter;
|
||||
|
||||
/* Clear the SMI interrupt event. */
|
||||
ENET_ClearInterruptStatus(base, ENET_EIR_MII_MASK);
|
||||
|
||||
/* Starts a SMI write command. */
|
||||
ENET_StartSMIWrite(base, phyAddr, phyReg, kENET_MiiWriteValidFrame, data);
|
||||
|
||||
/* Wait for SMI complete. */
|
||||
for (counter = PHY_TIMEOUT_COUNT; counter > 0; counter--)
|
||||
{
|
||||
if (ENET_GetInterruptStatus(base) & ENET_EIR_MII_MASK)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for timeout. */
|
||||
if (!counter)
|
||||
{
|
||||
return kStatus_PHY_SMIVisitTimeout;
|
||||
}
|
||||
|
||||
/* Clear MII interrupt event. */
|
||||
ENET_ClearInterruptStatus(base, ENET_EIR_MII_MASK);
|
||||
|
||||
#elif defined(FSL_FEATURE_SOC_LPC_ENET_COUNT) && (FSL_FEATURE_SOC_LPC_ENET_COUNT > 0)
|
||||
ENET_StartSMIWrite(base, phyAddr, phyReg, data);
|
||||
while (ENET_IsSMIBusy(base))
|
||||
;
|
||||
#endif
|
||||
return kStatus_Success;
|
||||
}
|
||||
|
||||
status_t PHY_Read(ENET_Type *base, uint32_t phyAddr, uint32_t phyReg, uint32_t *dataPtr)
|
||||
{
|
||||
#if defined(FSL_FEATURE_SOC_ENET_COUNT) && (FSL_FEATURE_SOC_ENET_COUNT > 0)
|
||||
assert(dataPtr);
|
||||
|
||||
uint32_t counter;
|
||||
|
||||
/* Clear the MII interrupt event. */
|
||||
ENET_ClearInterruptStatus(base, ENET_EIR_MII_MASK);
|
||||
|
||||
/* Starts a SMI read command operation. */
|
||||
ENET_StartSMIRead(base, phyAddr, phyReg, kENET_MiiReadValidFrame);
|
||||
|
||||
/* Wait for MII complete. */
|
||||
for (counter = PHY_TIMEOUT_COUNT; counter > 0; counter--)
|
||||
{
|
||||
if (ENET_GetInterruptStatus(base) & ENET_EIR_MII_MASK)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for timeout. */
|
||||
if (!counter)
|
||||
{
|
||||
return kStatus_PHY_SMIVisitTimeout;
|
||||
}
|
||||
|
||||
/* Get data from MII register. */
|
||||
*dataPtr = ENET_ReadSMIData(base);
|
||||
|
||||
/* Clear MII interrupt event. */
|
||||
ENET_ClearInterruptStatus(base, ENET_EIR_MII_MASK);
|
||||
#elif defined(FSL_FEATURE_SOC_LPC_ENET_COUNT) && (FSL_FEATURE_SOC_LPC_ENET_COUNT > 0)
|
||||
ENET_StartSMIRead(base, phyAddr, phyReg);
|
||||
while (ENET_IsSMIBusy(base))
|
||||
;
|
||||
*dataPtr = ENET_ReadSMIData(base);
|
||||
#endif
|
||||
return kStatus_Success;
|
||||
}
|
||||
|
||||
status_t PHY_GetLinkStatus(ENET_Type *base, uint32_t phyAddr, bool *status)
|
||||
{
|
||||
uint32_t reg;
|
||||
status_t result = kStatus_Success;
|
||||
|
||||
/* Read the basic status register. */
|
||||
result = PHY_Read(base, phyAddr, PHY_BASICSTATUS_REG, ®);
|
||||
if (result == kStatus_Success)
|
||||
{
|
||||
if (reg & PHY_BSTATUS_LINKSTATUS_MASK)
|
||||
{
|
||||
/* link up. */
|
||||
*status = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
*status = false;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
status_t PHY_GetLinkSpeedDuplex(ENET_Type *base, uint32_t phyAddr, phy_speed_t *speed, phy_duplex_t *duplex)
|
||||
{
|
||||
assert(duplex);
|
||||
assert(speed);
|
||||
|
||||
uint32_t reg;
|
||||
status_t result = kStatus_Success;
|
||||
|
||||
/* Read the control two register. */
|
||||
result = PHY_Read(base, phyAddr, PHY_SEPCIAL_CONTROL_REG, ®);
|
||||
if (result == kStatus_Success)
|
||||
{
|
||||
if (reg & PHY_SPECIALCTL_DUPLEX_MASK)
|
||||
{
|
||||
/* Full duplex. */
|
||||
*duplex = kPHY_FullDuplex;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Half duplex. */
|
||||
*duplex = kPHY_HalfDuplex;
|
||||
}
|
||||
|
||||
if (reg & PHY_SPECIALCTL_100SPEED_MASK)
|
||||
{
|
||||
/* 100M speed. */
|
||||
*speed = kPHY_Speed100M;
|
||||
}
|
||||
else
|
||||
{ /* 10M speed. */
|
||||
*speed = kPHY_Speed10M;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
* Copyright (c) 2016, Freescale Semiconductor, Inc.
|
||||
* Copyright 2016-2017 NXP
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* o Redistributions of source code must retain the above copyright notice, this list
|
||||
* of conditions and the following disclaimer.
|
||||
*
|
||||
* o 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.
|
||||
*
|
||||
* o Neither the name of the copyright holder 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 HOLDER 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.
|
||||
*/
|
||||
#ifndef _FSL_PHY_H_
|
||||
#define _FSL_PHY_H_
|
||||
|
||||
#include "fsl_enet.h"
|
||||
|
||||
/*!
|
||||
* @addtogroup phy_driver
|
||||
* @{
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
* Definitions
|
||||
******************************************************************************/
|
||||
|
||||
/*! @brief PHY driver version */
|
||||
#define FSL_PHY_DRIVER_VERSION (MAKE_VERSION(2, 0, 0)) /*!< Version 2.0.0. */
|
||||
|
||||
/*! @brief Defines the PHY registers. */
|
||||
#define PHY_BASICCONTROL_REG 0x00U /*!< The PHY basic control register. */
|
||||
#define PHY_BASICSTATUS_REG 0x01U /*!< The PHY basic status register. */
|
||||
#define PHY_ID1_REG 0x02U /*!< The PHY ID one register. */
|
||||
#define PHY_ID2_REG 0x03U /*!< The PHY ID two register. */
|
||||
#define PHY_AUTONEG_ADVERTISE_REG 0x04U /*!< The PHY auto-negotiate advertise register. */
|
||||
#define PHY_SEPCIAL_CONTROL_REG 0x1FU /*!< The PHY control two register. */
|
||||
|
||||
#define PHY_CONTROL_ID1 0x07U /*!< The PHY ID1*/
|
||||
|
||||
/*! @brief Defines the mask flag in basic control register. */
|
||||
#define PHY_BCTL_DUPLEX_MASK 0x0100U /*!< The PHY duplex bit mask. */
|
||||
#define PHY_BCTL_RESTART_AUTONEG_MASK 0x0200U /*!< The PHY restart auto negotiation mask. */
|
||||
#define PHY_BCTL_AUTONEG_MASK 0x1000U /*!< The PHY auto negotiation bit mask. */
|
||||
#define PHY_BCTL_SPEED_MASK 0x2000U /*!< The PHY speed bit mask. */
|
||||
#define PHY_BCTL_LOOP_MASK 0x4000U /*!< The PHY loop bit mask. */
|
||||
#define PHY_BCTL_RESET_MASK 0x8000U /*!< The PHY reset bit mask. */
|
||||
|
||||
/*!@brief Defines the mask flag of operation mode in special control register*/
|
||||
#define PHY_SPECIALCTL_AUTONEGDONE_MASK 0x1000U /*!< The PHY auto-negotiation complete mask. */
|
||||
#define PHY_SPECIALCTL_DUPLEX_MASK 0x0010U /*!< The PHY duplex mask. */
|
||||
#define PHY_SPECIALCTL_100SPEED_MASK 0x0008U /*!< The PHY speed mask. */
|
||||
#define PHY_SPECIALCTL_10SPEED_MASK 0x0004U /*!< The PHY speed mask. */
|
||||
#define PHY_SPECIALCTL_SPEEDUPLX_MASK 0x001cU /*!< The PHY speed and duplex mask. */
|
||||
|
||||
/*! @brief Defines the mask flag in basic status register. */
|
||||
#define PHY_BSTATUS_LINKSTATUS_MASK 0x0004U /*!< The PHY link status mask. */
|
||||
|
||||
/*! @brief Defines the mask flag in PHY auto-negotiation advertise register. */
|
||||
#define PHY_ALL_CAPABLE_MASK 0x1e0U
|
||||
|
||||
/*! @brief Defines the PHY status. */
|
||||
enum _phy_status
|
||||
{
|
||||
kStatus_PHY_SMIVisitTimeout = MAKE_STATUS(kStatusGroup_PHY, 0), /*!< ENET PHY SMI visit timeout. */
|
||||
};
|
||||
|
||||
/*! @brief Defines the PHY link speed. This is align with the speed for ENET MAC. */
|
||||
typedef enum _phy_speed {
|
||||
kPHY_Speed10M = 0U, /*!< ENET PHY 10M speed. */
|
||||
kPHY_Speed100M /*!< ENET PHY 100M speed. */
|
||||
} phy_speed_t;
|
||||
|
||||
/*! @brief Defines the PHY link duplex. */
|
||||
typedef enum _phy_duplex {
|
||||
kPHY_HalfDuplex = 0U, /*!< ENET PHY half duplex. */
|
||||
kPHY_FullDuplex /*!< ENET PHY full duplex. */
|
||||
} phy_duplex_t;
|
||||
|
||||
/*******************************************************************************
|
||||
* API
|
||||
******************************************************************************/
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* @name PHY Driver
|
||||
* @{
|
||||
*/
|
||||
|
||||
/*!
|
||||
* @brief Initializes PHY.
|
||||
*
|
||||
* This function initialize the SMI interface and initialize PHY.
|
||||
* The SMI is the MII management interface between PHY and MAC, which should be
|
||||
* firstly initialized before any other operation for PHY.
|
||||
*
|
||||
* @param base ENET peripheral base address.
|
||||
* @param phyAddr The PHY address.
|
||||
* @param srcClock_Hz The module clock frequency - system clock for MII management interface - SMI.
|
||||
* @retval kStatus_Success PHY initialize success
|
||||
* @retval kStatus_Fail PHY initialize fail
|
||||
*/
|
||||
status_t PHY_Init(ENET_Type *base, uint32_t phyAddr, uint32_t srcClock_Hz);
|
||||
|
||||
/*!
|
||||
* @brief PHY Write function. This function write data over the SMI to
|
||||
* the specified PHY register. This function is called by all PHY interfaces.
|
||||
*
|
||||
* @param base ENET peripheral base address.
|
||||
* @param phyAddr The PHY address.
|
||||
* @param phyReg The PHY register.
|
||||
* @param data The data written to the PHY register.
|
||||
* @retval kStatus_Success PHY write success
|
||||
* @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out
|
||||
*/
|
||||
status_t PHY_Write(ENET_Type *base, uint32_t phyAddr, uint32_t phyReg, uint32_t data);
|
||||
|
||||
/*!
|
||||
* @brief PHY Read function. This interface read data over the SMI from the
|
||||
* specified PHY register. This function is called by all PHY interfaces.
|
||||
*
|
||||
* @param base ENET peripheral base address.
|
||||
* @param phyAddr The PHY address.
|
||||
* @param phyReg The PHY register.
|
||||
* @param dataPtr The address to store the data read from the PHY register.
|
||||
* @retval kStatus_Success PHY read success
|
||||
* @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out
|
||||
*/
|
||||
status_t PHY_Read(ENET_Type *base, uint32_t phyAddr, uint32_t phyReg, uint32_t *dataPtr);
|
||||
|
||||
/*!
|
||||
* @brief Gets the PHY link status.
|
||||
*
|
||||
* @param base ENET peripheral base address.
|
||||
* @param phyAddr The PHY address.
|
||||
* @param status The link up or down status of the PHY.
|
||||
* - true the link is up.
|
||||
* - false the link is down.
|
||||
* @retval kStatus_Success PHY get link status success
|
||||
* @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out
|
||||
*/
|
||||
status_t PHY_GetLinkStatus(ENET_Type *base, uint32_t phyAddr, bool *status);
|
||||
|
||||
/*!
|
||||
* @brief Gets the PHY link speed and duplex.
|
||||
*
|
||||
* @param base ENET peripheral base address.
|
||||
* @param phyAddr The PHY address.
|
||||
* @param speed The address of PHY link speed.
|
||||
* @param duplex The link duplex of PHY.
|
||||
* @retval kStatus_Success PHY get link speed and duplex success
|
||||
* @retval kStatus_PHY_SMIVisitTimeout PHY SMI visit time out
|
||||
*/
|
||||
status_t PHY_GetLinkSpeedDuplex(ENET_Type *base, uint32_t phyAddr, phy_speed_t *speed, phy_duplex_t *duplex);
|
||||
|
||||
/* @} */
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
/*! @}*/
|
||||
|
||||
#endif /* _FSL_PHY_H_ */
|
|
@ -17,6 +17,10 @@
|
|||
#include "clock_config.h"
|
||||
#include "fsl_emc.h"
|
||||
#include "fsl_power.h"
|
||||
#include "fsl_flashiap.h"
|
||||
|
||||
#define CRC16
|
||||
#include "crc.h"
|
||||
|
||||
/*******************************************************************************
|
||||
* Definitions
|
||||
|
@ -37,6 +41,18 @@
|
|||
#define SDRAM_MODEREG_VALUE (0x23u)
|
||||
#define SDRAM_DEV_MEMORYMAP (0x09u) /* 128Mbits (8M*16, 4banks, 12 rows, 9 columns)*/
|
||||
|
||||
uint32_t FLASHIAP_ReadUid(uint32_t *addr)
|
||||
{
|
||||
uint32_t command[5], result[5];
|
||||
|
||||
command[0] = kIapCmd_FLASHIAP_ReadUid;
|
||||
iap_entry(command, result);
|
||||
|
||||
memcpy(addr, &result[1], (sizeof(uint32_t) * 4));
|
||||
|
||||
return result[0];
|
||||
}
|
||||
|
||||
// called before main
|
||||
void mbed_sdk_init()
|
||||
{
|
||||
|
@ -59,6 +75,38 @@ void rtc_setup_oscillator(void)
|
|||
SYSCON->RTCOSCCTRL |= SYSCON_RTCOSCCTRL_EN_MASK;
|
||||
}
|
||||
|
||||
// Provide ethernet devices with a semi-unique MAC address from the UUID
|
||||
void mbed_mac_address(char *mac)
|
||||
{
|
||||
uint16_t MAC[3]; // 3 16 bits words for the MAC
|
||||
uint32_t UID[4];
|
||||
|
||||
// get UID via ISP commands
|
||||
FLASHIAP_ReadUid(UID);
|
||||
|
||||
// generate three CRC16's using different slices of the UUID
|
||||
MAC[0] = crcSlow((const uint8_t *)UID, 8); // most significant half-word
|
||||
MAC[1] = crcSlow((const uint8_t *)UID, 12);
|
||||
MAC[2] = crcSlow((const uint8_t *)UID, 16); // least significant half word
|
||||
|
||||
// The network stack expects an array of 6 bytes
|
||||
// so we copy, and shift and copy from the half-word array to the byte array
|
||||
mac[0] = MAC[0] >> 8;
|
||||
mac[1] = MAC[0];
|
||||
mac[2] = MAC[1] >> 8;
|
||||
mac[3] = MAC[1];
|
||||
mac[4] = MAC[2] >> 8;
|
||||
mac[5] = MAC[2];
|
||||
|
||||
// We want to force bits [1:0] of the most significant byte [0]
|
||||
// to be "10"
|
||||
// http://en.wikipedia.org/wiki/MAC_address
|
||||
|
||||
mac[0] |= 0x02; // force bit 1 to a "1" = "Locally Administered"
|
||||
mac[0] &= 0xFE; // force bit 0 to a "0" = Unicast
|
||||
|
||||
}
|
||||
|
||||
void ADC_ClockPower_Configuration(void)
|
||||
{
|
||||
/* SYSCON power. */
|
||||
|
|
|
@ -194,6 +194,123 @@ typedef int32_t status_t;
|
|||
#define COUNT_TO_MSEC(count, clockFreqInHz) (uint64_t)((uint64_t)count * 1000U / clockFreqInHz)
|
||||
/* @} */
|
||||
|
||||
/*! @name Alignment variable definition macros */
|
||||
/* @{ */
|
||||
#if (defined(__ICCARM__))
|
||||
/**
|
||||
* Workaround to disable MISRA C message suppress warnings for IAR compiler.
|
||||
* http://supp.iar.com/Support/?note=24725
|
||||
*/
|
||||
_Pragma("diag_suppress=Pm120")
|
||||
#define SDK_PRAGMA(x) _Pragma(#x)
|
||||
_Pragma("diag_error=Pm120")
|
||||
/*! Macro to define a variable with alignbytes alignment */
|
||||
#define SDK_ALIGN(var, alignbytes) SDK_PRAGMA(data_alignment = alignbytes) var
|
||||
/*! Macro to define a variable with L1 d-cache line size alignment */
|
||||
#if defined(FSL_FEATURE_L1DCACHE_LINESIZE_BYTE)
|
||||
#define SDK_L1DCACHE_ALIGN(var) SDK_PRAGMA(data_alignment = FSL_FEATURE_L1DCACHE_LINESIZE_BYTE) var
|
||||
#endif
|
||||
/*! Macro to define a variable with L2 cache line size alignment */
|
||||
#if defined(FSL_FEATURE_L2CACHE_LINESIZE_BYTE)
|
||||
#define SDK_L2CACHE_ALIGN(var) SDK_PRAGMA(data_alignment = FSL_FEATURE_L2CACHE_LINESIZE_BYTE) var
|
||||
#endif
|
||||
#elif defined(__ARMCC_VERSION)
|
||||
/*! Macro to define a variable with alignbytes alignment */
|
||||
#define SDK_ALIGN(var, alignbytes) __align(alignbytes) var
|
||||
/*! Macro to define a variable with L1 d-cache line size alignment */
|
||||
#if defined(FSL_FEATURE_L1DCACHE_LINESIZE_BYTE)
|
||||
#define SDK_L1DCACHE_ALIGN(var) __align(FSL_FEATURE_L1DCACHE_LINESIZE_BYTE) var
|
||||
#endif
|
||||
/*! Macro to define a variable with L2 cache line size alignment */
|
||||
#if defined(FSL_FEATURE_L2CACHE_LINESIZE_BYTE)
|
||||
#define SDK_L2CACHE_ALIGN(var) __align(FSL_FEATURE_L2CACHE_LINESIZE_BYTE) var
|
||||
#endif
|
||||
#elif defined(__GNUC__)
|
||||
/*! Macro to define a variable with alignbytes alignment */
|
||||
#define SDK_ALIGN(var, alignbytes) var __attribute__((aligned(alignbytes)))
|
||||
/*! Macro to define a variable with L1 d-cache line size alignment */
|
||||
#if defined(FSL_FEATURE_L1DCACHE_LINESIZE_BYTE)
|
||||
#define SDK_L1DCACHE_ALIGN(var) var __attribute__((aligned(FSL_FEATURE_L1DCACHE_LINESIZE_BYTE)))
|
||||
#endif
|
||||
/*! Macro to define a variable with L2 cache line size alignment */
|
||||
#if defined(FSL_FEATURE_L2CACHE_LINESIZE_BYTE)
|
||||
#define SDK_L2CACHE_ALIGN(var) var __attribute__((aligned(FSL_FEATURE_L2CACHE_LINESIZE_BYTE)))
|
||||
#endif
|
||||
#else
|
||||
#error Toolchain not supported
|
||||
#define SDK_ALIGN(var, alignbytes) var
|
||||
#if defined(FSL_FEATURE_L1DCACHE_LINESIZE_BYTE)
|
||||
#define SDK_L1DCACHE_ALIGN(var) var
|
||||
#endif
|
||||
#if defined(FSL_FEATURE_L2CACHE_LINESIZE_BYTE)
|
||||
#define SDK_L2CACHE_ALIGN(var) var
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*! Macro to change a value to a given size aligned value */
|
||||
#define SDK_SIZEALIGN(var, alignbytes) \
|
||||
((unsigned int)((var) + ((alignbytes)-1)) & (unsigned int)(~(unsigned int)((alignbytes)-1)))
|
||||
/* @} */
|
||||
|
||||
/*! @name Non-cacheable region definition macros */
|
||||
/* For initialized non-zero non-cacheable variables, please using "AT_NONCACHEABLE_SECTION_INIT(var) ={xx};" or
|
||||
* "AT_NONCACHEABLE_SECTION_ALIGN_INIT(var) ={xx};" in your projects to define them, for zero-inited non-cacheable variables,
|
||||
* please using "AT_NONCACHEABLE_SECTION(var);" or "AT_NONCACHEABLE_SECTION_ALIGN(var);" to define them, these zero-inited variables
|
||||
* will be initialized to zero in system startup.
|
||||
*/
|
||||
/* @{ */
|
||||
#if (defined(__ICCARM__))
|
||||
#if defined(FSL_FEATURE_L1ICACHE_LINESIZE_BYTE)
|
||||
#define AT_NONCACHEABLE_SECTION(var) var @"NonCacheable"
|
||||
#define AT_NONCACHEABLE_SECTION_ALIGN(var, alignbytes) SDK_PRAGMA(data_alignment = alignbytes) var @"NonCacheable"
|
||||
#define AT_NONCACHEABLE_SECTION_INIT(var) var @"NonCacheable.init"
|
||||
#define AT_NONCACHEABLE_SECTION_ALIGN_INIT(var, alignbytes) SDK_PRAGMA(data_alignment = alignbytes) var @"NonCacheable.init"
|
||||
#else
|
||||
#define AT_NONCACHEABLE_SECTION(var) var
|
||||
#define AT_NONCACHEABLE_SECTION_ALIGN(var, alignbytes) SDK_PRAGMA(data_alignment = alignbytes) var
|
||||
#define AT_NONCACHEABLE_SECTION_INIT(var) var
|
||||
#define AT_NONCACHEABLE_SECTION_ALIGN_INIT(var, alignbytes) SDK_PRAGMA(data_alignment = alignbytes) var
|
||||
#endif
|
||||
#elif(defined(__ARMCC_VERSION))
|
||||
#if defined(FSL_FEATURE_L1ICACHE_LINESIZE_BYTE)
|
||||
#define AT_NONCACHEABLE_SECTION(var) __attribute__((section("NonCacheable"), zero_init)) var
|
||||
#define AT_NONCACHEABLE_SECTION_ALIGN(var, alignbytes) \
|
||||
__attribute__((section("NonCacheable"), zero_init)) __align(alignbytes) var
|
||||
#define AT_NONCACHEABLE_SECTION_INIT(var) __attribute__((section("NonCacheable.init"))) var
|
||||
#define AT_NONCACHEABLE_SECTION_ALIGN_INIT(var, alignbytes) \
|
||||
__attribute__((section("NonCacheable.init"))) __align(alignbytes) var
|
||||
#else
|
||||
#define AT_NONCACHEABLE_SECTION(var) var
|
||||
#define AT_NONCACHEABLE_SECTION_ALIGN(var, alignbytes) __align(alignbytes) var
|
||||
#define AT_NONCACHEABLE_SECTION_INIT(var) var
|
||||
#define AT_NONCACHEABLE_SECTION_ALIGN_INIT(var, alignbytes) __align(alignbytes) var
|
||||
#endif
|
||||
#elif(defined(__GNUC__))
|
||||
/* For GCC, when the non-cacheable section is required, please define "__STARTUP_INITIALIZE_NONCACHEDATA"
|
||||
* in your projects to make sure the non-cacheable section variables will be initialized in system startup.
|
||||
*/
|
||||
#if defined(FSL_FEATURE_L1ICACHE_LINESIZE_BYTE)
|
||||
#define AT_NONCACHEABLE_SECTION_INIT(var) __attribute__((section("NonCacheable.init"))) var
|
||||
#define AT_NONCACHEABLE_SECTION_ALIGN_INIT(var, alignbytes) \
|
||||
__attribute__((section("NonCacheable.init"))) var __attribute__((aligned(alignbytes)))
|
||||
#define AT_NONCACHEABLE_SECTION(var) __attribute__((section("NonCacheable,\"aw\",%nobits @"))) var
|
||||
#define AT_NONCACHEABLE_SECTION_ALIGN(var, alignbytes) \
|
||||
__attribute__((section("NonCacheable,\"aw\",%nobits @"))) var __attribute__((aligned(alignbytes)))
|
||||
#else
|
||||
#define AT_NONCACHEABLE_SECTION(var) var
|
||||
#define AT_NONCACHEABLE_SECTION_ALIGN(var, alignbytes) var __attribute__((aligned(alignbytes)))
|
||||
#define AT_NONCACHEABLE_SECTION_INIT(var) var
|
||||
#define AT_NONCACHEABLE_SECTION_ALIGN_INIT(var, alignbytes) var __attribute__((aligned(alignbytes)))
|
||||
#endif
|
||||
#else
|
||||
#error Toolchain not supported.
|
||||
#define AT_NONCACHEABLE_SECTION(var) var
|
||||
#define AT_NONCACHEABLE_SECTION_ALIGN(var, alignbytes) var
|
||||
#define AT_NONCACHEABLE_SECTION_INIT(var) var
|
||||
#define AT_NONCACHEABLE_SECTION_ALIGN_INIT(var, alignbytes) var
|
||||
#endif
|
||||
/* @} */
|
||||
|
||||
/*******************************************************************************
|
||||
* API
|
||||
******************************************************************************/
|
||||
|
|
|
@ -731,6 +731,7 @@
|
|||
"inherits": ["Target"],
|
||||
"detect_code": ["1056"],
|
||||
"device_has": ["ANALOGIN", "I2C", "I2CSLAVE", "INTERRUPTIN", "PORTIN", "PORTINOUT", "PORTOUT", "RTC", "SERIAL", "SLEEP", "SPI", "SPISLAVE", "STDIO_MESSAGES"],
|
||||
"features": ["LWIP"],
|
||||
"release_versions": ["2", "5"],
|
||||
"device_name" : "LPC54618J512ET180"
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue