Newer
Older
mbed-os / targets / TARGET_ARM_FM / TARGET_FVP_MPS2 / drivers / lan91c111.c
@Harrison Mutai Harrison Mutai on 15 Oct 2020 8 KB Add SPDX license identifier to Arm files
/* mbed Microcontroller Library
 * Copyright (c) 2006-2018 ARM Limited
 * SPDX-License-Identifier: Apache-2.0
 *
 * 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 "lan91c111.h"
#include <stddef.h>

static lan91_handle_t lan91c111_handle;

void LAN91_init(void)
{

    uint32_t tcr, stat;

    LAN91_ClearInterruptMasks();
    LAN91_Reset();

    /* Clear control registers. */
    LAN91_SelectBank(0);
    LREG(uint16_t, B0_RCR) = 0;
    LREG(uint16_t, B0_TCR) = 0;

    /* Write Configuration Registers */
    LAN91_SelectBank(1);
    LREG(uint16_t, B1_CR) = CR_EPH_POW_EN | CR_DEFAULT;

    /* Reset the PHY*/
    write_PHY(0, 0x8000);

    /* clear phy 18 status */
    read_PHY(18);

    /* Use 100MBit link speed by default. */
    LAN91_SelectBank(0);
    LREG(uint16_t, B0_RPCR) = RPCR_SPEED | LEDA_10M_100M | LEDB_TX_RX;

    /* Read Status Register */
    stat = read_PHY(18);

    /* Set the Control Register */
    LAN91_SelectBank(1);
    LREG(uint16_t, B1_CTR) = CTR_LE_ENABLE | CTR_CR_ENABLE | CTR_TE_ENABLE | CTR_AUTO_REL  | CTR_DEFAULT;

    /* Setup Transmit Control Register. */
    tcr = TCR_TXENA | TCR_PAD_EN;
    if (stat & 0x0040) {
        tcr |= TCR_FDUPLX;
    }
    LREG(uint16_t, B0_TCR) = (uint16_t)tcr;

    /* Reset MMU */
    LAN91_SelectBank(2);
    LREG(uint16_t, B2_MMUCR) = MMU_RESET;
    while (LREG(uint16_t, B2_MMUCR) & MMUCR_BUSY);

    /* Configure the Interrupts, allow  RX_OVRN and RCV intr. */
    LAN91_SetInterruptMasks(MSK_RCV);

    /* Set all buffers or data in handler for data transmit/receive process. */
    LAN91_SetHandler();
}

void read_MACaddr(uint8_t *addr)
{
    int i;

    LAN91_SelectBank(1);

    i = LREG(uint16_t, B1_IAR0);
    addr[0] = (uint8_t)i;
    addr[1] = (uint8_t)(i >> 8);

    i = LREG(uint16_t, B1_IAR2);
    addr[2] = (uint8_t)i;
    addr[3] = (uint8_t)(i >> 8);

    i = LREG(uint16_t, B1_IAR4);
    addr[4] = (uint8_t)i;
    addr[5] = (uint8_t)(i >> 8);
}

void LAN91_SetCallback(lan91_callback_t callback, void *userData)
{
    /* Set callback and userData. */
    lan91c111_handle.callback = callback;
    lan91c111_handle.userData = userData;
}


bool LAN91_send_frame(uint32_t *buff, uint32_t *size)
{
    /* Send frame to Lan91C111 ethernet controller */
    uint16_t *dp;
    uint8_t packnr;
    int i;

    LAN91_SelectBank(2);

    /* MMU allocate memory for transmitting*/
    LREG(uint16_t, B2_MMUCR) = MMU_ALLOC_TX;

    /* Check if Interrupt Status Register been set for MMU Allocate Ready */
    if (!(LREG(uint16_t, B2_IST) & IST_ALLOC_INT)) {
        /* Failed, Reset MMU */
        LREG(uint16_t, B2_MMUCR) = MMU_RESET;
        while (LREG(uint16_t, B2_MMUCR) & MMUCR_BUSY);
        return false;
    }

    /* set TX package number from allocated package number
       and also set pointer register as Auto Increase, write and transmit set */
    packnr = LREG(uint8_t, B2_ARR);
    LREG(uint8_t, B2_PNR) = packnr;
    LREG(uint16_t, B2_PTR) = PTR_AUTO_INCR;

    /* Reserve space for Status Word */
    LREG(uint16_t, B2_DATA0) = 0x0000;
    /* Total = Raw Data Size + STATUS WORD + BYTE COUNT + CONTROL BYTE + LAST BYTE */
    LREG(uint16_t, B2_DATA0) = (uint16_t) * size + 6;

    /* Copy frame data to Ethernet controller */
    dp = (uint16_t *)buff;
    for (i = *size; i > 1; i -= 2) {
        LREG(uint16_t, B2_DATA0) = *dp++;
    }

    /* If data is odd , Add a control word and odd byte. */
    if (i) {
        LREG(uint16_t, B2_DATA0) = (RFC_CRC | RFC_ODD) | (*dp & 0xFF);
    } else {
        /* Add control word. */
        LREG(uint16_t, B2_DATA0) = RFC_CRC;
    }

    LAN91_SelectBank(0);
    /* Enable transmitter. */
    LREG(uint16_t, B0_TCR)  = TCR_TXENA | TCR_PAD_EN;

    /* Enqueue the packet. */
    LAN91_SelectBank(2);
    LREG(uint16_t, B2_MMUCR) = MMU_ENQ_TX;

    return true;
}



bool LAN91_receive_frame(uint32_t *buff, uint32_t *size)
{
    uint32_t State, RxLen;
    uint32_t val, *dp;

    /* No receive packets queued in Rx FIFO queue */
    LAN91_SelectBank(2);
    State = LREG(uint16_t, B2_FIFO);
    if (State & FIFO_REMPTY) {
        return false;
    }


    /* Pointer Register set to  RCV + Auto Increase + Read access
    So that Data Register is use RX FIFO*/
    LREG(uint16_t, B2_PTR) = PTR_RCV | PTR_AUTO_INCR | PTR_READ;

    /* Read status word and packet length */
    val = LREG(uint32_t, B2_DATA);
    State = val & 0xFFFF;
    /* Raw Data Size = Total - STATUS WORD - BYTE COUNT - CONTROL BYTE - LAST BYTE */
    RxLen = (val >> 16) - 6;

    /* Check State word if Odd number of bytes in a frame. */
    if (State & RFS_ODDFRM) {
        RxLen++;
    }

    /* Packet too big, ignore it and free MMU and continue */
    if (RxLen > LAN91_ETH_MTU_SIZE) {
        LREG(uint16_t, B2_MMUCR) = MMU_REMV_REL_RX;
        return false;
    }

    if (buff != NULL) {
        /* Make sure that block is dword aligned */
        RxLen = (RxLen + 3) >> 2;
        *size = RxLen << 2;
        dp = buff;
        for (; RxLen; RxLen--) {
            *dp++ = LREG(uint32_t, B2_DATA);
        }
    }

    /* MMU free packet. Remove and Relase from TOP of RX */
    LREG(uint16_t, B2_MMUCR) = MMU_REMV_REL_RX;

    return true;
}



void ETHERNET_Handler(void)
{
    LAN91_SelectBank(2);
    if ((LREG(uint8_t, B2_IST) & IST_RCV) != 0) {
        LREG(uint8_t,  B2_MSK) = 0;
        /* Callback function. */
        if (lan91c111_handle.callback) {
            lan91c111_handle.callback(LAN91_RxEvent, lan91c111_handle.userData);
        }
    }
}


lan91_phy_status_t LAN91_GetLinkStatus(void)
{
    if (read_PHY(0x2u) & 0x4u) {
        return STATE_LINK_UP;
    } else {
        return STATE_LINK_DOWN;
    }
}

static void output_MDO(int bit_value)
{
    uint32_t val = MGMT_MDOE;

    if (bit_value) {
        val |= MGMT_MDO;
    }
    LREG(uint16_t, B3_MGMT) = (uint16_t)val;
    LREG(uint16_t, B3_MGMT) = (uint16_t)(val | MGMT_MCLK);
    LREG(uint16_t, B3_MGMT) = (uint16_t)val;
}



static int input_MDI(void)
{
    int val = 0;

    LREG(uint16_t, B3_MGMT) = 0;
    LREG(uint16_t, B3_MGMT) = MGMT_MCLK;
    if (LREG(uint16_t, B3_MGMT) & MGMT_MDI) {
        val = 1;
    }
    LREG(uint16_t, B3_MGMT) = 0;
    return (val);
}

static void write_PHY(uint32_t PhyReg, int Value)
{
    int i;

    LAN91_SelectBank(3);
    LREG(uint16_t, B3_MGMT) = MGMT_MDOE | MGMT_MDO;

    /* 32 consecutive ones on MDO to establish sync */
    for (i = 0; i < 32; i++) {
        LREG(uint16_t, B3_MGMT) = MGMT_MDOE | MGMT_MDO;
        LREG(uint16_t, B3_MGMT) = MGMT_MDOE | MGMT_MDO | MGMT_MCLK;
    }
    LREG(uint16_t, B3_MGMT) = MGMT_MDOE;

    /* start code (01) */
    output_MDO(0);
    output_MDO(1);

    /* write command (01) */
    output_MDO(0);
    output_MDO(1);

    /* write PHY address - which is five 0s for 91C111 */
    for (i = 0; i < 5; i++) {
        output_MDO(0);
    }

    /* write the PHY register to write (highest bit first) */
    for (i = 0; i < 5; i++) {
        output_MDO((PhyReg >> 4) & 0x01);
        PhyReg <<= 1;
    }

    /* turnaround MDO */
    output_MDO(1);
    output_MDO(0);

    /* write the data value (highest bit first) */
    for (i = 0; i < 16; i++) {
        output_MDO((Value >> 15) & 0x01);
        Value <<= 1;
    }

    /* turnaround MDO is tristated */
    LREG(uint16_t, B3_MGMT) = 0;
    LREG(uint16_t, B3_MGMT) = MGMT_MCLK;
    LREG(uint16_t, B3_MGMT) = 0;
}


static uint16_t read_PHY(uint32_t PhyReg)
{
    int i, val;

    LAN91_SelectBank(3);
    LREG(uint16_t, B3_MGMT) = MGMT_MDOE | MGMT_MDO;

    /* 32 consecutive ones on MDO to establish sync */
    for (i = 0; i < 32; i++) {
        LREG(uint16_t, B3_MGMT) = MGMT_MDOE | MGMT_MDO;
        LREG(uint16_t, B3_MGMT) = MGMT_MDOE | MGMT_MDO | MGMT_MCLK;
    }
    LREG(uint16_t, B3_MGMT) = MGMT_MDOE;

    /* start code (01) */
    output_MDO(0);
    output_MDO(1);

    /* read command (10) */
    output_MDO(1);
    output_MDO(0);

    /* write PHY address - which is five 0s for 91C111 */
    for (i = 0; i < 5; i++) {
        output_MDO(0);
    }

    /* write the PHY register to read (highest bit first) */
    for (i = 0; i < 5; i++) {
        output_MDO((PhyReg >> 4) & 0x01);
        PhyReg <<= 1;
    }

    /* turnaround MDO is tristated */
    LREG(uint16_t, B3_MGMT) = 0;
    LREG(uint16_t, B3_MGMT) = MGMT_MCLK;
    LREG(uint16_t, B3_MGMT) = 0;

    /* read the data value */
    val = 0;
    for (i = 0; i < 16; i++) {
        val <<= 1;
        val |= input_MDI();
    }

    /* turnaround MDO is tristated */
    LREG(uint16_t, B3_MGMT) = 0;
    LREG(uint16_t, B3_MGMT) = MGMT_MCLK;
    LREG(uint16_t, B3_MGMT) = 0;

    return (val);
}