Newer
Older
mbed-os / targets / TARGET_ARM_SSG / TARGET_CM3DS_MPS2 / device / drivers / smsc9220_eth_drv.c
@Harrison Mutai Harrison Mutai on 15 Oct 2020 34 KB Add SPDX license identifier to Arm files
/*
 * Copyright (c) 2016-2019 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 "cmsis.h"
#include "smsc9220_eth_drv.h"

/** Setter bit manipulation macro */
#define SET_BIT(WORD, BIT_INDEX) ((WORD) |= (1U << (BIT_INDEX)))
/** Clearing bit manipulation macro */
#define CLR_BIT(WORD, BIT_INDEX) ((WORD) &= ~(1U << (BIT_INDEX)))
/** Getter bit manipulation macro */
#define GET_BIT(WORD, BIT_INDEX) (bool)(((WORD) & (1U << (BIT_INDEX))))

/** Setter bit-field manipulation macro */
#define SET_BIT_FIELD(WORD, BIT_MASK, BIT_OFFSET, VALUE) \
            (WORD |= ((VALUE & BIT_MASK) << BIT_OFFSET))

/** Clearing bit-field manipulation macro */
#define CLR_BIT_FIELD(WORD, BIT_MASK, BIT_OFFSET, VALUE) \
            (WORD &= ~((VALUE & BIT_MASK) << BIT_OFFSET))

/** Getter bit-field manipulation macro */
#define GET_BIT_FIELD(WORD, BIT_MASK, BIT_OFFSET) \
            ((WORD >> BIT_OFFSET) & BIT_MASK)

/** Millisec timeout macros */
#define RESET_TIME_OUT_MS        10U
#define REG_WRITE_TIME_OUT_MS    50U
#define PHY_RESET_TIME_OUT_MS    100U
#define INIT_FINISH_DELAY        2000U

struct smsc9220_eth_reg_map_t {
__I  uint32_t rx_data_port;      /**< Receive FIFO Ports (offset 0x0) */
     uint32_t reserved1[0x7];
__O  uint32_t tx_data_port;      /**< Transmit FIFO Ports (offset 0x20) */
     uint32_t reserved2[0x7];

__I  uint32_t rx_status_port;   /**< Receive FIFO status port (offset 0x40) */
__I  uint32_t rx_status_peek;   /**< Receive FIFO status peek (offset 0x44) */
__I  uint32_t tx_status_port;   /**< Transmit FIFO status port (offset 0x48) */
__I  uint32_t tx_status_peek;   /**< Transmit FIFO status peek (offset 0x4C) */

__I  uint32_t id_revision;      /**< Chip ID and Revision (offset 0x50) */
__IO uint32_t irq_cfg;          /**< Main Interrupt Config (offset 0x54) */
__IO uint32_t irq_status;       /**< Interrupt Status (offset 0x58) */
__IO uint32_t irq_enable;       /**< Interrupt Enable Register (offset 0x5C) */
     uint32_t reserved3;        /**< Reserved for future use (offset 0x60) */
__I  uint32_t byte_test;        /**< Byte order test 87654321h (offset 0x64) */
__IO uint32_t fifo_level_irq;   /**< FIFO Level Interrupts (offset 0x68) */
__IO uint32_t rx_cfg;           /**< Receive Configuration (offset 0x6C) */
__IO uint32_t tx_cfg;           /**< Transmit Configuration (offset 0x70) */
__IO uint32_t hw_cfg;           /**< Hardware Configuration (offset 0x74) */
__IO uint32_t rx_datapath_ctrl; /**< RX Datapath Control (offset 0x78) */
__I  uint32_t rx_fifo_inf;      /**< Receive FIFO Information (offset 0x7C) */
__I  uint32_t tx_fifo_inf;      /**< Transmit FIFO Information (offset 0x80) */
__IO uint32_t pmt_ctrl;         /**< Power Management Control (offset 0x84) */
__IO uint32_t gpio_cfg;         /**< GPIO Configuration (offset 0x88) */
__IO uint32_t gptimer_cfg;      /**< GP Timer Configuration (offset 0x8C) */
__I  uint32_t gptimer_count;    /**< GP Timer Count (offset 0x90) */
     uint32_t reserved4;        /**< Reserved for future use (offset 0x94) */
__IO uint32_t word_swap;        /**< WORD SWAP Register (offset 0x98) */
__I  uint32_t free_run_counter; /**< Free Run Counter (offset 0x9C) */
__I  uint32_t rx_dropped_frames;/**< RX Dropped Frames Counter (offset 0xA0) */
__IO uint32_t mac_csr_cmd;      /**< MAC CSR Synchronizer Cmd (offset 0xA4) */
__IO uint32_t mac_csr_data;     /**< MAC CSR Synchronizer Data (offset 0xA8) */
__IO uint32_t afc_cfg;          /**< AutomaticFlow Ctrl Config (offset 0xAC) */
__IO uint32_t eeprom_cmd;       /**< EEPROM Command (offset 0xB0) */
__IO uint32_t eeprom_data;      /**< EEPROM Data (offset 0xB4) */
};

/**
 * \brief TX FIFO Size definitions
 *
 */
#define TX_STATUS_FIFO_SIZE_BYTES       512U /*< fixed allocation in bytes */
#define TX_DATA_FIFO_SIZE_KBYTES_POS    16U
#define TX_DATA_FIFO_SIZE_KBYTES_MASK   0x0FU
#define KBYTES_TO_BYTES_MULTIPLIER      1024U

/**
 * \brief FIFO Info definitions
 *
 */
#define FIFO_USED_SPACE_MASK         0xFFFFU
#define DATA_FIFO_USED_SPACE_POS     0U
#define STATUS_FIFO_USED_SPACE_POS   16U

/**
 * \brief MAC CSR Synchronizer Command bit definitions
 *
 */
enum mac_csr_cmd_bits_t{
    MAC_CSR_CMD_RW_INDEX = 30U,
    MAC_CSR_CMD_BUSY_INDEX = 31U,
};

#define MAC_CSR_CMD_ADDRESS_MASK    0x0FU

/**
 * \brief MAC Control register bit definitions
 *
 */
enum mac_reg_cr_bits_t{
    MAC_REG_CR_RXEN_INDEX = 2U,
    MAC_REG_CR_TXEN_INDEX = 3U
};

/**
 * \brief MII Access register bit definitions
 *
 */
enum mac_reg_mii_acc_bits_t{
    MAC_REG_MII_ACC_BUSY_INDEX = 0U,
    MAC_REG_MII_ACC_WRITE_INDEX = 1U,
    MAC_REG_MII_ACC_PHYADDR_INDEX = 11U
};
#define MAC_REG_MII_ACC_MII_REG_MASK    0x1FU
#define MAC_REG_MII_ACC_MII_REG_OFFSET  6U

/**
 * \brief Hardware config register bit definitions
 *
 */
enum hw_cfg_reg_bits_t{
    HW_CFG_REG_SRST_INDEX = 0U,
    HW_CFG_REG_SRST_TIMEOUT_INDEX = 1U,
    HW_CFG_REG_MUST_BE_ONE_INDEX = 20U,
};
#define HW_CFG_REG_TX_FIFO_SIZE_POS     16U
#define HW_CFG_REG_TX_FIFO_SIZE_MIN     2U  /*< Min Tx fifo size in KB */
#define HW_CFG_REG_TX_FIFO_SIZE_MAX     14U /*< Max Tx fifo size in KB */
#define HW_CFG_REG_TX_FIFO_SIZE         5U  /*< Tx fifo size in KB */

/**
 * \brief EEPROM command register bit definitions
 *
 */
enum eeprom_cmd_reg_bits_t{
    EEPROM_CMD_REG_BUSY_INDEX = 31U,
};

/**
 * \brief PHY Basic Control register bit definitions
 *
 */
enum phy_reg_bctrl_reg_bits_t{
    PHY_REG_BCTRL_RST_AUTO_NEG_INDEX = 9U,
    PHY_REG_BCTRL_AUTO_NEG_EN_INDEX = 12U,
    PHY_REG_BCTRL_RESET_INDEX = 15U
};

/**
 * \brief TX Command A bit definitions
 *
 */
#define TX_CMD_DATA_START_OFFSET_BYTES_POS   16U
#define TX_CMD_DATA_START_OFFSET_BYTES_MASK  0x1FU


enum tx_command_a_bits_t{
    TX_COMMAND_A_LAST_SEGMENT_INDEX = 12U,
    TX_COMMAND_A_FIRST_SEGMENT_INDEX = 13U
};

#define TX_CMD_PKT_LEN_BYTES_MASK  0x7FFU
#define TX_CMD_PKT_TAG_MASK        0xFFFFU
#define TX_CMD_PKT_TAG_POS         16U


/**
 * \brief RX Fifo Status bit definitions
 *
 */
enum rx_fifo_status_bits_t{
    RX_FIFO_STATUS_CRC_ERROR_INDEX       = 1U,
    RX_FIFO_STATUS_DRIBBLING_BIT_INDEX   = 2U,
    RX_FIFO_STATUS_MII_ERROR_INDEX       = 3U,
    RX_FIFO_STATUS_REC_WD_TIMEOUT_INDEX  = 4U,
    RX_FIFO_STATUS_FRAME_TYPE_INDEX      = 5U,
    RX_FIFO_STATUS_COLLISION_SEEN_INDEX  = 6U,
    RX_FIFO_STATUS_FRAME_TOO_LONG_INDEX  = 7U,
    RX_FIFO_STATUS_MULTICAST_INDEX       = 10U,
    RX_FIFO_STATUS_RUNT_FRAME_INDEX      = 11U,
    RX_FIFO_STATUS_LENGTH_ERROR_INDEX    = 12U,
    RX_FIFO_STATUS_BROADCAST_FRAME_INDEX = 13U,
    RX_FIFO_STATUS_ERROR_INDEX           = 15U,
    RX_FIFO_STATUS_FILTERING_FAIL_INDEX  = 30U,
};
#define RX_FIFO_STATUS_PKT_LENGTH_POS    16U
#define RX_FIFO_STATUS_PKT_LENGTH_MASK   0x3FFFU

/**
 * \brief Interrupt Configuration register bit definitions
 *
 */
enum irq_cfg_bits_t{
    IRQ_CFG_IRQ_EN_INDEX = 8U
};

#define IRQ_CFG_INT_DEAS_MASK   0xFFU
#define IRQ_CFG_INT_DEAS_POS    24U
#define IRQ_CFG_INT_DEAS_10US   0x22U

/**
 * \brief Automatic Flow Control register bit definitions
 *
 */
enum afc_bits_t{
    AFC_ANY_INDEX = 0U,
    AFC_ADDR_INDEX = 1U,
    AFC_BROADCAST_INDEX = 2U,
    AFC_MULTICAST_INDEX = 3U
};

#define AFC_BACK_DUR_MASK    0x0FU
#define AFC_BACK_DUR_POS     4U
#define AFC_BACK_DUR         4U  /**< equal to 50us */

#define AFC_LOW_LEVEL_MASK   0xFFU
#define AFC_LOW_LEVEL_POS    8U
#define AFC_LOW_LEVEL        55U  /**< specifies in multiple of 64 bytes */

#define AFC_HIGH_LEVEL_MASK  0xFFU
#define AFC_HIGH_LEVEL_POS   16U
#define AFC_HIGH_LEVEL       110U  /**< specifies in multiple of 64 bytes */

/**
 * \brief Auto-Negotiation Advertisement register bit definitions
 *
 */
enum aneg_bits_t{
    ANEG_10_BASE_T_INDEX             = 5U, /**< 10Mbps able */
    ANEG_10_BASE_T_FULL_DUPL_INDEX   = 6U, /**< 10Mbps with full duplex */
    ANEG_100_BASE_TX_INDEX           = 7U, /**< 100Mbps Tx able */
    ANEG_100_BASE_TX_FULL_DUPL_INDEX = 8U, /**< 100Mbps with full duplex */
    ANEG_SYMM_PAUSE_INDEX            = 10U, /**< Symmetric Pause */
    ANEG_ASYMM_PAUSE_INDEX           = 11U /**< Asymmetric Pause */
};

/**
 * \brief Transmit Configuration register bit definitions
 *
 */
enum tx_cfg_bits_t{
    TX_CFG_STOP_INDEX = 0U,      /*< stop */
    TX_CFG_ON_INDEX = 1U,        /*< on */
    TX_CFG_AO_INDEX = 2U,        /*< allow overrun */
    TX_CFG_TXD_DUMP_INDEX = 14U, /*< Data FIFO dump */
    TX_CFG_TXS_DUMP_INDEX = 15U  /*< Status FIFO dump */
};

/**
 * \brief Chip ID definitions
 *
 */
#define CHIP_ID         0x9220U
#define CHIP_ID_MASK    0xFFFFU
#define CHIP_ID_POS     16U

/**
 * \brief GPIO Configuration register bit definitions
 *
 */
enum gpio_cfg_bits_t{
    GPIO_CFG_GPIO0_PUSHPULL_INDEX = 16U, /*< GPIO0 push/pull or open-drain */
    GPIO_CFG_GPIO1_PUSHPULL_INDEX = 17U, /*< GPIO1 push/pull or open-drain */
    GPIO_CFG_GPIO2_PUSHPULL_INDEX = 18U, /*< GPIO2 push/pull or open-drain */
    GPIO_CFG_GPIO0_LED_INDEX = 28U,      /*< GPIO0 set to LED1 */
    GPIO_CFG_GPIO1_LED_INDEX = 29U,      /*< GPIO1 set to LED2 */
    GPIO_CFG_GPIO2_LED_INDEX = 30U       /*< GPIO2 set to LED3 */
};


static void fill_tx_fifo(const struct smsc9220_eth_dev_t* dev,
                               uint8_t *data, uint32_t size_bytes)
{
    struct smsc9220_eth_reg_map_t* register_map =
            (struct smsc9220_eth_reg_map_t*)dev->cfg->base;

    uint32_t tx_data_port_tmp = 0;
    uint8_t *tx_data_port_tmp_ptr = (uint8_t *)&tx_data_port_tmp;

    /*If the data length is not a multiple of 4, then the beginning of the first
     * DWORD of the TX DATA FIFO gets filled up with zeros and a byte offset is
     * set accordingly to guarantee proper transmission.*/
    uint32_t remainder_bytes = (size_bytes % 4);
    uint32_t filler_bytes = (4 - remainder_bytes);
    for(uint32_t i = 0; i < 4; i++){
        if(i < filler_bytes){
            tx_data_port_tmp_ptr[i] = 0;
        } else {
            tx_data_port_tmp_ptr[i] = data[i-filler_bytes];
        }
    }
    register_map->tx_data_port = tx_data_port_tmp;
    size_bytes -= remainder_bytes;
    data += remainder_bytes;

    while (size_bytes > 0) {
        /* Keep the same endianness in data as in the temp variable */
        tx_data_port_tmp_ptr[0] = data[0];
        tx_data_port_tmp_ptr[1] = data[1];
        tx_data_port_tmp_ptr[2] = data[2];
        tx_data_port_tmp_ptr[3] = data[3];
        register_map->tx_data_port = tx_data_port_tmp;
        data += 4;
        size_bytes -= 4;
    }
}

static void empty_rx_fifo(const struct smsc9220_eth_dev_t* dev,
                               uint8_t *data, uint32_t size_bytes)
{
    struct smsc9220_eth_reg_map_t* register_map =
            (struct smsc9220_eth_reg_map_t*)dev->cfg->base;

    uint32_t rx_data_port_tmp = 0;
    uint8_t *rx_data_port_tmp_ptr = (uint8_t *)&rx_data_port_tmp;

    uint32_t remainder_bytes = (size_bytes % 4);
    size_bytes -= remainder_bytes;

    while (size_bytes > 0) {
        /* Keep the same endianness in data as in the temp variable */
        rx_data_port_tmp = register_map->rx_data_port;
        data[0] = rx_data_port_tmp_ptr[0];
        data[1] = rx_data_port_tmp_ptr[1];
        data[2] = rx_data_port_tmp_ptr[2];
        data[3] = rx_data_port_tmp_ptr[3];
        data += 4;
        size_bytes -= 4;
    }

    rx_data_port_tmp = register_map->rx_data_port;
    for(uint32_t i = 0; i < remainder_bytes; i++) {
        data[i] = rx_data_port_tmp_ptr[i];
    }
}

enum smsc9220_error_t smsc9220_mac_regread(
        const struct smsc9220_eth_dev_t* dev,
        enum smsc9220_mac_reg_offsets_t regoffset,
        uint32_t *data)
{
    volatile uint32_t val;
    uint32_t maccmd = GET_BIT_FIELD(regoffset,
                                        MAC_CSR_CMD_ADDRESS_MASK, 0);
    uint32_t time_out = REG_WRITE_TIME_OUT_MS;

    struct smsc9220_eth_reg_map_t* register_map =
            (struct smsc9220_eth_reg_map_t*)dev->cfg->base;

    /* Make sure there's no pending operation */
    if(!(GET_BIT(register_map->mac_csr_cmd, MAC_CSR_CMD_BUSY_INDEX))) {
        SET_BIT(maccmd, MAC_CSR_CMD_RW_INDEX);
        SET_BIT(maccmd, MAC_CSR_CMD_BUSY_INDEX);
        register_map->mac_csr_cmd = maccmd;    /* Start operation */

        do {
            val = register_map->byte_test;  /* A no-op read. */
            (void)val;
            if (dev->data->wait_ms) {
                dev->data->wait_ms(1);
            }
            time_out--;
        } while(time_out &&
                GET_BIT(register_map->mac_csr_cmd,MAC_CSR_CMD_BUSY_INDEX));

        if(!time_out) {
            return SMSC9220_ERROR_TIMEOUT;
        }
        else {
            *data = register_map->mac_csr_data;
        }
    } else {
        return SMSC9220_ERROR_BUSY;
    }
    return SMSC9220_ERROR_NONE;
}

enum smsc9220_error_t  smsc9220_mac_regwrite(
        const struct smsc9220_eth_dev_t* dev,
        enum smsc9220_mac_reg_offsets_t regoffset,
        uint32_t data)
{
    volatile uint32_t read = 0;
    uint32_t maccmd = GET_BIT_FIELD(regoffset,
                                        MAC_CSR_CMD_ADDRESS_MASK, 0);
    uint32_t time_out = REG_WRITE_TIME_OUT_MS;

    struct smsc9220_eth_reg_map_t* register_map =
            (struct smsc9220_eth_reg_map_t*)dev->cfg->base;

    /* Make sure there's no pending operation */
    if(!GET_BIT(register_map->mac_csr_cmd, MAC_CSR_CMD_BUSY_INDEX)) {
        register_map->mac_csr_data = data;      /* Store data. */
        CLR_BIT(maccmd, MAC_CSR_CMD_RW_INDEX);
        SET_BIT(maccmd, MAC_CSR_CMD_BUSY_INDEX);
        register_map->mac_csr_cmd = maccmd;

        do {
            read = register_map->byte_test;     /* A no-op read. */
            (void)read;
            if (dev->data->wait_ms) {
                dev->data->wait_ms(1);
            }
            time_out--;
        } while(time_out &&
                (register_map->mac_csr_cmd &
                 GET_BIT(register_map->mac_csr_cmd, MAC_CSR_CMD_BUSY_INDEX)));

        if(!time_out) {
            return SMSC9220_ERROR_TIMEOUT;
        }
    } else {
       return SMSC9220_ERROR_BUSY;
    }
    return SMSC9220_ERROR_NONE;
}

enum smsc9220_error_t smsc9220_phy_regread(
        const struct smsc9220_eth_dev_t* dev,
        enum phy_reg_offsets_t regoffset,
        uint32_t *data)
{
    uint32_t val = 0;
    uint32_t phycmd = 0;
    uint32_t time_out = REG_WRITE_TIME_OUT_MS;

    if (smsc9220_mac_regread(dev, SMSC9220_MAC_REG_OFFSET_MII_ACC, &val)) {
        return SMSC9220_ERROR_INTERNAL;
    }

    if(!GET_BIT(val, MAC_REG_MII_ACC_BUSY_INDEX)) {
        phycmd = 0;
        SET_BIT(phycmd, MAC_REG_MII_ACC_PHYADDR_INDEX);
        SET_BIT_FIELD(phycmd, MAC_REG_MII_ACC_MII_REG_MASK,
                      MAC_REG_MII_ACC_MII_REG_OFFSET, regoffset);
        CLR_BIT(phycmd, MAC_REG_MII_ACC_WRITE_INDEX);
        SET_BIT(phycmd, MAC_REG_MII_ACC_BUSY_INDEX);

        if (smsc9220_mac_regwrite(dev, SMSC9220_MAC_REG_OFFSET_MII_ACC,
                                  phycmd)) {
            return SMSC9220_ERROR_INTERNAL;
        }

        val = 0;
        do {
            if (dev->data->wait_ms) {
                dev->data->wait_ms(1);
            }
            time_out--;
            if (smsc9220_mac_regread(dev, SMSC9220_MAC_REG_OFFSET_MII_ACC,
                &val)) {
                return SMSC9220_ERROR_INTERNAL;
            }
        } while(time_out && (GET_BIT(val, MAC_REG_MII_ACC_BUSY_INDEX)));

        if (!time_out) {
            return SMSC9220_ERROR_TIMEOUT;
        } else if (smsc9220_mac_regread(dev, SMSC9220_MAC_REG_OFFSET_MII_DATA,
                   data)) {
            return SMSC9220_ERROR_INTERNAL;
        }
    } else {
        return SMSC9220_ERROR_BUSY;
    }
    return SMSC9220_ERROR_NONE;
}

enum smsc9220_error_t smsc9220_phy_regwrite(
        const struct smsc9220_eth_dev_t* dev,
        enum phy_reg_offsets_t regoffset,
        uint32_t data)
{
    uint32_t val = 0;
    uint32_t phycmd = 0;
    uint32_t time_out = REG_WRITE_TIME_OUT_MS;

    if (smsc9220_mac_regread(dev, SMSC9220_MAC_REG_OFFSET_MII_ACC, &val)) {
        return SMSC9220_ERROR_INTERNAL;
    }

    if(!GET_BIT(val, MAC_REG_MII_ACC_BUSY_INDEX)) {
        /* Load the data */
        if (smsc9220_mac_regwrite(dev, SMSC9220_MAC_REG_OFFSET_MII_DATA,
                                 (data & 0xFFFF))) {
            return SMSC9220_ERROR_INTERNAL;
        }
        phycmd = 0;
        SET_BIT(phycmd, MAC_REG_MII_ACC_PHYADDR_INDEX);
        SET_BIT_FIELD(phycmd, MAC_REG_MII_ACC_MII_REG_MASK,
                      MAC_REG_MII_ACC_MII_REG_OFFSET, regoffset);
        SET_BIT(phycmd, MAC_REG_MII_ACC_WRITE_INDEX);
        SET_BIT(phycmd, MAC_REG_MII_ACC_BUSY_INDEX);
        /* Start operation */
        if (smsc9220_mac_regwrite(dev, SMSC9220_MAC_REG_OFFSET_MII_ACC,
                                  phycmd)) {
            return SMSC9220_ERROR_INTERNAL;
        }

        phycmd = 0;

        do {
            if (dev->data->wait_ms) {
                dev->data->wait_ms(1);
            }
            time_out--;
            if (smsc9220_mac_regread(dev, SMSC9220_MAC_REG_OFFSET_MII_ACC,
                                     &phycmd)){
                return SMSC9220_ERROR_INTERNAL;
            }
        } while(time_out &&  GET_BIT(phycmd, 0));

        if (!time_out) {
            return SMSC9220_ERROR_TIMEOUT;
        }

    } else {
        return SMSC9220_ERROR_BUSY;
    }
    return SMSC9220_ERROR_NONE;
}

uint32_t smsc9220_read_id(const struct smsc9220_eth_dev_t* dev)
{
    struct smsc9220_eth_reg_map_t* register_map =
            (struct smsc9220_eth_reg_map_t*)dev->cfg->base;

    return register_map->id_revision;
}

enum smsc9220_error_t  smsc9220_soft_reset(
        const struct smsc9220_eth_dev_t* dev)
{
    uint32_t time_out = RESET_TIME_OUT_MS;

    struct smsc9220_eth_reg_map_t* register_map =
            (struct smsc9220_eth_reg_map_t*)dev->cfg->base;

    /* Soft reset */
    SET_BIT(register_map->hw_cfg, HW_CFG_REG_SRST_INDEX);

    do {
        if (dev->data->wait_ms) {
            dev->data->wait_ms(1);
        }
        time_out--;
    } while(time_out &&
            GET_BIT(register_map->hw_cfg, HW_CFG_REG_SRST_TIMEOUT_INDEX));

    if (!time_out) {
        return SMSC9220_ERROR_TIMEOUT;
    }

    return SMSC9220_ERROR_NONE;
}

void smsc9220_set_txfifo(const struct smsc9220_eth_dev_t* dev,
                         uint32_t val)
{
    struct smsc9220_eth_reg_map_t* register_map =
            (struct smsc9220_eth_reg_map_t*)dev->cfg->base;

    if(val >= HW_CFG_REG_TX_FIFO_SIZE_MIN &&
       val <= HW_CFG_REG_TX_FIFO_SIZE_MAX) {
       register_map->hw_cfg = val << HW_CFG_REG_TX_FIFO_SIZE_POS;
    }
}

enum smsc9220_error_t smsc9220_set_fifo_level_irq(
        const struct smsc9220_eth_dev_t* dev,
        enum smsc9220_fifo_level_irq_pos_t irq_level_pos,
        uint32_t level)
{
    struct smsc9220_eth_reg_map_t* register_map =
            (struct smsc9220_eth_reg_map_t*)dev->cfg->base;

    if (level < SMSC9220_FIFO_LEVEL_IRQ_LEVEL_MIN ||
        level > SMSC9220_FIFO_LEVEL_IRQ_LEVEL_MAX) {
        return SMSC9220_ERROR_PARAM;
    }

    CLR_BIT_FIELD(register_map->fifo_level_irq, SMSC9220_FIFO_LEVEL_IRQ_MASK,
                  irq_level_pos, SMSC9220_FIFO_LEVEL_IRQ_MASK);
    SET_BIT_FIELD(register_map->fifo_level_irq, SMSC9220_FIFO_LEVEL_IRQ_MASK,
                  irq_level_pos, level);
    return SMSC9220_ERROR_NONE;
}

enum smsc9220_error_t smsc9220_wait_eeprom(
        const struct smsc9220_eth_dev_t* dev)
{
    uint32_t time_out = REG_WRITE_TIME_OUT_MS;

    struct smsc9220_eth_reg_map_t* register_map =
            (struct smsc9220_eth_reg_map_t*)dev->cfg->base;

    do {
        if (dev->data->wait_ms) {
            dev->data->wait_ms(1);
        }
        time_out--;
    } while(time_out &&
            GET_BIT(register_map->eeprom_cmd, EEPROM_CMD_REG_BUSY_INDEX));

    if (!time_out) {
        return SMSC9220_ERROR_TIMEOUT;
    }

    return SMSC9220_ERROR_NONE;
}

void smsc9220_init_irqs(const struct smsc9220_eth_dev_t* dev)
{
    struct smsc9220_eth_reg_map_t* register_map =
            (struct smsc9220_eth_reg_map_t*)dev->cfg->base;

    smsc9220_disable_all_interrupts(dev);
    smsc9220_clear_all_interrupts(dev);

    /* Set IRQ deassertion interval */
    SET_BIT_FIELD(register_map->irq_cfg, IRQ_CFG_INT_DEAS_MASK,
                  IRQ_CFG_INT_DEAS_POS, IRQ_CFG_INT_DEAS_10US);

    /* enable interrupts */
    SET_BIT(register_map->irq_cfg, IRQ_CFG_IRQ_EN_INDEX);
}

enum smsc9220_error_t smsc9220_check_phy(const struct smsc9220_eth_dev_t* dev)
{
    uint32_t phyid1 = 0;
    uint32_t phyid2 = 0;

    if (smsc9220_phy_regread(dev, SMSC9220_PHY_REG_OFFSET_ID1,&phyid1)) {
        return SMSC9220_ERROR_INTERNAL;
    }
    if (smsc9220_phy_regread(dev, SMSC9220_PHY_REG_OFFSET_ID2,&phyid2)) {
        return SMSC9220_ERROR_INTERNAL;
    }
    if ((phyid1 == 0xFFFF && phyid2 == 0xFFFF) ||
            (phyid1 == 0x0 && phyid2 == 0x0)) {
        return SMSC9220_ERROR_INTERNAL;
    }
    return SMSC9220_ERROR_NONE;
}

enum smsc9220_error_t smsc9220_reset_phy(const struct smsc9220_eth_dev_t* dev)
{
    uint32_t read = 0;

    if(smsc9220_phy_regread(dev, SMSC9220_PHY_REG_OFFSET_BCTRL, &read)) {
        return SMSC9220_ERROR_INTERNAL;
    }

    SET_BIT(read, PHY_REG_BCTRL_RESET_INDEX);
    if(smsc9220_phy_regwrite(dev, SMSC9220_PHY_REG_OFFSET_BCTRL, read)) {
        return SMSC9220_ERROR_INTERNAL;
    }
    return SMSC9220_ERROR_NONE;
}

void smsc9220_advertise_cap(const struct smsc9220_eth_dev_t* dev)
{
    uint32_t aneg_adv = 0;
    smsc9220_phy_regread(dev, SMSC9220_PHY_REG_OFFSET_ANEG_ADV, &aneg_adv);

    SET_BIT(aneg_adv, ANEG_10_BASE_T_INDEX);
    SET_BIT(aneg_adv, ANEG_10_BASE_T_FULL_DUPL_INDEX);
    SET_BIT(aneg_adv, ANEG_100_BASE_TX_INDEX);
    SET_BIT(aneg_adv, ANEG_100_BASE_TX_FULL_DUPL_INDEX);
    SET_BIT(aneg_adv, ANEG_SYMM_PAUSE_INDEX);
    SET_BIT(aneg_adv, ANEG_ASYMM_PAUSE_INDEX);

    smsc9220_phy_regwrite(dev, SMSC9220_PHY_REG_OFFSET_ANEG_ADV, aneg_adv);
}

void smsc9220_enable_xmit(const struct smsc9220_eth_dev_t* dev)
{
    struct smsc9220_eth_reg_map_t* register_map =
            (struct smsc9220_eth_reg_map_t*)dev->cfg->base;

    SET_BIT(register_map->tx_cfg, TX_CFG_ON_INDEX);
}

void smsc9220_disable_xmit(const struct smsc9220_eth_dev_t* dev)
{
    struct smsc9220_eth_reg_map_t* register_map =
            (struct smsc9220_eth_reg_map_t*)dev->cfg->base;

    CLR_BIT(register_map->tx_cfg, TX_CFG_ON_INDEX);
}

void smsc9220_enable_mac_xmit(const struct smsc9220_eth_dev_t* dev)
{
    uint32_t mac_cr = 0;
    smsc9220_mac_regread(dev, SMSC9220_MAC_REG_OFFSET_CR, &mac_cr);

    SET_BIT(mac_cr, MAC_REG_CR_TXEN_INDEX);

    smsc9220_mac_regwrite(dev, SMSC9220_MAC_REG_OFFSET_CR, mac_cr);
}

void smsc9220_disable_mac_xmit(const struct smsc9220_eth_dev_t* dev)
{
    uint32_t mac_cr = 0;
    smsc9220_mac_regread(dev, SMSC9220_MAC_REG_OFFSET_CR, &mac_cr);

    CLR_BIT(mac_cr, MAC_REG_CR_TXEN_INDEX);

    smsc9220_mac_regwrite(dev, SMSC9220_MAC_REG_OFFSET_CR, mac_cr);
}

void smsc9220_enable_mac_recv(const struct smsc9220_eth_dev_t* dev)
{
    uint32_t mac_cr = 0;
    smsc9220_mac_regread(dev, SMSC9220_MAC_REG_OFFSET_CR, &mac_cr);

    SET_BIT(mac_cr, MAC_REG_CR_RXEN_INDEX);

    smsc9220_mac_regwrite(dev, SMSC9220_MAC_REG_OFFSET_CR, mac_cr);
}

void smsc9220_disable_mac_recv(const struct smsc9220_eth_dev_t* dev)
{
    uint32_t mac_cr = 0;
    smsc9220_mac_regread(dev, SMSC9220_MAC_REG_OFFSET_CR, &mac_cr);

    CLR_BIT(mac_cr, MAC_REG_CR_RXEN_INDEX);

    smsc9220_mac_regwrite(dev, SMSC9220_MAC_REG_OFFSET_CR, mac_cr);
}

int smsc9220_check_id(const struct smsc9220_eth_dev_t* dev)
{
    uint32_t id = smsc9220_read_id(dev);

    return ((GET_BIT_FIELD(id, CHIP_ID_MASK, CHIP_ID_POS) == CHIP_ID) ? 0 : 1);
}

void smsc9220_enable_interrupt(const struct smsc9220_eth_dev_t* dev,
                               enum smsc9220_interrupt_source source)
{
    struct smsc9220_eth_reg_map_t* register_map =
            (struct smsc9220_eth_reg_map_t*)dev->cfg->base;

    SET_BIT(register_map->irq_enable, source);
}

void smsc9220_disable_interrupt(const struct smsc9220_eth_dev_t* dev,
                                enum smsc9220_interrupt_source source)
{
    struct smsc9220_eth_reg_map_t* register_map =
            (struct smsc9220_eth_reg_map_t*)dev->cfg->base;

    CLR_BIT(register_map->irq_enable, source);
}

void smsc9220_disable_all_interrupts(const struct smsc9220_eth_dev_t* dev)
{
    struct smsc9220_eth_reg_map_t* register_map =
            (struct smsc9220_eth_reg_map_t*)dev->cfg->base;

    register_map->irq_enable = 0;
}

void smsc9220_clear_interrupt(const struct smsc9220_eth_dev_t* dev,
                              enum smsc9220_interrupt_source source)
{
    struct smsc9220_eth_reg_map_t* register_map =
            (struct smsc9220_eth_reg_map_t*)dev->cfg->base;

    SET_BIT(register_map->irq_status, source);
}

void smsc9220_clear_all_interrupts(const struct smsc9220_eth_dev_t* dev)
{
    struct smsc9220_eth_reg_map_t* register_map =
            (struct smsc9220_eth_reg_map_t*)dev->cfg->base;

    register_map->irq_status = UINT32_MAX;
}

int smsc9220_get_interrupt(const struct smsc9220_eth_dev_t* dev,
                           enum smsc9220_interrupt_source source)
{
    struct smsc9220_eth_reg_map_t* register_map =
            (struct smsc9220_eth_reg_map_t*)dev->cfg->base;

    return GET_BIT(register_map->irq_status, source);
}

void smsc9220_establish_link(const struct smsc9220_eth_dev_t* dev)
{
    uint32_t bcr = 0;
    struct smsc9220_eth_reg_map_t* register_map =
            (struct smsc9220_eth_reg_map_t*)dev->cfg->base;

    smsc9220_phy_regread(dev, SMSC9220_PHY_REG_OFFSET_BCTRL, &bcr);
    SET_BIT(bcr, PHY_REG_BCTRL_AUTO_NEG_EN_INDEX);
    SET_BIT(bcr, PHY_REG_BCTRL_RST_AUTO_NEG_INDEX);
    smsc9220_phy_regwrite(dev, SMSC9220_PHY_REG_OFFSET_BCTRL, bcr);

    SET_BIT(register_map->hw_cfg, HW_CFG_REG_MUST_BE_ONE_INDEX);
}

enum smsc9220_error_t smsc9220_read_mac_address(
        const struct smsc9220_eth_dev_t* dev, char *mac)
{
    uint32_t mac_low = 0;
    uint32_t mac_high = 0;

    if(!mac) {
        return SMSC9220_ERROR_PARAM;
    }

    if (smsc9220_mac_regread(dev, SMSC9220_MAC_REG_OFFSET_ADDRH, &mac_high)) {
        return SMSC9220_ERROR_INTERNAL;
    }
    if (smsc9220_mac_regread(dev, SMSC9220_MAC_REG_OFFSET_ADDRL, &mac_low)) {
        return SMSC9220_ERROR_INTERNAL;
    }
    mac[0] = mac_low & 0xFF;
    mac[1] = (mac_low >> 8) & 0xFF;
    mac[2] = (mac_low >> 16) & 0xFF;
    mac[3] = (mac_low >> 24) & 0xFF;
    mac[4] = mac_high & 0xFF;
    mac[5] = (mac_high >> 8) & 0xFF;

    return SMSC9220_ERROR_NONE;
}

uint32_t smsc9220_get_tx_data_fifo_size(
        const struct smsc9220_eth_dev_t* dev)
{
    struct smsc9220_eth_reg_map_t* register_map =
            (struct smsc9220_eth_reg_map_t*)dev->cfg->base;

    uint32_t tx_fifo_size =
            GET_BIT_FIELD(register_map->hw_cfg,
                    TX_DATA_FIFO_SIZE_KBYTES_MASK,
                    TX_DATA_FIFO_SIZE_KBYTES_POS) * KBYTES_TO_BYTES_MULTIPLIER;

    return (tx_fifo_size - TX_STATUS_FIFO_SIZE_BYTES);
}

enum smsc9220_error_t smsc9220_init(
        const struct smsc9220_eth_dev_t* dev,
        void(* wait_ms_function)(uint32_t))
{
    uint32_t phyreset = 0;
    enum smsc9220_error_t error = SMSC9220_ERROR_NONE;
    struct smsc9220_eth_reg_map_t* register_map =
            (struct smsc9220_eth_reg_map_t*)dev->cfg->base;

    if (!wait_ms_function) {
        return SMSC9220_ERROR_PARAM;
    }
    dev->data->wait_ms = wait_ms_function;

    error = smsc9220_check_id(dev);
    if(error != SMSC9220_ERROR_NONE) {
        return error;
    }

    error = smsc9220_soft_reset(dev);
    if(error != SMSC9220_ERROR_NONE) {
        return error;
    }

    smsc9220_set_txfifo(dev, HW_CFG_REG_TX_FIFO_SIZE);

    SET_BIT_FIELD(register_map->afc_cfg, AFC_BACK_DUR_MASK,
                  AFC_BACK_DUR_POS, AFC_BACK_DUR);
    SET_BIT_FIELD(register_map->afc_cfg, AFC_LOW_LEVEL_MASK,
                  AFC_LOW_LEVEL_POS, AFC_LOW_LEVEL);
    SET_BIT_FIELD(register_map->afc_cfg, AFC_HIGH_LEVEL_MASK,
                  AFC_HIGH_LEVEL_POS, AFC_HIGH_LEVEL);

    error = smsc9220_wait_eeprom(dev);
    if(error != SMSC9220_ERROR_NONE) {
        return error;
    }

    /* Configure GPIOs as LED outputs. */
    register_map->gpio_cfg = 0;
    SET_BIT(register_map->gpio_cfg, GPIO_CFG_GPIO0_PUSHPULL_INDEX);
    SET_BIT(register_map->gpio_cfg, GPIO_CFG_GPIO1_PUSHPULL_INDEX);
    SET_BIT(register_map->gpio_cfg, GPIO_CFG_GPIO2_PUSHPULL_INDEX);
    SET_BIT(register_map->gpio_cfg, GPIO_CFG_GPIO0_LED_INDEX);
    SET_BIT(register_map->gpio_cfg, GPIO_CFG_GPIO1_LED_INDEX);
    SET_BIT(register_map->gpio_cfg, GPIO_CFG_GPIO2_LED_INDEX);

    smsc9220_init_irqs(dev);

    /* Configure MAC addresses here if needed. */

    error = smsc9220_check_phy(dev);
    if(error != SMSC9220_ERROR_NONE) {
        return error;
    }

    error = smsc9220_reset_phy(dev);
    if(error != SMSC9220_ERROR_NONE) {
        return error;
    }

    if (dev->data->wait_ms) {
        dev->data->wait_ms(PHY_RESET_TIME_OUT_MS);
    }
    /* Checking whether phy reset completed successfully.*/
    error = smsc9220_phy_regread(dev, SMSC9220_PHY_REG_OFFSET_BCTRL,
                                &phyreset);
    if(error != SMSC9220_ERROR_NONE) {
        return error;
    }

    if(GET_BIT(phyreset, PHY_REG_BCTRL_RESET_INDEX)) {
        return SMSC9220_ERROR_INTERNAL;
    }

    smsc9220_advertise_cap(dev);
    smsc9220_establish_link(dev);

    smsc9220_enable_mac_xmit(dev);
    smsc9220_enable_xmit(dev);
    smsc9220_enable_mac_recv(dev);

    /* This sleep is compulsory otherwise txmit/receive will fail. */
    if (dev->data->wait_ms) {
        dev->data->wait_ms(INIT_FINISH_DELAY);
    }
    dev->data->state = 1;

    return SMSC9220_ERROR_NONE;
}

enum smsc9220_error_t smsc9220_send_by_chunks(
                            const struct smsc9220_eth_dev_t* dev,
                            uint32_t total_payload_length,
                            bool is_new_packet,
                            const char *data, uint32_t current_size)
{
    struct smsc9220_eth_reg_map_t* register_map =
            (struct smsc9220_eth_reg_map_t*)dev->cfg->base;
    bool is_first_segment = false;
    bool is_last_segment = false;
    uint32_t txcmd_a, txcmd_b = 0;
    uint32_t tx_buffer_free_space = 0;
    volatile uint32_t xmit_stat = 0;

    if (!data) {
        return SMSC9220_ERROR_PARAM;
    }

    if (is_new_packet) {
        is_first_segment = true;
        dev->data->ongoing_packet_length = total_payload_length;
        dev->data->ongoing_packet_length_sent = 0;
    } else if (dev->data->ongoing_packet_length != total_payload_length ||
             dev->data->ongoing_packet_length_sent >= total_payload_length) {
        return SMSC9220_ERROR_PARAM;
    }

    /* Would next chunk fit into buffer? */
    tx_buffer_free_space = GET_BIT_FIELD(register_map->tx_fifo_inf,
                                         FIFO_USED_SPACE_MASK,
                                         DATA_FIFO_USED_SPACE_POS);
    if (current_size > tx_buffer_free_space) {
        return SMSC9220_ERROR_INTERNAL; /* Not enough space in FIFO */
    }
    if ((dev->data->ongoing_packet_length_sent + current_size) ==
         total_payload_length) {
        is_last_segment = true;
    }

    txcmd_a = 0;
    txcmd_b = 0;

    if (is_last_segment) {
        SET_BIT(txcmd_a, TX_COMMAND_A_LAST_SEGMENT_INDEX);
    }
    if (is_first_segment) {
        SET_BIT(txcmd_a, TX_COMMAND_A_FIRST_SEGMENT_INDEX);
    }

    uint32_t data_start_offset_bytes = (4 - (current_size % 4));

    SET_BIT_FIELD(txcmd_a, TX_CMD_PKT_LEN_BYTES_MASK, 0, current_size);
    SET_BIT_FIELD(txcmd_a, TX_CMD_DATA_START_OFFSET_BYTES_MASK,
                           TX_CMD_DATA_START_OFFSET_BYTES_POS,
                           data_start_offset_bytes);

    SET_BIT_FIELD(txcmd_b, TX_CMD_PKT_LEN_BYTES_MASK, 0, current_size);
    SET_BIT_FIELD(txcmd_b, TX_CMD_PKT_TAG_MASK, TX_CMD_PKT_TAG_POS,
                  current_size);

    register_map->tx_data_port = txcmd_a;
    register_map->tx_data_port = txcmd_b;

    fill_tx_fifo(dev, (uint8_t *)data, current_size);

    if (is_last_segment) {
        /* Pop status port for error check */
        xmit_stat = register_map->tx_status_port;
        (void)xmit_stat;
    }
    dev->data->ongoing_packet_length_sent += current_size;
    return SMSC9220_ERROR_NONE;
}

uint32_t smsc9220_get_rxfifo_data_used_space(const struct
                                                 smsc9220_eth_dev_t* dev)
{
    struct smsc9220_eth_reg_map_t* register_map =
            (struct smsc9220_eth_reg_map_t*)dev->cfg->base;

    return GET_BIT_FIELD(register_map->rx_fifo_inf, FIFO_USED_SPACE_MASK,
                         DATA_FIFO_USED_SPACE_POS);
}

uint32_t smsc9220_receive_by_chunks(const struct smsc9220_eth_dev_t* dev,
                                        char *data, uint32_t dlen)
{
    uint32_t rxfifo_inf = 0;
    uint32_t rxfifo_stat = 0;
    uint32_t packet_length_byte = 0;
    struct smsc9220_eth_reg_map_t* register_map =
            (struct smsc9220_eth_reg_map_t*)dev->cfg->base;

    if (!data) {
        return 0; /* Invalid input parameter, cannot read */
    }
    rxfifo_inf = register_map->rx_fifo_inf;

    if(rxfifo_inf & 0xFFFF) { /* If there's data */
        rxfifo_stat = register_map->rx_status_port;
        if(rxfifo_stat != 0) {   /* Fetch status of this packet */
                /* Ethernet controller is padding to 32bit aligned data */
                packet_length_byte = GET_BIT_FIELD(rxfifo_stat,
                                        RX_FIFO_STATUS_PKT_LENGTH_MASK,
                                        RX_FIFO_STATUS_PKT_LENGTH_POS);
                packet_length_byte -= 4;
                dev->data->current_rx_size_words = packet_length_byte;
        }
    }

    empty_rx_fifo(dev, (uint8_t *)data, packet_length_byte);
    dev->data->current_rx_size_words = 0;
    return packet_length_byte;
}

uint32_t smsc9220_peek_next_packet_size(const struct
                                            smsc9220_eth_dev_t* dev)
{
    uint32_t packet_size = 0;
    struct smsc9220_eth_reg_map_t* register_map =
            (struct smsc9220_eth_reg_map_t*)dev->cfg->base;

    if(smsc9220_get_rxfifo_data_used_space(dev)) {
        packet_size = GET_BIT_FIELD(register_map->rx_status_peek,
                                    RX_FIFO_STATUS_PKT_LENGTH_MASK,
                                    RX_FIFO_STATUS_PKT_LENGTH_POS);
    }
    return packet_size;
}