aos/usr/drivers/enet/enet_module.c
2022-06-02 20:43:17 +00:00

708 lines
18 KiB
C

/**
* \file
* \brief imx8 NIC driver module
*/
/*
* Copyright (c) 2019, ETH Zurich.
* All rights reserved.
*
* This file is distributed under the terms in the attached LICENSE file.
* If you do not find this file, copies can be found by writing to:
* ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <devif/queue_interface_backend.h>
#include <devif/backends/net/enet_devif.h>
#include <aos/aos.h>
#include <aos/deferred.h>
#include <driverkit/driverkit.h>
#include <dev/imx8x/enet_dev.h>
#include <maps/imx8x_map.h>
#include <netutil/etharp.h>
#include <netutil/types.h>
#include "enet.h"
#define PHY_ID 0x2
static errval_t enet_write_mdio(struct enet_driver_state* st, int8_t phyaddr,
int8_t regaddr, int16_t data)
{
// Some protocol ...
enet_mmfr_t reg = 0;
reg = enet_mmfr_pa_insert(reg, phyaddr);
reg = enet_mmfr_ra_insert(reg, regaddr);
reg = enet_mmfr_data_insert(reg, data);
reg = enet_mmfr_st_insert(reg, 0x1);
reg = enet_mmfr_ta_insert(reg, 0x2);
// 1 is write 2 is read
reg = enet_mmfr_op_insert(reg, 0x1);
ENET_DEBUG("Write MDIO: write cmd %lx \n", reg);
enet_mmfr_wr(st->d, reg);
uint16_t tries = 1000;
while (!(enet_eir_mii_rdf(st->d) & 0x1)) {
tries--;
//barrelfish_usleep(10);
if (tries == 0) {
return ENET_ERR_MDIO_WRITE;
}
}
enet_eir_mii_wrf(st->d, 0x1);
return SYS_ERR_OK;
}
static errval_t enet_read_mdio(struct enet_driver_state* st, int8_t phyaddr,
int8_t regaddr, int16_t *data)
{
// Some protocol ...
enet_eir_mii_wrf(st->d, 0x1);
enet_mmfr_t reg = 0;
reg = enet_mmfr_pa_insert(reg, phyaddr);
reg = enet_mmfr_ra_insert(reg, regaddr);
reg = enet_mmfr_st_insert(reg, 0x1);
reg = enet_mmfr_ta_insert(reg, 0x2);
// 1 is write 2 is read
reg = enet_mmfr_op_insert(reg, 0x2);
enet_mmfr_wr(st->d, reg);
ENET_DEBUG("Read MDIO: read cmd %lx \n", reg);
uint16_t tries = 1000;
while (!(enet_eir_mii_rdf(st->d) & 0x1)) {
barrelfish_usleep(10);
tries--;
if (tries == 0) {
return ENET_ERR_MDIO_WRITE;
}
}
enet_eir_mii_wrf(st->d, 0x1);
*data = enet_mmfr_data_rdf(st->d);
return SYS_ERR_OK;
}
static errval_t enet_get_phy_id(struct enet_driver_state* st)
{
errval_t err;
int16_t data;
uint32_t phy_id;
// get phy ID1
err = enet_read_mdio(st, PHY_ID, 0x2, &data);
if (err_is_fail(err)) {
return err;
}
phy_id = data << 16;
// get phy ID2
err = enet_read_mdio(st, PHY_ID, 0x3, &data);
if (err_is_fail(err)) {
return err;
}
phy_id |= data;
st->phy_id = phy_id;
return err;
}
#define PHY_RESET 0x8000
#define PHY_RESET_CMD 0x0
#define PHY_STATUS_CMD 0x1
#define PHY_AUTONEG_CMD 0x4
#define PHY_LPA_CMD 0x5
#define PHY_CTRL1000_CMD 0x09
#define PHY_STAT1000_CMD 0x0a
static errval_t enet_reset_phy(struct enet_driver_state* st)
{
errval_t err;
err = enet_write_mdio(st, PHY_ID, PHY_RESET_CMD, PHY_RESET);
if (err_is_fail(err)) {
return err;
}
int16_t data;
err = enet_read_mdio(st, PHY_ID, PHY_RESET_CMD, &data);
if (err_is_fail(err)) {
return err;
}
int timeout = 500;
while ((data & PHY_RESET) && timeout > 0) {
err = enet_read_mdio(st, PHY_ID, PHY_RESET_CMD, &data);
if (err_is_fail(err)) {
return err;
}
barrelfish_usleep(1000);
timeout--;
}
if (data & PHY_RESET) {
return ENET_ERR_PHY_RESET;
}
return SYS_ERR_OK;
}
static errval_t enet_setup_autoneg(struct enet_driver_state* st)
{
errval_t err;
int16_t status;
int16_t autoneg;
// Read BASIC MODE status register
err = enet_read_mdio(st, PHY_ID, 0x1, &status);
if (err_is_fail(err)) {
return err;
}
// READ autoneg status
err = enet_read_mdio(st, PHY_ID, PHY_AUTONEG_CMD, &autoneg);
if (err_is_fail(err)) {
return err;
}
// Read BASIC contorl register
err = enet_read_mdio(st, PHY_ID, PHY_RESET_CMD, &status);
if (err_is_fail(err)) {
return err;
}
return SYS_ERR_OK;
}
#define AUTONEG_100FULL 0x0100
#define AUTONEG_100HALF 0x0080
#define AUTONEG_10FULL 0x0040
#define AUTONEG_10HALF 0x0020
#define AUTONEG_PSB_802_3 0x0001
#define AUTONEG_ENABLE 0x1000
#define AUTONEG_RESTART 0x0200
static errval_t enet_restart_autoneg(struct enet_driver_state* st)
{
errval_t err;
err = enet_write_mdio(st, PHY_ID, PHY_RESET_CMD, PHY_RESET);
if (err_is_fail(err)) {
return err;
}
barrelfish_usleep(1000);
//barrelfish_usleep(1000);
err = enet_write_mdio(st, PHY_ID, PHY_AUTONEG_CMD,
AUTONEG_100FULL | AUTONEG_100HALF | AUTONEG_10FULL |
AUTONEG_10HALF | AUTONEG_PSB_802_3);
if (err_is_fail(err)) {
return err;
}
err = enet_write_mdio(st, PHY_ID, PHY_RESET_CMD,
AUTONEG_ENABLE | AUTONEG_RESTART);
if (err_is_fail(err)) {
return err;
}
return SYS_ERR_OK;
}
static errval_t enet_init_phy(struct enet_driver_state* st)
{
errval_t err;
err = enet_get_phy_id(st);
if (err_is_fail(err)) {
return err;
}
err = enet_reset_phy(st);
if (err_is_fail(err)) {
return err;
}
// board_phy_config in uboot driver. Don't know what
// this actually does ...
err = enet_write_mdio(st, PHY_ID, 0x1d, 0x1f);
assert(err_is_ok(err));
err = enet_write_mdio(st, PHY_ID, 0x1e, 0x8);
assert(err_is_ok(err));
err = enet_write_mdio(st, PHY_ID, 0x1d, 0x00);
assert(err_is_ok(err));
err = enet_write_mdio(st, PHY_ID, 0x1e, 0x82ee);
assert(err_is_ok(err));
err = enet_write_mdio(st, PHY_ID, 0x1d, 0x05);
assert(err_is_ok(err));
err = enet_write_mdio(st, PHY_ID, 0x1e, 0x100);
assert(err_is_ok(err));
err = enet_setup_autoneg(st);
if (err_is_fail(err)) {
return err;
}
return SYS_ERR_OK;
}
#define PHY_STATUS_LSTATUS 0x0004
#define PHY_STATUS_ANEG_COMP 0x0020
#define PHY_STATUS_ESTAT 0x0100
#define PHY_STATUS_ERCAP 0x0001
#define PHY_LPA_100HALF 0x0080
#define PHY_LPA_100FULL 0x0100
#define PHY_LPA_10FULL 0x0040
// TODO check for rest of link capabilities
static void enet_parse_link(struct enet_driver_state* st)
{
// just a sanity check if values are ok
errval_t err;
int16_t status;
err = enet_read_mdio(st, PHY_ID, PHY_STAT1000_CMD, &status);
assert(err_is_ok(err));
int16_t mii_reg;
err = enet_read_mdio(st, PHY_ID, PHY_STATUS_CMD, &mii_reg);
assert(err_is_ok(err));
if (status < 0) {
debug_printf("ENET not capable of 1G \n");
return;
} else {
err = enet_read_mdio(st, PHY_ID, PHY_CTRL1000_CMD, &status);
assert(err_is_ok(err));
if (status == 0) {
int16_t lpa, lpa2;
err = enet_read_mdio(st, PHY_ID, PHY_AUTONEG_CMD, &lpa);
assert(err_is_ok(err));
err = enet_read_mdio(st, PHY_ID, PHY_LPA_CMD, &lpa2);
assert(err_is_ok(err));
lpa &= lpa2;
if (lpa & (PHY_LPA_100FULL | PHY_LPA_100HALF)) {
if (lpa & PHY_LPA_100FULL) {
debug_printf("LINK 100 Mbit/s FULL duplex \n");
} else {
debug_printf("LINK 100 Mbit/s half\n");
}
}
}
}
}
static errval_t enet_phy_startup(struct enet_driver_state* st)
{
errval_t err;
// board_phy_config in uboot driver. Don't know what
// this actually does ...
int16_t mii_reg;
err = enet_read_mdio(st, PHY_ID, PHY_STATUS_CMD, &mii_reg);
assert(err_is_ok(err));
if (mii_reg & PHY_STATUS_LSTATUS) {
debug_printf("LINK already UP\n");
return SYS_ERR_OK;
}
if (!(mii_reg & PHY_STATUS_ANEG_COMP)) {
DEBUG_PRINTF("[enet] Starting autonegotiation \n");
while(!(mii_reg & PHY_STATUS_ANEG_COMP)) {
err = enet_read_mdio(st, PHY_ID, PHY_STATUS_CMD, &mii_reg);
assert(err_is_ok(err));
barrelfish_usleep(1000);
}
ENET_DEBUG("Autonegotation done\n");
}
enet_parse_link(st);
return SYS_ERR_OK;
}
// bool promiscous for promiscous mode.
// This will also set it so that all multicast packets will also be received!
/*
static void enet_init_multicast_filt(struct enet_driver_state* st, bool promisc)
{
if (promisc) {
enet_rcr_prom_wrf(st->d, 1);
return;
}
enet_rcr_prom_wrf(st->d, 0);
// TODO Catching all multicast packets for now
enet_gaur_wr(st->d, 0xFFFFFFFF);
enet_galr_wr(st->d, 0xFFFFFFFF);
// TODO if we do not catch all multicast packet then do this:
// crc32 value of mac address
#if 0
unsigned int crc = 0xffffffff;
unsigned char hash;
unsigned int hash_high = 0, hash_low = 0;
for (int i = 0; i < 6; i++) {
unsigned char data = ((uint8_t*) &st->mac)[i];
for (int bit = 0; bit < 8; bit++, data >>= 1) {
crc = (crc >> 1) ^ (((crc ^ data) & 1) ? ENET_CRC32_POLY : 0);
}
hash = (crc >> (32 - ENET_HASH_BITS)) & 0x3f;
if (hash > 31) {
hash_high |= 1 << (hash - 32);
} else {
hash_low |= 1 << hash;
}
}
enet_gaur_gaddr_wrf(st->d, hash_high);
enet_galr_gaddr_wrf(st->d, hash_low);
#endif
// TODO if this is M5272 then set the hash table entries to 0 ...
}
*/
static void enet_read_mac(struct enet_driver_state* st)
{
uint64_t lower = enet_palr_paddr1_rdf(st->d);
uint64_t upper = enet_paur_paddr2_rdf(st->d);
// this is weird lower seems to be the upper part of the address ..
uint64_t mac = (lower << 16) | upper;
ENET_DEBUG("MAC %lx \n", mac);
st->mac = mac;
}
static void enet_write_mac(struct enet_driver_state* st)
{
uint64_t lower = st->mac >> 16;
uint32_t upper = st->mac & 0xFFFF;
enet_palr_paddr1_wrf(st->d, lower);
enet_paur_paddr2_wrf(st->d, upper);
}
static errval_t enet_reset(struct enet_driver_state* st)
{
// reset device
ENET_DEBUG("Reset device\n");
uint64_t ecr = enet_ecr_rd(st->d);
enet_ecr_wr(st->d, ecr | 0x1);
int timeout = 500;
while ((enet_ecr_rd(st->d) & 0x1) && timeout > 0) {
barrelfish_usleep(10);
// TODO timeout
}
if (timeout <= 0) {
return ENET_ERR_DEV_RESET;
}
return SYS_ERR_OK;
}
static void enet_reg_setup(struct enet_driver_state* st)
{
// Set interrupt mask register
ENET_DEBUG("Set interrupt mask register\n");
enet_eimr_wr(st->d, 0x0);
// Clear outstanding interrupts
ENET_DEBUG("Clear outstanding interrupts\n");
enet_eir_wr(st->d, 0xFFFFFFFF);
uint64_t reg;
// TODO see if other fields are required, not in dump
reg = enet_rcr_rd(st->d);
reg = enet_rcr_loop_insert(reg, 0x0);
reg = enet_rcr_rmii_mode_insert(reg, 0x1);
reg = enet_rcr_mii_mode_insert(reg, 0x1);
reg = enet_rcr_fce_insert(reg, 0x1);
reg = enet_rcr_max_fl_insert(reg, 1522);
reg = enet_rcr_crcfwd_insert(reg, 1); // strip received Ethernet CRC
//reg = enet_rcr_prom_insert(reg, 1);
enet_rcr_wr(st->d, reg);
enet_racc_t racc = 0;
racc = enet_racc_linedis_insert(racc, 1); // discard received packets with invalid Ethernet CRC
enet_racc_wr(st->d, racc);
enet_tcr_crcfwd_wrf(st->d, 0); // add Ethernet CRC when transmitting
}
static errval_t enet_open(struct enet_driver_state *st)
{
errval_t err = SYS_ERR_OK;
// Enable full duplex, disable heartbeet
enet_tcr_fden_wrf(st->d, 0x1);
// Enable HW endian swap
enet_ecr_dbswp_wrf(st->d, 0x1);
enet_ecr_en1588_wrf(st->d, 0x0);
// Enable store and forward mode
enet_tfwr_strfwd_wrf(st->d, 0x1);
// Enable controler
enet_ecr_etheren_wrf(st->d, 0x1);
// TODO don't think this is MX25/MX53 or MX6SL
// Startup PHY
err = enet_phy_startup(st);
if (err_is_fail(err)) {
return err;
}
uint8_t speed = enet_ecr_speed_rdf(st->d);
if (!speed) {
enet_rcr_rmii_10t_wrf(st->d, 0x0);
}
//enet_activate_rx_ring(st);
ENET_DEBUG("Init done! \n");
return err;
}
static errval_t enet_init(struct enet_driver_state* st)
{
errval_t err = SYS_ERR_OK;
// set HW addreses
enet_iaur_wr(st->d, 0);
enet_ialr_wr(st->d, 0);
enet_gaur_wr(st->d, 0);
enet_galr_wr(st->d, 0);
enet_write_mac(st);
enet_reg_setup(st);
uint64_t reg;
// Set MII speed, do not drop preamble and set hold time to 10ns
reg = enet_mscr_rd(st->d);
reg = enet_mscr_mii_speed_insert(reg, 0x18);
reg = enet_mscr_hold_time_insert(reg, 0x1);
enet_mscr_wr(st->d, reg);
// Set Opcode and Pause duration
enet_opd_wr(st->d, 0x00010020);
enet_tfwr_tfwr_wrf(st->d, 0x2);
// Set multicast addr filter
enet_gaur_wr(st->d, 0);
enet_galr_wr(st->d, 0);
// Max pkt size rewrite ...
enet_mrbr_wr(st->d, 0x600);
// Tell card beginning of rx/tx rings
//enet_rdsr_wr(st->d, st->rxq->desc_mem.devaddr);
//enet_tdsr_wr(st->d, st->txq->desc_mem.devaddr);
err = enet_restart_autoneg(st);
if (err_is_fail(err)) {
return err;
}
err = enet_open(st);
if (err_is_fail(err)) {
// TODO cleanup
return err;
}
return err;
}
static errval_t enet_probe(struct enet_driver_state* st)
{
errval_t err;
err = enet_reset(st);
if (err_is_fail(err)) {
return err;
}
enet_reg_setup(st);
uint64_t reg;
// Set MII speed, do not drop preamble and set hold time to 10ns
reg = enet_mscr_rd(st->d);
reg = enet_mscr_mii_speed_insert(reg, 0x18);
reg = enet_mscr_hold_time_insert(reg, 0x1);
enet_mscr_wr(st->d, reg);
err = enet_init_phy(st);
if (err_is_fail(err)) {
debug_printf("Failed PHY reset\n");
return err;
}
// Write back mac again
ENET_DEBUG("Reset MAC\n");
// TODO do this later? NOT in dump
enet_write_mac(st);
enet_read_mac(st);
// TODO checked dump until here!
return SYS_ERR_OK;
}
struct enet_driver_state *st;
int main(int argc, char *argv[]) {
errval_t err;
DEBUG_PRINTF("Enet driver started \n");
st = (struct enet_driver_state*)calloc(1, sizeof(struct enet_driver_state));
assert(st != NULL);
/* Net Project: get the capability to the register region
* and then map it so it is accessible.
* set st->d_vaddr to the memory mapped register region */
struct capref cap_arg0 = {
.cnode = cnode_arg,
.slot = 0
};
err = paging_map_frame_attr(get_current_paging_state(),
(void **) &st->d_vaddr, IMX8X_ENET_SIZE,
cap_arg0, VREGION_FLAGS_READ_WRITE_NOCACHE);
if (err_is_fail(err)) return err;
if ((void *)st->d_vaddr == NULL) {
USER_PANIC("ENET: No register region mapped \n");
}
/* Initialize Mackerel binding */
st->d = (enet_t *) malloc(sizeof(enet_t));
enet_initialize(st->d, (void *) st->d_vaddr);
assert(st->d != NULL);
enet_read_mac(st);
err = enet_probe(st);
if (err_is_fail(err)) {
// TODO cleanup
return err;
}
err = enet_init(st);
if (err_is_fail(err)) {
// TODO cleanup
return err;
}
DEBUG_PRINTF("Enet driver init done \n");
DEBUG_PRINTF("Creating devqs \n");
err = enet_rx_queue_create(&st->rxq, st->d);
if (err_is_fail(err)) {
debug_printf("Failed creating RX devq \n");
return err;
}
err = enet_tx_queue_create(&st->txq, st->d);
if (err_is_fail(err)) {
debug_printf("Failed creating TX devq \n");
return err;
}
// Add some memory to receive stuffa
err = frame_alloc(&st->rx_mem, 512*2048, NULL);
if (err_is_fail(err)) {
return err;
}
regionid_t rid;
err = devq_register((struct devq*) st->rxq, st->rx_mem, &rid);
if (err_is_fail(err)) {
return err;
}
// Enqueue buffers
for (int i = 0; i < st->rxq->size-1; i++) {
err = devq_enqueue((struct devq*) st->rxq, rid, i*(2048), 2048,
0, 2048, 0);
if (err_is_fail(err)) {
return err;
}
}
err = frame_alloc(&st->tx_mem, 512*2048, NULL);
if (err_is_fail(err)) {
return err;
}
err = devq_register((struct devq*) st->txq, st->tx_mem, &rid);
if (err_is_fail(err)) {
return err;
}
st->tx_rid = rid;
st->tx_init_i = 0;
st->tx_alloc_queue_head = NULL;
st->tx_alloc_queue_tail = NULL;
if (argc < 3) {
printf("ENET: Error: missing address arguments\n");
return 1;
}
int a1, a2, a3, a4;
int parsed = sscanf(argv[1], "%d.%d.%d.%d.", &a1, &a2, &a3, &a4);
if (parsed != 4) {
printf("ENET: Error: failed to parse IP address\n");
return 1;
}
st->my_ip = MK_IP(a1, a2, a3, a4);
int net_bits = strtol(argv[2], NULL, 10);
if (!(0 <= net_bits && net_bits <= 32)) {
printf("ENET: Error: network bit count out of range\n");
return 1;
}
st->net_bits = net_bits;
if (net_bits == 0) st->net_mask = 0; // shift by 32 bits is undefined behavior
else st->net_mask = 0xffffffffU << (32 - net_bits);
struct eth_addr_net mac = eth_addr_wr(st->mac);
debug_printf("my MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
mac.addr[0], mac.addr[1], mac.addr[2], mac.addr[3], mac.addr[4], mac.addr[5]
);
struct uint32_net ip = uint32_wr(st->my_ip);
debug_printf("my IP: %d.%d.%d.%d\n",
ip.val[0], ip.val[1], ip.val[2], ip.val[3]
);
struct uint32_net net = uint32_wr(st->my_ip & st->net_mask);
debug_printf("net: %d.%d.%d.%d/%d\n",
net.val[0], net.val[1], net.val[2], net.val[3],
st->net_bits
);
enet_loop();
}