| /** | |
| * | |
| * \file | |
| * | |
| * \brief KS8851SNL driver for SAM. | |
| * | |
| * Copyright (c) 2013-2015 Atmel Corporation. All rights reserved. | |
| * | |
| * \asf_license_start | |
| * | |
| * \page License | |
| * | |
| * 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 Atmel may not be used to endorse or promote products derived | |
| * from this software without specific prior written permission. | |
| * | |
| * 4. This software may only be redistributed and used in connection with an | |
| * Atmel microcontroller product. | |
| * | |
| * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED | |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE | |
| * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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. | |
| * | |
| * \asf_license_stop | |
| * | |
| */ | |
| /* | |
| * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> | |
| */ | |
| /* FreeRTOS includes. */ | |
| #include "FreeRTOS.h" | |
| #include "task.h" | |
| #include "spi_master.h" | |
| #include "ksz8851snl.h" | |
| #include "ksz8851snl_reg.h" | |
| #include "delay.h" | |
| #include "pio.h" | |
| #include "pio_handler.h" | |
| #include "pdc.h" | |
| #include "conf_eth.h" | |
| /* Clock polarity. */ | |
| #define SPI_CLK_POLARITY 0 | |
| /* Clock phase. */ | |
| #define SPI_CLK_PHASE 1 | |
| /* SPI PDC register base. */ | |
| Pdc *g_p_spi_pdc = 0; | |
| int lUDPLoggingPrintf( const char *pcFormatString, ... ); | |
| /* Temporary buffer for PDC reception. */ | |
| uint8_t tmpbuf[1536] __attribute__ ((aligned (16))); | |
| union { | |
| uint64_t ul[2]; | |
| uint8_t uc[16]; | |
| } cmdBuf, respBuf; | |
| void dbg_add_line( const char *pcFormat, ... ); | |
| static void spi_clear_ovres( void ); | |
| /** | |
| * \brief Read register content, set bitmask and write back to register. | |
| * | |
| * \param reg the register address to modify. | |
| * \param bits_to_set bitmask to apply. | |
| */ | |
| void ksz8851_reg_setbits(uint16_t reg, uint16_t bits_to_set) | |
| { | |
| uint16_t temp; | |
| temp = ksz8851_reg_read(reg); | |
| temp |= bits_to_set; | |
| ksz8851_reg_write(reg, temp); | |
| } | |
| /** | |
| * \brief Read register content, clear bitmask and write back to register. | |
| * | |
| * \param reg the register address to modify. | |
| * \param bits_to_set bitmask to apply. | |
| */ | |
| void ksz8851_reg_clrbits(uint16_t reg, uint16_t bits_to_clr) | |
| { | |
| uint16_t temp; | |
| temp = ksz8851_reg_read(reg); | |
| temp &= ~(uint32_t) bits_to_clr; | |
| ksz8851_reg_write(reg, temp); | |
| } | |
| /** | |
| * \brief Configure the INTN interrupt. | |
| */ | |
| void configure_intn(void (*p_handler) (uint32_t, uint32_t)) | |
| { | |
| // gpio_configure_pin(KSZ8851SNL_INTN_GPIO, PIO_INPUT); | |
| // pio_set_input(PIOA, PIO_PA11_IDX, PIO_PULLUP); | |
| /* Configure PIO clock. */ | |
| pmc_enable_periph_clk(INTN_ID); | |
| /* Adjust PIO debounce filter parameters, uses 10 Hz filter. */ | |
| pio_set_debounce_filter(INTN_PIO, INTN_PIN_MSK, 10); | |
| /* Initialize PIO interrupt handlers, see PIO definition in board.h. */ | |
| pio_handler_set(INTN_PIO, INTN_ID, INTN_PIN_MSK, | |
| INTN_ATTR, p_handler); | |
| /* Enable NVIC interrupts. */ | |
| NVIC_SetPriority(INTN_IRQn, INT_PRIORITY_PIO); | |
| NVIC_EnableIRQ((IRQn_Type)INTN_ID); | |
| /* Enable PIO interrupts. */ | |
| pio_enable_interrupt(INTN_PIO, INTN_PIN_MSK); | |
| } | |
| /** | |
| * \brief Read a register value. | |
| * | |
| * \param reg the register address to modify. | |
| * | |
| * \return the register value. | |
| */ | |
| uint16_t ksz8851_reg_read(uint16_t reg) | |
| { | |
| pdc_packet_t g_pdc_spi_tx_packet; | |
| pdc_packet_t g_pdc_spi_rx_packet; | |
| uint16_t cmd = 0; | |
| uint16_t res = 0; | |
| int iTryCount = 3; | |
| while( iTryCount-- > 0 ) | |
| { | |
| uint32_t ulStatus; | |
| spi_clear_ovres(); | |
| /* Move register address to cmd bits 9-2, make 32-bit address. */ | |
| cmd = (reg << 2) & REG_ADDR_MASK; | |
| /* Last 2 bits still under "don't care bits" handled with byte enable. */ | |
| /* Select byte enable for command. */ | |
| if (reg & 2) { | |
| /* Odd word address writes bytes 2 and 3 */ | |
| cmd |= (0xc << 10); | |
| } else { | |
| /* Even word address write bytes 0 and 1 */ | |
| cmd |= (0x3 << 10); | |
| } | |
| /* Add command read code. */ | |
| cmd |= CMD_READ; | |
| cmdBuf.uc[0] = cmd >> 8; | |
| cmdBuf.uc[1] = cmd & 0xff; | |
| cmdBuf.uc[2] = CONFIG_SPI_MASTER_DUMMY; | |
| cmdBuf.uc[3] = CONFIG_SPI_MASTER_DUMMY; | |
| /* Prepare PDC transfer. */ | |
| g_pdc_spi_tx_packet.ul_addr = (uint32_t) cmdBuf.uc; | |
| g_pdc_spi_tx_packet.ul_size = 4; | |
| g_pdc_spi_rx_packet.ul_addr = (uint32_t) tmpbuf; | |
| g_pdc_spi_rx_packet.ul_size = 4; | |
| pdc_disable_transfer(g_p_spi_pdc, PERIPH_PTCR_RXTDIS | PERIPH_PTCR_TXTDIS); | |
| pdc_tx_init(g_p_spi_pdc, &g_pdc_spi_tx_packet, NULL); | |
| pdc_rx_init(g_p_spi_pdc, &g_pdc_spi_rx_packet, NULL); | |
| gpio_set_pin_low(KSZ8851SNL_CSN_GPIO); | |
| spi_disable_interrupt( KSZ8851SNL_SPI, ~0ul ); | |
| pdc_enable_transfer(g_p_spi_pdc, PERIPH_PTCR_RXTEN | PERIPH_PTCR_TXTEN); | |
| for( ;; ) | |
| { | |
| ulStatus = spi_read_status( KSZ8851SNL_SPI ); | |
| if( ( ulStatus & ( SPI_SR_OVRES | SPI_SR_ENDRX ) ) != 0 ) | |
| { | |
| break; | |
| } | |
| } | |
| gpio_set_pin_high( KSZ8851SNL_CSN_GPIO ); | |
| if( ( ulStatus & SPI_SR_OVRES ) == 0 ) | |
| { | |
| break; | |
| } | |
| pdc_disable_transfer(g_p_spi_pdc, PERIPH_PTCR_RXTDIS | PERIPH_PTCR_TXTDIS); | |
| lUDPLoggingPrintf( "ksz8851_reg_read: SPI_SR_OVRES\n" ); | |
| } | |
| res = (tmpbuf[3] << 8) | tmpbuf[2]; | |
| return res; | |
| } | |
| /** | |
| * \brief Write a register value. | |
| * | |
| * \param reg the register address to modify. | |
| * \param wrdata the new register value. | |
| */ | |
| void ksz8851_reg_write(uint16_t reg, uint16_t wrdata) | |
| { | |
| pdc_packet_t g_pdc_spi_tx_packet; | |
| pdc_packet_t g_pdc_spi_rx_packet; | |
| uint16_t cmd = 0; | |
| int iTryCount = 3; | |
| while( iTryCount-- > 0 ) | |
| { | |
| uint32_t ulStatus; | |
| spi_clear_ovres(); | |
| /* Move register address to cmd bits 9-2, make 32-bit address. */ | |
| cmd = (reg << 2) & REG_ADDR_MASK; | |
| /* Last 2 bits still under "don't care bits" handled with byte enable. */ | |
| /* Select byte enable for command. */ | |
| if (reg & 2) { | |
| /* Odd word address writes bytes 2 and 3 */ | |
| cmd |= (0xc << 10); | |
| } else { | |
| /* Even word address write bytes 0 and 1 */ | |
| cmd |= (0x3 << 10); | |
| } | |
| /* Add command write code. */ | |
| cmd |= CMD_WRITE; | |
| cmdBuf.uc[0] = cmd >> 8; | |
| cmdBuf.uc[1] = cmd & 0xff; | |
| cmdBuf.uc[2] = wrdata & 0xff; | |
| cmdBuf.uc[3] = wrdata >> 8; | |
| /* Prepare PDC transfer. */ | |
| g_pdc_spi_tx_packet.ul_addr = (uint32_t) cmdBuf.uc; | |
| g_pdc_spi_tx_packet.ul_size = 4; | |
| g_pdc_spi_rx_packet.ul_addr = (uint32_t) tmpbuf; | |
| g_pdc_spi_rx_packet.ul_size = 4; | |
| pdc_disable_transfer(g_p_spi_pdc, PERIPH_PTCR_RXTDIS | PERIPH_PTCR_TXTDIS); | |
| pdc_tx_init(g_p_spi_pdc, &g_pdc_spi_tx_packet, NULL); | |
| pdc_rx_init(g_p_spi_pdc, &g_pdc_spi_rx_packet, NULL); | |
| gpio_set_pin_low(KSZ8851SNL_CSN_GPIO); | |
| spi_disable_interrupt( KSZ8851SNL_SPI, ~0ul ); | |
| pdc_enable_transfer(g_p_spi_pdc, PERIPH_PTCR_RXTEN | PERIPH_PTCR_TXTEN); | |
| for( ;; ) | |
| { | |
| ulStatus = spi_read_status( KSZ8851SNL_SPI ); | |
| if( ( ulStatus & ( SPI_SR_OVRES | SPI_SR_ENDRX ) ) != 0 ) | |
| { | |
| break; | |
| } | |
| } | |
| gpio_set_pin_high( KSZ8851SNL_CSN_GPIO ); | |
| if( ( ulStatus & SPI_SR_OVRES ) == 0 ) | |
| { | |
| break; | |
| } | |
| pdc_disable_transfer(g_p_spi_pdc, PERIPH_PTCR_RXTDIS | PERIPH_PTCR_TXTDIS); | |
| lUDPLoggingPrintf( "ksz8851_reg_write: SPI_SR_OVRES\n" ); | |
| } | |
| } | |
| static void spi_clear_ovres( void ) | |
| { | |
| volatile uint32_t rc; | |
| rc = KSZ8851SNL_SPI->SPI_RDR; | |
| spi_read_status( KSZ8851SNL_SPI ); | |
| } | |
| /** | |
| * \brief Read internal fifo buffer. | |
| * | |
| * \param buf the buffer to store the data from the fifo buffer. | |
| * \param len the amount of data to read. | |
| */ | |
| void ksz8851_fifo_read(uint8_t *buf, uint32_t len) | |
| { | |
| pdc_packet_t g_pdc_spi_tx_packet; | |
| pdc_packet_t g_pdc_spi_rx_packet; | |
| pdc_packet_t g_pdc_spi_tx_npacket; | |
| pdc_packet_t g_pdc_spi_rx_npacket; | |
| memset( cmdBuf.uc, '\0', sizeof cmdBuf ); | |
| cmdBuf.uc[0] = FIFO_READ; | |
| spi_clear_ovres(); | |
| /* Prepare PDC transfer. */ | |
| g_pdc_spi_tx_packet.ul_addr = (uint32_t) cmdBuf.uc; | |
| g_pdc_spi_tx_packet.ul_size = 9; | |
| g_pdc_spi_rx_packet.ul_addr = (uint32_t) respBuf.uc; | |
| g_pdc_spi_rx_packet.ul_size = 9; | |
| g_pdc_spi_tx_npacket.ul_addr = (uint32_t) buf; | |
| g_pdc_spi_tx_npacket.ul_size = len; | |
| g_pdc_spi_rx_npacket.ul_addr = (uint32_t) buf; | |
| g_pdc_spi_rx_npacket.ul_size = len; | |
| pdc_disable_transfer(g_p_spi_pdc, PERIPH_PTCR_RXTDIS | PERIPH_PTCR_TXTDIS); | |
| pdc_tx_init(g_p_spi_pdc, &g_pdc_spi_tx_packet, &g_pdc_spi_tx_npacket); | |
| pdc_rx_init(g_p_spi_pdc, &g_pdc_spi_rx_packet, &g_pdc_spi_rx_npacket); | |
| spi_enable_interrupt(KSZ8851SNL_SPI, SPI_IER_RXBUFF | SPI_IER_OVRES); | |
| pdc_enable_transfer(g_p_spi_pdc, PERIPH_PTCR_RXTEN | PERIPH_PTCR_TXTEN); | |
| } | |
| /** | |
| * \brief Write internal fifo buffer. | |
| * | |
| * \param buf the buffer to send to the fifo buffer. | |
| * \param ulActualLength the total amount of data to write. | |
| * \param ulFIFOLength the size of the first pbuf to write from the pbuf chain. | |
| */ | |
| void ksz8851_fifo_write(uint8_t *buf, uint32_t ulActualLength, uint32_t ulFIFOLength) | |
| { | |
| static uint8_t frameID = 0; | |
| pdc_packet_t g_pdc_spi_tx_packet; | |
| pdc_packet_t g_pdc_spi_rx_packet; | |
| pdc_packet_t g_pdc_spi_tx_npacket; | |
| pdc_packet_t g_pdc_spi_rx_npacket; | |
| /* Prepare control word and byte count. */ | |
| cmdBuf.uc[0] = FIFO_WRITE; | |
| cmdBuf.uc[1] = frameID++ & 0x3f; | |
| cmdBuf.uc[2] = 0; | |
| cmdBuf.uc[3] = ulActualLength & 0xff; | |
| cmdBuf.uc[4] = ulActualLength >> 8; | |
| spi_clear_ovres(); | |
| /* Prepare PDC transfer. */ | |
| g_pdc_spi_tx_packet.ul_addr = (uint32_t) cmdBuf.uc; | |
| g_pdc_spi_tx_packet.ul_size = 5; | |
| g_pdc_spi_rx_packet.ul_addr = (uint32_t) respBuf.uc; | |
| g_pdc_spi_rx_packet.ul_size = 5; | |
| g_pdc_spi_tx_npacket.ul_addr = (uint32_t) buf; | |
| g_pdc_spi_tx_npacket.ul_size = ulFIFOLength; | |
| g_pdc_spi_rx_npacket.ul_addr = (uint32_t) tmpbuf; | |
| g_pdc_spi_rx_npacket.ul_size = ulFIFOLength; | |
| pdc_disable_transfer(g_p_spi_pdc, PERIPH_PTCR_RXTDIS | PERIPH_PTCR_TXTDIS); | |
| pdc_tx_init(g_p_spi_pdc, &g_pdc_spi_tx_packet, &g_pdc_spi_tx_npacket); | |
| #if( TX_USES_RECV == 1 ) | |
| pdc_rx_init(g_p_spi_pdc, &g_pdc_spi_rx_packet, &g_pdc_spi_rx_npacket); | |
| spi_enable_interrupt(KSZ8851SNL_SPI, SPI_IER_ENDRX | SPI_IER_OVRES); | |
| pdc_enable_transfer(g_p_spi_pdc, PERIPH_PTCR_RXTEN | PERIPH_PTCR_TXTEN); | |
| #else | |
| spi_enable_interrupt(KSZ8851SNL_SPI, SPI_SR_TXBUFE | SPI_IER_OVRES); | |
| pdc_enable_transfer(g_p_spi_pdc, PERIPH_PTCR_TXTEN); | |
| #endif | |
| } | |
| /** | |
| * \brief Write dummy data to the internal fifo buffer. | |
| * | |
| * \param len the amount of dummy data to write. | |
| */ | |
| void ksz8851_fifo_dummy(uint32_t len) | |
| { | |
| pdc_packet_t g_pdc_spi_tx_packet; | |
| pdc_packet_t g_pdc_spi_rx_packet; | |
| /* Prepare PDC transfer. */ | |
| g_pdc_spi_tx_packet.ul_addr = (uint32_t) tmpbuf; | |
| g_pdc_spi_tx_packet.ul_size = len; | |
| g_pdc_spi_rx_packet.ul_addr = (uint32_t) tmpbuf; | |
| g_pdc_spi_rx_packet.ul_size = len; | |
| pdc_disable_transfer(g_p_spi_pdc, PERIPH_PTCR_RXTDIS | PERIPH_PTCR_TXTDIS); | |
| pdc_tx_init(g_p_spi_pdc, &g_pdc_spi_tx_packet, NULL); | |
| pdc_rx_init(g_p_spi_pdc, &g_pdc_spi_rx_packet, NULL); | |
| pdc_enable_transfer(g_p_spi_pdc, PERIPH_PTCR_RXTEN | PERIPH_PTCR_TXTEN); | |
| while (!(spi_read_status(KSZ8851SNL_SPI) & SPI_SR_ENDRX)) | |
| ; | |
| } | |
| void ksz8851snl_set_registers(void) | |
| { | |
| /* Init step2-4: write QMU MAC address (low, middle then high). */ | |
| ksz8851_reg_write(REG_MAC_ADDR_0, (ETHERNET_CONF_ETHADDR4 << 8) | ETHERNET_CONF_ETHADDR5); | |
| ksz8851_reg_write(REG_MAC_ADDR_2, (ETHERNET_CONF_ETHADDR2 << 8) | ETHERNET_CONF_ETHADDR3); | |
| ksz8851_reg_write(REG_MAC_ADDR_4, (ETHERNET_CONF_ETHADDR0 << 8) | ETHERNET_CONF_ETHADDR1); | |
| /* Init step5: enable QMU Transmit Frame Data Pointer Auto Increment. */ | |
| ksz8851_reg_write(REG_TX_ADDR_PTR, ADDR_PTR_AUTO_INC); | |
| /* Init step6: configure QMU transmit control register. */ | |
| ksz8851_reg_write(REG_TX_CTRL, | |
| TX_CTRL_ICMP_CHECKSUM | | |
| TX_CTRL_UDP_CHECKSUM | | |
| TX_CTRL_TCP_CHECKSUM | | |
| TX_CTRL_IP_CHECKSUM | | |
| TX_CTRL_FLOW_ENABLE | | |
| TX_CTRL_PAD_ENABLE | | |
| TX_CTRL_CRC_ENABLE | |
| ); | |
| /* Init step7: enable QMU Receive Frame Data Pointer Auto Increment. */ | |
| ksz8851_reg_write(REG_RX_ADDR_PTR, ADDR_PTR_AUTO_INC); | |
| /* Init step8: configure QMU Receive Frame Threshold for one frame. */ | |
| ksz8851_reg_write(REG_RX_FRAME_CNT_THRES, 1); | |
| /* Init step9: configure QMU receive control register1. */ | |
| ksz8851_reg_write(REG_RX_CTRL1, | |
| RX_CTRL_UDP_CHECKSUM | | |
| RX_CTRL_TCP_CHECKSUM | | |
| RX_CTRL_IP_CHECKSUM | | |
| RX_CTRL_MAC_FILTER | | |
| RX_CTRL_FLOW_ENABLE | | |
| RX_CTRL_BROADCAST | | |
| RX_CTRL_ALL_MULTICAST| | |
| RX_CTRL_UNICAST); | |
| // ksz8851_reg_write(REG_RX_CTRL1, | |
| // RX_CTRL_UDP_CHECKSUM | | |
| // RX_CTRL_TCP_CHECKSUM | | |
| // RX_CTRL_IP_CHECKSUM | | |
| // RX_CTRL_FLOW_ENABLE | | |
| // RX_CTRL_PROMISCUOUS); | |
| ksz8851_reg_write(REG_RX_CTRL2, | |
| RX_CTRL_IPV6_UDP_NOCHECKSUM | | |
| RX_CTRL_UDP_LITE_CHECKSUM | | |
| RX_CTRL_ICMP_CHECKSUM | | |
| RX_CTRL_BURST_LEN_FRAME); | |
| //#define RXQ_TWOBYTE_OFFSET (0x0200) /* Enable adding 2-byte before frame header for IP aligned with DWORD */ | |
| #warning Remember to try the above option to get a 2-byte offset | |
| /* Init step11: configure QMU receive queue: trigger INT and auto-dequeue frame. */ | |
| ksz8851_reg_write( REG_RXQ_CMD, RXQ_CMD_CNTL | RXQ_TWOBYTE_OFFSET ); | |
| /* Init step12: adjust SPI data output delay. */ | |
| ksz8851_reg_write(REG_BUS_CLOCK_CTRL, BUS_CLOCK_166 | BUS_CLOCK_DIVIDEDBY_1); | |
| /* Init step13: restart auto-negotiation. */ | |
| ksz8851_reg_setbits(REG_PORT_CTRL, PORT_AUTO_NEG_RESTART); | |
| /* Init step13.1: force link in half duplex if auto-negotiation failed. */ | |
| if ((ksz8851_reg_read(REG_PORT_CTRL) & PORT_AUTO_NEG_RESTART) != PORT_AUTO_NEG_RESTART) | |
| { | |
| ksz8851_reg_clrbits(REG_PORT_CTRL, PORT_FORCE_FULL_DUPLEX); | |
| } | |
| /* Init step14: clear interrupt status. */ | |
| ksz8851_reg_write(REG_INT_STATUS, 0xFFFF); | |
| /* Init step15: set interrupt mask. */ | |
| ksz8851_reg_write(REG_INT_MASK, INT_RX); | |
| /* Init step16: enable QMU Transmit. */ | |
| ksz8851_reg_setbits(REG_TX_CTRL, TX_CTRL_ENABLE); | |
| /* Init step17: enable QMU Receive. */ | |
| ksz8851_reg_setbits(REG_RX_CTRL1, RX_CTRL_ENABLE); | |
| } | |
| /** | |
| * \brief KSZ8851SNL initialization function. | |
| * | |
| * \return 0 on success, 1 on communication error. | |
| */ | |
| uint32_t ksz8851snl_init(void) | |
| { | |
| uint32_t count = 10; | |
| uint16_t dev_id = 0; | |
| uint8_t id_ok = 0; | |
| /* Configure the SPI peripheral. */ | |
| spi_enable_clock(KSZ8851SNL_SPI); | |
| spi_disable(KSZ8851SNL_SPI); | |
| spi_reset(KSZ8851SNL_SPI); | |
| spi_set_master_mode(KSZ8851SNL_SPI); | |
| spi_disable_mode_fault_detect(KSZ8851SNL_SPI); | |
| spi_set_peripheral_chip_select_value(KSZ8851SNL_SPI, ~(uint32_t)(1UL << KSZ8851SNL_CS_PIN)); | |
| spi_set_fixed_peripheral_select(KSZ8851SNL_SPI); | |
| //spi_disable_peripheral_select_decode(KSZ8851SNL_SPI); | |
| spi_set_clock_polarity(KSZ8851SNL_SPI, KSZ8851SNL_CS_PIN, SPI_CLK_POLARITY); | |
| spi_set_clock_phase(KSZ8851SNL_SPI, KSZ8851SNL_CS_PIN, SPI_CLK_PHASE); | |
| spi_set_bits_per_transfer(KSZ8851SNL_SPI, KSZ8851SNL_CS_PIN, | |
| SPI_CSR_BITS_8_BIT); | |
| spi_set_baudrate_div(KSZ8851SNL_SPI, KSZ8851SNL_CS_PIN, (sysclk_get_cpu_hz() / KSZ8851SNL_CLOCK_SPEED)); | |
| // spi_set_transfer_delay(KSZ8851SNL_SPI, KSZ8851SNL_CS_PIN, CONFIG_SPI_MASTER_DELAY_BS, | |
| // CONFIG_SPI_MASTER_DELAY_BCT); | |
| spi_set_transfer_delay(KSZ8851SNL_SPI, KSZ8851SNL_CS_PIN, 0, 0); | |
| spi_enable(KSZ8851SNL_SPI); | |
| /* Get pointer to UART PDC register base. */ | |
| g_p_spi_pdc = spi_get_pdc_base(KSZ8851SNL_SPI); | |
| pdc_enable_transfer(g_p_spi_pdc, PERIPH_PTCR_RXTEN | PERIPH_PTCR_TXTEN); | |
| /* Control RSTN and CSN pin from the driver. */ | |
| gpio_configure_pin(KSZ8851SNL_CSN_GPIO, KSZ8851SNL_CSN_FLAGS); | |
| gpio_set_pin_high(KSZ8851SNL_CSN_GPIO); | |
| gpio_configure_pin(KSZ8851SNL_RSTN_GPIO, KSZ8851SNL_RSTN_FLAGS); | |
| /* Reset the Micrel in a proper state. */ | |
| while( count-- ) | |
| { | |
| /* Perform hardware reset with respect to the reset timing from the datasheet. */ | |
| gpio_set_pin_low(KSZ8851SNL_RSTN_GPIO); | |
| vTaskDelay(2); | |
| gpio_set_pin_high(KSZ8851SNL_RSTN_GPIO); | |
| vTaskDelay(2); | |
| /* Init step1: read chip ID. */ | |
| dev_id = ksz8851_reg_read(REG_CHIP_ID); | |
| if( ( dev_id & 0xFFF0 ) == CHIP_ID_8851_16 ) | |
| { | |
| id_ok = 1; | |
| break; | |
| } | |
| } | |
| if( id_ok != 0 ) | |
| { | |
| ksz8851snl_set_registers(); | |
| } | |
| return id_ok ? 1 : -1; | |
| } | |
| uint32_t ksz8851snl_reinit(void) | |
| { | |
| uint32_t count = 10; | |
| uint16_t dev_id = 0; | |
| uint8_t id_ok = 0; | |
| /* Reset the Micrel in a proper state. */ | |
| while( count-- ) | |
| { | |
| /* Perform hardware reset with respect to the reset timing from the datasheet. */ | |
| gpio_set_pin_low(KSZ8851SNL_RSTN_GPIO); | |
| vTaskDelay(2); | |
| gpio_set_pin_high(KSZ8851SNL_RSTN_GPIO); | |
| vTaskDelay(2); | |
| /* Init step1: read chip ID. */ | |
| dev_id = ksz8851_reg_read(REG_CHIP_ID); | |
| if( ( dev_id & 0xFFF0 ) == CHIP_ID_8851_16 ) | |
| { | |
| id_ok = 1; | |
| break; | |
| } | |
| } | |
| if( id_ok != 0 ) | |
| { | |
| ksz8851snl_set_registers(); | |
| } | |
| return id_ok ? 1 : -1; | |
| } | |
| uint32_t ksz8851snl_reset_rx( void ) | |
| { | |
| uint16_t usValue; | |
| usValue = ksz8851_reg_read(REG_RX_CTRL1); | |
| usValue &= ~( ( uint16_t ) RX_CTRL_ENABLE | RX_CTRL_FLUSH_QUEUE ); | |
| ksz8851_reg_write( REG_RX_CTRL1, usValue ); vTaskDelay( 2 ); | |
| ksz8851_reg_write( REG_RX_CTRL1, usValue | RX_CTRL_FLUSH_QUEUE ); vTaskDelay( 1 ); | |
| ksz8851_reg_write( REG_RX_CTRL1, usValue ); vTaskDelay( 1 ); | |
| ksz8851_reg_write( REG_RX_CTRL1, usValue | RX_CTRL_ENABLE ); vTaskDelay( 1 ); | |
| return ( uint32_t )usValue; | |
| } | |
| uint32_t ksz8851snl_reset_tx( void ) | |
| { | |
| uint16_t usValue; | |
| usValue = ksz8851_reg_read( REG_TX_CTRL ); | |
| usValue &= ~( ( uint16_t ) TX_CTRL_ENABLE | TX_CTRL_FLUSH_QUEUE ); | |
| ksz8851_reg_write( REG_TX_CTRL, usValue ); vTaskDelay( 2 ); | |
| ksz8851_reg_write( REG_TX_CTRL, usValue | TX_CTRL_FLUSH_QUEUE ); vTaskDelay( 1 ); | |
| ksz8851_reg_write( REG_TX_CTRL, usValue ); vTaskDelay( 1 ); | |
| ksz8851_reg_write( REG_TX_CTRL, usValue | TX_CTRL_ENABLE ); vTaskDelay( 1 ); | |
| return ( uint32_t )usValue; | |
| } |