/** * \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 #include #include #include #include #include #include #include #include #include #include #include #include #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(); }