From 2201bc969f066545439c1c52731bcd2a6ab348e6 Mon Sep 17 00:00:00 2001 From: Julian Uziemblo Date: Fri, 6 Sep 2024 12:41:26 +0200 Subject: [PATCH] drivers/enet: Add support for ethernet in imxrt1064-evk JIRA: RTOS-507 --- _targets/Makefile.armv7a7-imx6ull | 2 +- _targets/Makefile.armv7m7-imxrt106x | 6 +- drivers/Makefile | 5 +- drivers/ephy.c | 138 +++++-- drivers/ephy.h | 4 +- drivers/gpio.h | 8 +- drivers/imx-enet-regs.h | 263 ++++++++---- drivers/imx-enet.c | 619 ++++++++++++++++++++-------- drivers/imx6ull-gpio.c | 197 ++++++++- drivers/imxrt106x-gpio.c | 258 ++++++++++++ drivers/time-util.c | 42 ++ drivers/time-util.h | 33 ++ 12 files changed, 1290 insertions(+), 285 deletions(-) create mode 100644 drivers/imxrt106x-gpio.c create mode 100644 drivers/time-util.c create mode 100644 drivers/time-util.h diff --git a/_targets/Makefile.armv7a7-imx6ull b/_targets/Makefile.armv7a7-imx6ull index c8e1690..df6fcf1 100644 --- a/_targets/Makefile.armv7a7-imx6ull +++ b/_targets/Makefile.armv7a7-imx6ull @@ -9,4 +9,4 @@ NET_DRIVERS_SUPPORTED := enet tuntap NET_DRIVERS ?= $(NET_DRIVERS_SUPPORTED) -DRIVERS_SRCS_enet = imx-enet.c ephy.c gpio.c imx6ull-gpio.c $(DRIVERS_SRCS_UTIL) hw-debug.c +DRIVERS_SRCS_enet = imx-enet.c ephy.c imx6ull-gpio.c $(DRIVERS_SRCS_UTIL) hw-debug.c diff --git a/_targets/Makefile.armv7m7-imxrt106x b/_targets/Makefile.armv7m7-imxrt106x index d9564c1..da61b27 100644 --- a/_targets/Makefile.armv7m7-imxrt106x +++ b/_targets/Makefile.armv7m7-imxrt106x @@ -3,9 +3,11 @@ # # iMX RT1064 target # -# Copyright 2021 Phoenix Systems +# Copyright 2021, 2024 Phoenix Systems # -NET_DRIVERS_SUPPORTED := pppou pppos +NET_DRIVERS_SUPPORTED := pppou pppos enet NET_DRIVERS ?= $(NET_DRIVERS_SUPPORTED) PPPOS_MODEM := huawei + +DRIVERS_SRCS_enet = imx-enet.c ephy.c imxrt106x-gpio.c $(DRIVERS_SRCS_UTIL) \ No newline at end of file diff --git a/drivers/Makefile b/drivers/Makefile index 96fa618..026eb4b 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -6,6 +6,9 @@ endif ifeq ($(EPHY_KSZ8081),RND) LOCAL_CFLAGS := -DEPHY_KSZ8081RND endif +ifeq ($(EPHY_KSZ8081),RNB) + LOCAL_CFLAGS := -DEPHY_KSZ8081RNB +endif # make possible to specify defalult APN name externally ifneq ($(PPPOS_DEFAULT_APN),) @@ -15,7 +18,7 @@ endif # abstract drivers available on every platform DRIVERS_SRCS := netif-driver.c -DRIVERS_SRCS_UTIL := bdring.c pktmem.c physmmap.c res-create.c +DRIVERS_SRCS_UTIL := bdring.c pktmem.c physmmap.c res-create.c time-util.c DRIVERS_SRCS_pppos := pppos.c DRIVERS_SRCS_pppou := pppou.c DRIVERS_SRCS_tuntap := tuntap.c diff --git a/drivers/ephy.c b/drivers/ephy.c index 4901ea4..bb72aa2 100644 --- a/drivers/ephy.c +++ b/drivers/ephy.c @@ -3,8 +3,8 @@ * * Ethernet PHY common routines * - * Copyright 2018 Phoenix Systems - * Author: Michał Mirosław + * Copyright 2018, 2024 Phoenix Systems + * Author: Michał Mirosław, Julian Uziembło * * %LICENSE% */ @@ -23,8 +23,6 @@ #include -//#define EPHY_KSZ8081RND - enum { EPHY_00_BASIC_CONTROL = 0x00, EPHY_01_BASIC_STATUS, @@ -48,22 +46,53 @@ enum { }; +#if 1 +static inline void ephy_printf(eth_phy_state_t *phy, const char *format, ...) +{ + char buf[192]; + va_list arg; + + va_start(arg, format); + vsnprintf(buf, sizeof(buf), format, arg); + va_end(arg); + + printf("lwip: ephy%u.%u: %s\n", phy->bus, phy->addr, buf); +} +#else +#define ephy_printf(...) +#endif + +#if EPHY_DEBUG +#define ephy_debug_printf(phy, ...) ephy_printf(phy, __VA_ARGS__) +#else +#define ephy_debug_printf(...) +#endif + + static uint16_t ephy_reg_read(eth_phy_state_t *phy, uint16_t reg) { - return mdio_read(phy->bus, phy->addr, reg); + uint16_t ret = mdio_read(phy->bus, phy->addr, reg); +#if MDIO_DEBUG + ephy_printf(phy, "read: val from reg: 0x%04x", phy->bus, phy->addr, ret); +#endif + return ret; } static void ephy_reg_write(eth_phy_state_t *phy, uint16_t reg, uint16_t val) { mdio_write(phy->bus, phy->addr, reg, val); - ephy_reg_read(phy, reg); + uint16_t ret __attribute__((unused)) = ephy_reg_read(phy, reg); +#if MDIO_DEBUG + ephy_printf(phy, "write: val from reg: 0x%04x", ret); +#endif } static void ephy_reset(eth_phy_state_t *phy) { if (gpio_valid(&phy->reset)) { + ephy_debug_printf(phy, "ephy_reset: start hardware reset..."); // TODO: prepare bootstrap pins gpio_set(&phy->reset, 1); usleep(phy->reset_hold_time_us); @@ -71,22 +100,26 @@ static void ephy_reset(eth_phy_state_t *phy) gpio_set(&phy->reset, 0); usleep(phy->reset_release_time_us); mdio_unlock_bus(phy->bus); + ephy_debug_printf(phy, "ephy_reset: hardware reset complete."); } else { int res = 0, retries = 10; + ephy_debug_printf(phy, "ephy_reset: start software reset..."); + ephy_reg_write(phy, EPHY_00_BASIC_CONTROL, 1 << 15); usleep(phy->reset_release_time_us); - while (retries--) { + do { res = ephy_reg_read(phy, EPHY_00_BASIC_CONTROL); if ((res & (1 << 15)) == 0) { + ephy_debug_printf(phy, "ephy_reset: software reset complete."); break; } - } + } while (retries--); if ((res & (1 << 15)) != 0) { - printf("lwip: ephy%u.%u soft-reset timed out\n", phy->bus, phy->addr); + ephy_printf(phy, "software reset timed out"); } } } @@ -113,14 +146,13 @@ static uint32_t ephy_readPhyId(eth_phy_state_t *phy) } -static void ephy_show_link_state(eth_phy_state_t *phy) +static void ephy_setLinkState(eth_phy_state_t *phy) { uint16_t bctl, bstat, adv, lpa, pc1, pc2; int speed, full_duplex; bctl = ephy_reg_read(phy, EPHY_00_BASIC_CONTROL); bstat = ephy_reg_read(phy, EPHY_01_BASIC_STATUS); - bstat = ephy_reg_read(phy, EPHY_01_BASIC_STATUS); adv = ephy_reg_read(phy, EPHY_04_AN_ADV); lpa = ephy_reg_read(phy, EPHY_05_AN_LP_ABILITY); pc1 = ephy_reg_read(phy, EPHY_1E_PHY_CTRL1); @@ -133,10 +165,16 @@ static void ephy_show_link_state(eth_phy_state_t *phy) phy->link_state_callback(phy->link_state_callback_arg, linkup); } - printf("lwip: ephy%u.%u link is %s %uMbps/%s (ctl %04x, status %04x, adv %04x, lpa %04x, pctl %04x,%04x)\n", - phy->bus, phy->addr, linkup ? "UP " : "DOWN", - speed, full_duplex ? "Full" : "Half", - bctl, bstat, adv, lpa, pc1, pc2); + if (speed != 0) { + ephy_printf(phy, "link is %s %uMbps/%s (ctl %04x, status %04x, adv %04x, lpa %04x, pctl %04x,%04x)", + linkup ? "UP " : "DOWN", + speed, full_duplex ? "Full" : "Half", + bctl, bstat, adv, lpa, pc1, pc2); + } + else { + ephy_printf(phy, "link is in AN mode (ctl %04x, status %04x, adv %04x, lpa %04x, pctl %04x,%04x)", + bctl, bstat, adv, lpa, pc1, pc2); + } } @@ -170,18 +208,26 @@ static void ephy_link_thread(void *arg) { eth_phy_state_t *phy = arg; uint16_t stat; + int err; for (;;) { - gpio_wait(&phy->irq_gpio, 1, 0); + err = gpio_wait(&phy->irq_gpio, 1, NULL); // FIXME: thread exit - stat = ephy_reg_read(phy, EPHY_1B_IRQ_CTRL_STATUS); - if (stat >= 0 && (stat & 0xFF) != 0) { - ephy_show_link_state(phy); + if (err == 0) { + stat = ephy_reg_read(phy, EPHY_1B_IRQ_CTRL_STATUS); + if ((stat & 0xFF) != 0) { + ephy_setLinkState(phy); + } + } +#if EPHY_DEBUG + else { + ephy_printf(phy, "ephy_link_thread: gpio_wait timed out"); } +#endif } - printf("lwip ephy%u.%u thread finished.\n", phy->bus, phy->addr); + ephy_printf(phy, "ephy link-detect thread finished"); endthread(); } @@ -240,6 +286,7 @@ static int ephy_config(eth_phy_state_t *phy, char *cfg) phy->addr = strtoul(cfg, &p, 0); } else { + printf("ephy: WARN: setting default bus 0\n"); phy->bus = 0; } @@ -294,15 +341,17 @@ int ephy_enableLoopback(eth_phy_state_t *phy, bool enable) ephy_reg_write(phy, EPHY_1F_PHY_CTRL2, phy_ctrl2); if (ephy_reg_read(phy, EPHY_00_BASIC_CONTROL) != bmcr) { - printf("ephy: failed to set loopback mode\n"); + ephy_printf(phy, "failed to set loopback mode"); return -1; } if (ephy_reg_read(phy, EPHY_1F_PHY_CTRL2) != phy_ctrl2) { - printf("ephy: failed to force link up\n"); + ephy_printf(phy, "failed to force link up"); return -1; } + ephy_debug_printf(phy, "loopback %s", enable ? "enabled" : "disabled"); + return 0; } @@ -340,26 +389,29 @@ static int ephy_setAltConfig(eth_phy_state_t *phy, int cfg_id) ephy_reg_write(phy, EPHY_1F_PHY_CTRL2, phy_ctrl2); if (ephy_reg_read(phy, EPHY_1F_PHY_CTRL2) != phy_ctrl2) { - printf("ephy: failed to set alternative clock\n"); + ephy_printf(phy, "failed to set alternative clock"); return -1; } + ephy_debug_printf(phy, "alternative clock set successfully"); + return 1; } int ephy_init(eth_phy_state_t *phy, char *conf, uint8_t board_rev, link_state_cb_t cb, void *cb_arg) { - (void)board_rev; uint32_t phyid; - int err; + int err, cfg_id; memset(phy, 0, sizeof(*phy)); err = ephy_config(phy, conf); if (err != 0) { + printf("lwip: ephy: Couldn't configure PHY: %s (%d)", strerror(err), err); return -EINVAL; } + ephy_debug_printf(phy, "PHY configured"); /* * for KSZ8081RNA/D, KSZ8081RNB: @@ -373,6 +425,7 @@ int ephy_init(eth_phy_state_t *phy, char *conf, uint8_t board_rev, link_state_cb err = mdio_setup(phy->bus, 2500 /* kHz */, 10 /* ns */, 0 /* with-preamble */); if (err != 0) { + ephy_printf(phy, "Couldn't init MDIO: %s (%d)", strerror(err), err); return err; } @@ -380,6 +433,7 @@ int ephy_init(eth_phy_state_t *phy, char *conf, uint8_t board_rev, link_state_cb phyid = ephy_readPhyId(phy); if (phyid == 0u || phyid == ~0u) { + ephy_printf(phy, "Couldn't read PHY ID"); gpio_set(&phy->reset, 1); return -ENODEV; } @@ -387,24 +441,36 @@ int ephy_init(eth_phy_state_t *phy, char *conf, uint8_t board_rev, link_state_cb /* make address 0 not broadcast, disable NAND-tree mode */ ephy_reg_write(phy, EPHY_16_OP_MODE_STRAP_OVERRIDE, (1 << 1) | (1 << 9)); - ephy_setAltConfig(phy, 0); /* KSZ8081 RND - default config */ + err = ephy_setAltConfig(phy, 0); /* KSZ8081 RND - default config */ + if (err <= 0) { + ephy_printf(phy, "Couldn't set default config"); + return -ENODEV; + } #ifdef LWIP_EPHY_INIT_HOOK LWIP_EPHY_INIT_HOOK(phy, phyid, board_rev); +#else + (void)board_rev; #endif + #if defined(EPHY_KSZ8081RNA) || defined(EPHY_KSZ8081RNB) - ephy_setAltConfig(phy, 1); + cfg_id = 1; #elif defined(EPHY_KSZ8081RND) - ephy_setAltConfig(phy, 0); + cfg_id = 0; #endif + ephy_setAltConfig(phy, cfg_id); + if (err <= 0) { + ephy_printf(phy, "Couldn't set alternative config %d", cfg_id); + return -ENODEV; + } phy->link_state_callback = cb; phy->link_state_callback_arg = cb_arg; - ephy_show_link_state(phy); + ephy_setLinkState(phy); if (gpio_valid(&phy->irq_gpio)) { - err = beginthread(ephy_link_thread, 0, phy->th_stack, sizeof(phy->th_stack), phy); + err = beginthread(ephy_link_thread, 6, phy->th_stack, sizeof(phy->th_stack), phy); if (err != 0) { gpio_set(&phy->reset, 1); return err; @@ -412,9 +478,21 @@ int ephy_init(eth_phy_state_t *phy, char *conf, uint8_t board_rev, link_state_cb /* enable link up/down IRQ signal */ ephy_reg_write(phy, EPHY_1B_IRQ_CTRL_STATUS, (1 << 8) | (1 << 10)); + + if ((phy->irq_gpio.flags & GPIO_INVERTED) == 0) { + /* set interrupt pin active high */ + uint16_t phy_ctrl2 = ephy_reg_read(phy, EPHY_1F_PHY_CTRL2); + ephy_reg_write(phy, EPHY_1F_PHY_CTRL2, phy_ctrl2 | (1 << 9)); + } + } + else { + ephy_printf(phy, "WARN: irq_gpio not valid, could not start PHY IRQ thread"); + return -ENODEV; } ephy_restart_an(phy); + ephy_debug_printf(phy, "Successfully initialized PHY"); + return 0; } diff --git a/drivers/ephy.h b/drivers/ephy.h index 9ed6fff..e058f01 100644 --- a/drivers/ephy.h +++ b/drivers/ephy.h @@ -17,7 +17,7 @@ #include #include -typedef void (*link_state_cb_t)(void* arg, int state); +typedef void (*link_state_cb_t)(void *arg, int state); typedef struct { unsigned bus; @@ -36,7 +36,9 @@ typedef struct { int ephy_init(eth_phy_state_t *phy, char *conf, uint8_t board_rev, link_state_cb_t cb, void *cb_arg); int ephy_linkSpeed(eth_phy_state_t *phy, int *full_duplex); +#ifdef ENET_SELFTEST /* toggle MACPHY internal loopback for test mode */ int ephy_enableLoopback(eth_phy_state_t *phy, bool enable); +#endif #endif /* NET_EPHY_H_ */ diff --git a/drivers/gpio.h b/drivers/gpio.h index b2e1821..bdc7efb 100644 --- a/drivers/gpio.h +++ b/drivers/gpio.h @@ -13,6 +13,7 @@ #include #include +#include enum { GPIO_INVERTED = 1 << 0, @@ -27,14 +28,17 @@ enum { typedef struct gpio_info_ { unsigned flags; - int fd; + union { + int fd; + int num; + }; uint32_t pin; } gpio_info_t; int gpio_set(gpio_info_t *gp, int active); uint32_t gpio_get(gpio_info_t *gp); -int gpio_wait(gpio_info_t *gp, int active, time_t timeout); +int gpio_wait(gpio_info_t *gp, int active, struct timespec *timeout); int gpio_init(gpio_info_t *gp, const char *arg, unsigned flags); int gpio_close(gpio_info_t *gp); int gpio_config(const char *name, uint32_t mask, unsigned flags); diff --git a/drivers/imx-enet-regs.h b/drivers/imx-enet-regs.h index ba5ba07..947c3ed 100644 --- a/drivers/imx-enet-regs.h +++ b/drivers/imx-enet-regs.h @@ -1,10 +1,10 @@ /* * Phoenix-RTOS --- networking stack * - * i.MX 6ULL built-in ENET register and structure definitions + * i.MX 6ULL/RT106x built-in ENET register and structure definitions * - * Copyright 2018 Phoenix Systems - * Author: Michał Mirosław + * Copyright 2018, 2024 Phoenix Systems + * Author: Michał Mirosław, Julian Uziembło * * %LICENSE% */ @@ -12,44 +12,77 @@ #define NET_IMX_ENET_REGS_H_ #include +#include +#include +#include "lwipopts.h" +#include "sys/platform.h" /* + * ENET registers must be read and written with 32-bit accesses + * + * i.MX RT106x: + * ENET base: 0x402D8000 irq 130 (@AIPS-1) PHY: bus=0, addr=2=; irq=10, reset=9 (GPIO_IO10 and GPIO_IO09) + * ENET2 base: 0x402D4000 irq 152 (@AIPS-4) + * * iMX6UL / iMX6ULL: - * ENET1: base 0x0218_8000 irq 150 (@AIPS-2) - * ENET2: base 0x020B_4000 irq 152 (@AIPS-1) - * all register accesses are required to be U32 + * ENET1: base 0x02188000 irq 150 (@AIPS-2) + * ENET2: base 0x020B4000 irq 152 (@AIPS-1) */ -typedef struct { - uint32_t S, C; /* 1588 control/status + compare capture */ -} timer_control_t; +#if defined(__CPU_IMXRT106X) +#include + +#define ENET_ADDR_ENET1 (0x402D8000) +#define CCM_ANALOG_PLL_ENET_SET_ADDR (0x400D80E4) +#define CCM_ANALOG_PLL_ENET_CLR_ADDR (0x400D80E8) +#define OCOTP_MEMORY_ADDR (0x401F4000) + +#define ENET_CLK_KHZ (132000) +#elif defined(__CPU_IMX6ULL) +#include +#include "hw-debug.h" + +#define ENET_ADDR_ENET1 (0x02188000) +#define ENET_ADDR_ENET2 (0x020B4000) +#define OCOTP_MEMORY_ADDR (0x021bc000) -#define R_RESERVED(start, end) uint32_t rsvd_##start##_##end[(end - start) / sizeof(uint32_t)] -#define BIT(i) (1u << (i)) +#define ENET_CLK_KHZ (66000 /* IPG */) /* BT_FREQ=0 */ +#else +#error "Unsupported TARGET" +#endif + + +#define R_RESERVED(start, end) uint32_t rsvd_##start##_##end[(end - start) / sizeof(uint32_t)] /* reserved address */ +#define BIT(i) (1u << (i)) /* bitmask for single bit */ +#define BITS(start, end) (((1u << ((end) - (start) + 1u)) - 1u) << (start)) /* bitmask for bits in provided range (inclusive) */ struct enet_regs { R_RESERVED(0x000, 0x004); - uint32_t EIR, EIMR; /* irq event, irq mask */ -#define ENET_IRQ_BABR BIT(30) /* babbling receive error */ -#define ENET_IRQ_BABT BIT(29) /* babbling transmit error */ -#define ENET_IRQ_GRA BIT(28) /* graceful stop complete (tx) */ -#define ENET_IRQ_TXF BIT(27) /* frame txed */ -#define ENET_IRQ_TXB BIT(26) /* tx buffer updated */ -#define ENET_IRQ_RXF BIT(25) /* frame rxed */ -#define ENET_IRQ_RXB BIT(24) /* rx buffer updated (excl. last in frame) */ -#define ENET_IRQ_MII BIT(23) /* MII transfer complete */ -#define ENET_IRQ_EBERR BIT(22) /* system bus error (ECR.ETHEREN forced clear) */ -#define ENET_IRQ_LC BIT(21) /* late collision */ -#define ENET_IRQ_RL BIT(20) /* tx collision retry limit hit for a frame (frame dropped) */ -#define ENET_IRQ_UN BIT(19) /* TX FIFO underrun */ -#define ENET_IRQ_PLR BIT(18) /* rxed frame for which payload length check failed */ -#define ENET_IRQ_WAKEUP BIT(17) /* woke up by magic packet */ -#define ENET_IRQ_TS_AVAIL BIT(16) /* ATSTMP valid after transmit */ -#define ENET_IRQ_TS_TIMER BIT(15) /* timer wrapped */ + uint32_t EIR, EIMR; /* irq event, irq mask */ +#define ENET_IRQ_BABR BIT(30) /* babbling receive error */ +#define ENET_IRQ_BABT BIT(29) /* babbling transmit error */ +#define ENET_IRQ_GRA BIT(28) /* graceful stop complete (tx) */ +#define ENET_IRQ_TXF BIT(27) /* frame txed */ +#define ENET_IRQ_TXB BIT(26) /* tx buffer updated */ +#define ENET_IRQ_RXF BIT(25) /* frame rxed */ +#define ENET_IRQ_RXB BIT(24) /* rx buffer updated (excl. last in frame) */ +#define ENET_IRQ_MII BIT(23) /* MII transfer complete */ +#define ENET_IRQ_EBERR BIT(22) /* system bus error (ECR.ETHEREN forced clear) */ +#define ENET_IRQ_LC BIT(21) /* late collision */ +#define ENET_IRQ_RL BIT(20) /* tx collision retry limit hit for a frame (frame dropped) */ +#define ENET_IRQ_UN BIT(19) /* TX FIFO underrun */ +#define ENET_IRQ_PLR BIT(18) /* rxed frame for which payload length check failed */ +#define ENET_IRQ_WAKEUP BIT(17) /* woke up by magic packet */ +#define ENET_IRQ_TS_AVAIL BIT(16) /* ATSTMP valid after transmit */ +#define ENET_IRQ_TS_TIMER BIT(15) /* timer wrapped */ +#define ENET_IRQ_ALL BITS(15, 30) /* all valid IRQs */ R_RESERVED(0x00C, 0x010); - uint32_t RDAR, TDAR; /* RX/TX new desc trigger command */ + uint32_t RDAR; /* RX new desc trigger command */ +#define ENET_RDAR_RDAR BIT(24) + uint32_t TDAR; /* TX new desc trigger command */ +#define ENET_TDAR_TDAR BIT(24) R_RESERVED(0x018, 0x024); - uint32_t ECR; /* ethernet control [desc format, endianness, reset, ...] */ -#define ENET_ECR_MAGIC_VAL 0x70000000 + uint32_t ECR; /* ethernet control [desc format, endianness, reset, ...] */ +#define ENET_ECR_MAGIC_VAL 0x70000000 /* Magic number that has to be written to ECR on every write (imxrt106x: RM 41.5.1.6.4) */ #define ENET_ECR_DBSWP BIT(8) #define ENET_ECR_DBGEN BIT(6) #define ENET_ECR_EN1588 BIT(4) @@ -58,44 +91,88 @@ struct enet_regs { #define ENET_ECR_ETHEREN BIT(1) #define ENET_ECR_RESET BIT(0) R_RESERVED(0x028, 0x040); - uint32_t MMFR, MSCR; /* MII control */ -#define ENET_MSCR_HOLDTIME_SHIFT 8 -#define ENET_MSCR_HOLDTIME_MASK 0x700 + uint32_t MMFR; /* MII control */ +#define ENET_MMFR_ST_SHIFT (30) +#define ENET_MMFR_ST BITS(ENET_MMFR_ST_SHIFT, 31) /* Start of frame delimiter */ +#define ENET_MMFR_OP_SHIFT (28) +#define ENET_MMFR_OP_WRITE (1) /* MDIO write opcode */ +#define ENET_MMFR_OP_READ (2) /* MDIO read opcode */ +#define ENET_MMFR_OP_ADDR (0) +#define ENET_MMFR_OP BITS(ENET_MMFR_OP_SHIFT, 29) /* Opcode */ +#define ENET_MMFR_PA_SHIFT (23) +#define ENET_MMFR_PA BITS(ENET_MMFR_PA_SHIFT, 27) /* PHY addr */ +#define ENET_MMFR_RA_SHIFT (18) +#define ENET_MMFR_RA BITS(ENET_MMFR_RA_SHIFT, 22) /* reg addr */ +#define ENET_MMFR_TA_SHIFT (16) +#define ENET_MMFR_TA_VAL (2) /* value for a valid TA */ +#define ENET_MMFR_TA BITS(ENET_MMFR_TA_SHIFT, 17) /* turn around */ +#define ENET_MMFR_DATA_SHIFT (0) +#define ENET_MMFR_DATA BITS(ENET_MMFR_DATA_SHIFT, 15) /* data */ +#define ENET_MMFR_ST_CLAUSE45_VAL (0 << 30) /* default value of ST field for Clause45 MDIO frames */ +#define ENET_MMFR_ST_CLAUSE22_VAL (1 << 30) /* default value of ST field for Clause22 MDIO frames */ + uint32_t MSCR; /* MII speed */ +#define ENET_MSCR_HOLDTIME_SHIFT (8) +#define ENET_MSCR_HOLDTIME_MASK BITS(ENET_MSCR_HOLDTIME_SHIFT, 10) #define ENET_MSCR_DIS_PRE BIT(7) -#define ENET_MSCR_MII_SPEED_SHIFT 1 -#define ENET_MSCR_MII_SPEED_MASK 0x7E +#define ENET_MSCR_MII_SPEED_SHIFT (1) +#define ENET_MSCR_MII_SPEED_MASK BITS(ENET_MSCR_MII_SPEED_SHIFT, 6) R_RESERVED(0x048, 0x064); uint32_t MIBC; /* MIB control */ +#define ENET_MIBC_MIB_DIS BIT(31) +#define ENET_MIBC_MIB_IDLE BIT(30) +#define ENET_MIBC_MIB_CLEAR BIT(29) R_RESERVED(0x068, 0x084); - uint32_t RCR; /* RX control */ -#define ENET_RCR_GRS BIT(31) /* [ro] RX stopped */ -#define ENET_RCR_NLC BIT(30) /* payloach check enable */ -#define ENET_RCR_MAX_FL_SHIFT 16 -#define ENET_RCR_MAX_FL_MASK ((BIT(14) - 1) << ENET_RCR_MAX_FL_SHIFT) -#define ENET_RCR_CFEN BIT(15) /* discard non-PAUSE MAC control frames */ -#define ENET_RCR_CRCFWD BIT(14) /* strip FCS from received frame data */ -#define ENET_RCR_PAUFWD BIT(13) /* forward PAUSE frames to user */ -#define ENET_RCR_PADEN BIT(12) /* remove padding for short packets (forces CRCFWD=1) */ -#define ENET_RCR_RMII_10T BIT(9) /* RMII 10Mbps mode (vs 100Mbps when clear) */ -#define ENET_RCR_RMII_MODE BIT(8) /* RMII mode (vs MII when clear) */ -#define ENET_RCR_FCE BIT(5) /* process incoming PAUSE frames (iow. enable flow control for tx) */ -#define ENET_RCR_BR_REJ BIT(4) /* discard broadcast frames (unless in PROMISC) */ -#define ENET_RCR_PROM BIT(3) /* PROMISC mode (== receive-all) */ -#define ENET_RCR_MII_MODE BIT(2) /* MII mode (required to be set) */ -#define ENET_RCR_DRT BIT(1) /* half-duplex mode */ -#define ENET_RCR_LOOP BIT(0) /* MII loopback mode (requires: MII_MODE=1, RMII_MODE=0, DRT=0, clocks for MII provided) */ + uint32_t RCR; /* RX control */ +#define ENET_RCR_GRS BIT(31) /* [ro] RX stopped */ +#define ENET_RCR_NLC BIT(30) /* payloach check enable */ +#define ENET_RCR_MAX_FL_SHIFT (16) +#define ENET_RCR_MAX_FL_WITH_VLAN_VAL (1522 << ENET_RCR_MAX_FL_SHIFT) /* recommended val from RM (with VLAN) */ +#define ENET_RCR_MAX_FL_NO_VLAN_VAL (1518 << ENET_RCR_MAX_FL_SHIFT) /* recommended val from RM (no VLAN) */ +#define ENET_RCR_MAX_FL BITS(16, 29) /* max RX frame length */ +#define ENET_RCR_CFEN BIT(15) /* discard non-PAUSE MAC control frames */ +#define ENET_RCR_CRCFWD BIT(14) /* strip FCS from received frame data */ +#define ENET_RCR_PAUFWD BIT(13) /* forward PAUSE frames to user */ +#define ENET_RCR_PADEN BIT(12) /* remove padding for short packets (forces CRCFWD=1) */ +#define ENET_RCR_RMII_10T BIT(9) /* RMII 10Mbps mode (vs 100Mbps when clear) */ +#define ENET_RCR_RMII_MODE BIT(8) /* RMII mode (vs MII when clear) */ +#define ENET_RCR_FCE BIT(5) /* process incoming PAUSE frames (iow. enable flow control for tx) */ +#define ENET_RCR_BR_REJ BIT(4) /* discard broadcast frames (unless in PROMISC) */ +#define ENET_RCR_PROM BIT(3) /* PROMISC mode (== receive-all) */ +#define ENET_RCR_MII_MODE BIT(2) /* MII mode (required to be set) */ +#define ENET_RCR_DRT BIT(1) /* half-duplex mode */ +#define ENET_RCR_LOOP BIT(0) /* MII loopback mode (requires: MII_MODE=1, RMII_MODE=0, DRT=0, clocks for MII provided) */ R_RESERVED(0x088, 0x0C4); - uint32_t TCR; /* TX control */ -#define ENET_TCR_CRCFWD BIT(9) /* don't ever append FCS to transmitted frame data */ -#define ENET_TCR_ADDINS BIT(8) /* overwrite SA with node's address (set in PALR+PAUR) */ -#define ENET_TCR_RFC_PAUSE BIT(4) /* (ro) TX is being held after PAUSE frame received */ -#define ENET_TCR_TFC_PAUSE BIT(3) /* (auto-clears) trigger transmission of PAUSE frame */ -#define ENET_TCR_FDEN BIT(2) /* full-duplex mode (== ignore CS and COL on transmit) */ -#define ENET_TCR_GTS BIT(0) /* stop transmit (after current frame transmission ends) */ + uint32_t TCR; /* TX control */ +#define ENET_TCR_CRCFWD BIT(9) /* don't ever append FCS to transmitted frame data */ +#define ENET_TCR_ADDINS BIT(8) /* overwrite SA with node's address (set in PALR+PAUR) */ +#define ENET_TCR_ADDSEL_SHIFT (5) +#define ENET_TCR_ADDSEL BITS(ENET_TCR_ADDSEL_SHIFT, 7) +#define ENET_TCR_ADDSEL_VAL (0b000 << ENET_TCR_ADDSEL_SHIFT) +#define ENET_TCR_RFC_PAUSE BIT(4) /* (ro) TX is being held after PAUSE frame received */ +#define ENET_TCR_TFC_PAUSE BIT(3) /* (auto-clears) trigger transmission of PAUSE frame */ +#define ENET_TCR_FDEN BIT(2) /* full-duplex mode (== ignore CS and COL on transmit) */ +#define ENET_TCR_GTS BIT(0) /* stop transmit (after current frame transmission ends) */ R_RESERVED(0x0C8, 0x0E4); - uint32_t PALR, PAUR; /* MAC address */ - uint32_t OPD; /* pause duration (for TXed pauses) */ - uint32_t TXIC; /* TX irq coalescing */ + uint32_t PALR; +#define ENET_PALR_PADDR1_BYTE0_SHIFT (24) +#define ENET_PALR_PADDR1_BYTE0 BITS(ENET_PALR_PADDR1_BYTE0_SHIFT, 31) +#define ENET_PALR_PADDR1_BYTE1_SHIFT (16) +#define ENET_PALR_PADDR1_BYTE1 BITS(ENET_PALR_PADDR1_BYTE1_SHIFT, 23) +#define ENET_PALR_PADDR1_BYTE2_SHIFT (8) +#define ENET_PALR_PADDR1_BYTE2 BITS(ENET_PALR_PADDR1_BYTE2_SHIFT, 15) +#define ENET_PALR_PADDR1_BYTE3_SHIFT (0) +#define ENET_PALR_PADDR1_BYTE3 BITS(ENET_PALR_PADDR1_BYTE3_SHIFT, 7) + uint32_t PAUR; /* MAC address */ +#define ENET_PAUR_PADDR1_BYTE4_SHIFT (24) +#define ENET_PAUR_PADDR1_BYTE4 BITS(ENET_PAUR_PADDR1_BYTE4_SHIFT, 31) +#define ENET_PAUR_PADDR1_BYTE5_SHIFT (16) +#define ENET_PAUR_PADDR1_BYTE5 BITS(ENET_PAUR_PADDR1_BYTE5_SHIFT, 23) +#define ENET_PAUR_TYPE_SHIFT (0) +#define ENET_PAUR_TYPE BITS(ENET_PAUR_TYPE_SHIFT, 15) /* `Type` field in PAUSE frames */ +#define ENET_PAUR_TYPE_RESET_VAL (0x8808) /* RM 41.5.1.13.4 */ + uint32_t OPD; /* pause duration (for TXed pauses) */ +#define ENET_OPD_INIT_VAL (0x00010000) /* RM 41.5.1.14.1 */ + uint32_t TXIC; /* TX irq coalescing */ R_RESERVED(0x0F4, 0x100); uint32_t RXIC; /* RX irq coalescing */ R_RESERVED(0x104, 0x118); @@ -127,13 +204,19 @@ struct enet_regs { #define ENET_RACC_PRODIS BIT(2) /* discard frames with TCP/UDP/ICMP checksum error */ #define ENET_RACC_IPDIS BIT(1) /* discard frames with IPv4 checksum error */ #define ENET_RACC_PADREM BIT(0) /* remove ethernet payload padding from short IP frames */ - R_RESERVED(0x1C8, 0x200); + R_RESERVED(0x01c8, 0x0200); union { uint32_t rawstats[64]; /* various stats counters (32-bit each) */ #define ENET_VALID_COUTERS 0x01FFFFFE1FFFFFFFull struct { - uint32_t RMON_T_DROP; /* Count of frames not cntd correctly */ +#if defined(__CPU_IMX6ULL) + uint32_t RMON_T_DROP; /* Count of frames not cntd correctly */ +#elif defined(__CPU_IMXRT106X) + R_RESERVED(0x0200, 0x0204); +#else +#error "Unsupported TARGET" +#endif uint32_t RMON_T_PACKETS; /* RMON TX packet count */ uint32_t RMON_T_BC_PKT; /* RMON TX broadcast pkts */ uint32_t RMON_T_MC_PKT; /* RMON TX multicast pkts */ @@ -151,18 +234,24 @@ struct enet_regs { uint32_t RMON_T_P1024TO2047; /* RMON TX 1024 to 2047 byte pkts */ uint32_t RMON_T_P_GTE2048; /* RMON TX pkts > 2048 bytes */ uint32_t RMON_T_OCTETS; /* RMON TX octets */ - uint32_t IEEE_T_DROP; /* Count of frames not counted correctly */ - uint32_t IEEE_T_FRAME_OK; /* Frames tx'd OK */ - uint32_t IEEE_T_1COL; /* Frames tx'd with single collision */ - uint32_t IEEE_T_MCOL; /* Frames tx'd with multiple collision */ - uint32_t IEEE_T_DEF; /* Frames tx'd after deferral delay */ - uint32_t IEEE_T_LCOL; /* Frames tx'd with late collision */ - uint32_t IEEE_T_EXCOL; /* Frames tx'd with excessive collisions */ - uint32_t IEEE_T_MACERR; /* Frames tx'd with TX FIFO underrun */ - uint32_t IEEE_T_CSERR; /* Frames tx'd with carrier sense err */ - uint32_t IEEE_T_SQE; /* Frames tx'd with SQE err */ - uint32_t IEEE_T_FDXFC; /* Flow control pause frames tx'd */ - uint32_t IEEE_T_OCTETS_OK; /* Octet count for frames tx'd w/o err */ +#if defined(__CPU_IMXRT106X) + R_RESERVED(0x0248, 0x024c); +#elif defined(__CPU_IMX6ULL) + uint32_t IEEE_T_DROP; /* Count of frames not counted correctly */ +#else +#error "Unsupported TARGET" +#endif + uint32_t IEEE_T_FRAME_OK; /* Frames tx'd OK */ + uint32_t IEEE_T_1COL; /* Frames tx'd with single collision */ + uint32_t IEEE_T_MCOL; /* Frames tx'd with multiple collision */ + uint32_t IEEE_T_DEF; /* Frames tx'd after deferral delay */ + uint32_t IEEE_T_LCOL; /* Frames tx'd with late collision */ + uint32_t IEEE_T_EXCOL; /* Frames tx'd with excessive collisions */ + uint32_t IEEE_T_MACERR; /* Frames tx'd with TX FIFO underrun */ + uint32_t IEEE_T_CSERR; /* Frames tx'd with carrier sense err */ + uint32_t IEEE_T_SQE; /* Frames tx'd with SQE err */ + uint32_t IEEE_T_FDXFC; /* Flow control pause frames tx'd */ + uint32_t IEEE_T_OCTETS_OK; /* Octet count for frames tx'd w/o err */ R_RESERVED(0x0278, 0x0284); uint32_t RMON_R_PACKETS; /* RMON RX packet count */ uint32_t RMON_R_BC_PKT; /* RMON RX broadcast pkts */ @@ -200,8 +289,15 @@ struct enet_regs { uint32_t ATINC; /* timer increment (base increment value per ts_clk, correction increment value) */ uint32_t ATSTMP; /* last TX timestamp (for last frame with TxBD[TS] set) */ R_RESERVED(0x41C, 0x604); - uint32_t TGSR; /* timer flags (channel 0-3) */ - timer_control_t TC_R[4]; /* timer flags (channel 0-3) */ + uint32_t TGSR; /* timer flags (channel 0-3) */ + uint32_t TCSR0; + uint32_t TCCR0; + uint32_t TCSR1; + uint32_t TCCR1; + uint32_t TCSR2; + uint32_t TCCR2; + uint32_t TCSR3; + uint32_t TCCR3; R_RESERVED(0x628, 0x800); }; @@ -219,8 +315,13 @@ typedef struct typedef struct { /* first 3 same as enet_short_desc_t */ - uint16_t len, flags; - uint32_t addr; + union { + struct { + uint16_t len, flags; + uint32_t addr; + }; + enet_legacy_desc_t legacy; + }; uint16_t xflags, yflags; uint16_t csum, proto; /* csum: IP payload (iow excluding IP header) */ uint16_t rsvd1, dflags; diff --git a/drivers/imx-enet.c b/drivers/imx-enet.c index 45bebc0..8abba64 100644 --- a/drivers/imx-enet.c +++ b/drivers/imx-enet.c @@ -3,8 +3,8 @@ * * iMX6ULL ENET network module driver * - * Copyright 2018 Phoenix Systems - * Author: Michał Mirosław + * Copyright 2018, 2024 Phoenix Systems + * Author: Michał Mirosław, Julian Uziembło * * %LICENSE% */ @@ -14,15 +14,14 @@ #include "netif-driver.h" #include "bdring.h" #include "ephy.h" -#include "hw-debug.h" #include "physmmap.h" #include "res-create.h" #include "imx-enet-regs.h" +#include "time-util.h" +#include #include -#include #include -#include #include #include #include @@ -33,23 +32,13 @@ #include #include +#define MDIO_TIMEOUT (NULL) +#define ENET_RX_RING_SIZE (64) +#define ENET_TX_RING_SIZE (64) +#define ENET_RING_ALIGNMENT (64) +#define ENET_BUFFER_SIZE (2048 - 64) -#define ENET_CLK_KHZ (66000 /* IPG */) // BT_FREQ=0 - -#define USE_ENET_EXT_DESCRIPTORS 0 -#define USE_RMII 1 -#define ENABLE_FLOW_CONTROL 1 -#define ENABLE_PROMISC 0 -#define ENABLE_RX_PAD_REMOVE 1 -#define ENET_RX_RING_SIZE 64 -#define ENET_TX_RING_SIZE 64 -#define ENET_BUFFER_SIZE (2048 - 64) -#define ENET_MDC_ALWAYS_ON 1 -#define MDIO_DEBUG 0 -/* #define ENET_VERBOSE */ - - -#if USE_ENET_EXT_DESCRIPTORS +#if ENET_USE_ENHANCED_DESCRIPTORS typedef enet_enhanced_desc_t enet_buf_desc_t; #else typedef enet_legacy_desc_t enet_buf_desc_t; @@ -77,8 +66,6 @@ typedef struct addr_t dev_phys_addr; uint32_t mscr; - char name[32]; - eth_phy_state_t phy; struct { @@ -92,13 +79,7 @@ typedef struct } enet_state_t; -enum { - VDBG = 0, - DEBUG = 1, - NOTICE = 2, - - EV_BUS_ERROR = 0x01, -}; +enum { EV_BUS_ERROR = 0x01 }; #if 1 @@ -110,29 +91,46 @@ static void enet_printf(enet_state_t *state, const char *format, ...) va_start(arg, format); vsnprintf(buf, sizeof(buf), format, arg); va_end(arg); - printf("lwip: %s %s\n", state->name, buf); + printf("lwip: enet@%08x %s\n", state->dev_phys_addr, buf); } #else #define enet_printf(...) #endif +#if ENET_DEBUG +#define enet_debug_printf(state, ...) enet_printf(state, __VA_ARGS__) +#else +#define enet_debug_printf(...) +#endif -static void enet_reset(enet_state_t *state) + +static int enet_reset(enet_state_t *state, const struct timespec *timeout) { - // FIXME: timeout + struct timespec now, when; + clock_gettime(CLOCK_MONOTONIC, &now); + when = time_add(&now, timeout); /* trigger and wait for reset */ - enet_printf(state, "Resetting device..."); + enet_debug_printf(state, "Resetting device..."); state->mmio->ECR = ENET_ECR_MAGIC_VAL | ENET_ECR_RESET; do { usleep(100); - } while (state->mmio->ECR & ENET_ECR_ETHEREN); - enet_printf(state, "Reset done."); + if (timeout != NULL) { + clock_gettime(CLOCK_MONOTONIC, &now); + if (time_cmp(&now, &when) >= 0) { + enet_printf(state, "Couldn't reset device: timeout"); + return -ETIMEDOUT; + } + } + } while ((state->mmio->ECR & ENET_ECR_ETHEREN) != 0); + enet_debug_printf(state, "Reset done."); state->mmio->IAUR = 0; state->mmio->IALR = 0; state->mmio->GAUR = 0; state->mmio->GALR = 0; + + return 0; } @@ -141,60 +139,105 @@ static void enet_start(enet_state_t *state) // addr_t ecr_pa = (addr_t)&((struct enet_regs *)state->phys)->ECR; // FIXME: last_will(ECR = ENET_ECR_MAGIC_VAL | ENET_ECR_RESET); + state->mmio->ECR &= ENET_ECR_ETHEREN; + state->mmio->MRBR = ENET_BUFFER_SIZE; // FIXME: coerce with net_allocPktBuf() state->mmio->FTRL = BIT(14) - 1; // FIXME: truncation to just above link MTU - state->mmio->RCR = (1518 << 16) | - ENET_RCR_CRCFWD | ENET_RCR_PAUFWD | -#if ENABLE_RX_PAD_REMOVE + state->mmio->RCR = +#if ETHARP_SUPPORT_VLAN + ENET_RCR_MAX_FL_WITH_VLAN_VAL | +#else + ENET_RCR_MAX_FL_NO_VLAN_VAL | +#endif +#if ENET_TERMINATE_RECEIVED_CRC + ENET_RCR_CRCFWD | +#endif +#if ENET_FWD_PAUSE_FRAMES + ENET_RCR_PAUFWD | +#endif +#if ENET_ENABLE_RX_PAD_REMOVE ENET_RCR_PADEN | #endif #if ENET_RMII_MODE ENET_RCR_RMII_MODE | #endif -#if ENABLE_FLOW_CONTROL +#if ENET_ENABLE_FLOW_CONTROL ENET_RCR_FCE | #endif -#if ENABLE_PROMISC +#if ENET_ENABLE_PROMISC ENET_RCR_PROM | +#endif +#if ENET_DIS_RX_ON_TX + ENET_RCR_DRT | +#endif +#if ENET_MAC_CONTROL_FRAME_ENABLE + ENET_RCR_CFEN | +#endif +#if ENET_ENABLE_INTERNAL_LOOPBACK && !LWIP_ENABLE_RMII && !LWIP_DIS_RX_ON_TX + ENET_RCR_LOOP | #endif ENET_RCR_MII_MODE; - state->mmio->RACC = ENET_RACC_SHIFT16 | -#if !ENABLE_PROMISC + /* RCR */ + + state->mmio->RACC = +#if ETH_PAD_SIZE == 2 + ENET_RACC_SHIFT16 | +#endif +#if !ENET_ENABLE_PROMISC ENET_RACC_LINEDIS | ENET_RACC_PRODIS | ENET_RACC_IPDIS | #endif ENET_RACC_PADREM; + /* RACC */ - state->mmio->TCR = ENET_TCR_FDEN; + state->mmio->TACC = +#if ENET_ENABLE_CHECKSUM + ENET_TACC_PROCHK | +#endif +#if ENET_ENABLE_IPCHK + ENET_TACC_IPCHK | +#endif #if ETH_PAD_SIZE == 2 - state->mmio->TACC = ENET_TACC_SHIFT16; -#else - state->mmio->TACC = 0; + ENET_TACC_SHIFT16 | +#endif + 0x0; + /* TACC */ + + state->mmio->TCR = +#if ENET_SET_MAC_ON_TX + ENET_TCR_ADDINS | #endif + ENET_TCR_FDEN; + /* TCR */ + + state->mmio->EIR = ENET_IRQ_ALL; mutexLock(state->irq_lock); state->mmio->EIMR |= ENET_IRQ_EBERR; mutexUnlock(state->irq_lock); + state->mmio->OPD = ENET_OPD_INIT_VAL; + state->mmio->ECR = ENET_ECR_MAGIC_VAL | -#if USE_ENET_EXT_DESCRIPTORS +#if ENET_USE_ENHANCED_DESCRIPTORS ENET_ECR_EN1588 | #endif #if __BYTE_ORDER == __LITTLE_ENDIAN ENET_ECR_DBSWP | #endif ENET_ECR_ETHEREN; + /* ECR */ /* trigger HW RX */ - state->mmio->RDAR = ~0u; + state->mmio->RDAR = ENET_RDAR_RDAR; -#ifdef ENET_VERBOSE - enet_printf(state, "regs: ECR , EIMR , TACC , RACC , TCR , RCR , MRBR , FTRL "); - enet_printf(state, "regs: %08x %08x %08x %08x %08x %08x %08x %08x", +#if defined(__CPU_IMX6ULL) + enet_debug_printf(state, "regs: ECR , EIMR , TACC , RACC , TCR , RCR , MRBR , FTRL "); + enet_debug_printf(state, "regs: %08x %08x %08x %08x %08x %08x %08x %08x", state->mmio->ECR, state->mmio->EIMR, state->mmio->TACC, state->mmio->RACC, state->mmio->TCR, state->mmio->RCR, state->mmio->MRBR, state->mmio->FTRL); - enet_printf(state, "regs: PLL6 , CCGR0 , GPR1 ,TXCLKMUX,TXCLKPAD,RCLK1SID,OSC24-M0,OSC24-LP"); - enet_printf(state, "regs: %08x %08x %08x %08x %08x %08x %08x %08x", + enet_debug_printf(state, "regs: PLL6 , CCGR0 , GPR1 ,TXCLKMUX,TXCLKPAD,RCLK1SID,OSC24-M0,OSC24-LP"); + enet_debug_printf(state, "regs: %08x %08x %08x %08x %08x %08x %08x %08x", hwdebug_read(0x20c80e0), hwdebug_read(0x20c4068), hwdebug_read(0x20e4004), hwdebug_read(0x20e00dc), hwdebug_read(0x20e0368), hwdebug_read(0x20e0574), hwdebug_read(0x20c8150), hwdebug_read(0x20c8270)); #endif @@ -203,12 +246,13 @@ static void enet_start(enet_state_t *state) static int enet_readFusedMac(uint32_t *buf) { - volatile uint32_t *va = physmmap(0x21bc000, 0x1000); + volatile uint32_t *va = physmmap(OCOTP_MEMORY_ADDR, 0x1000); if (va == MAP_FAILED) { return -ENOMEM; } + // FIXME: extract otp memory logic buf[0] = va[0x100 + 4 * 0x22]; buf[1] = va[0x100 + 4 * 0x23]; buf[2] = va[0x100 + 4 * 0x24]; @@ -221,7 +265,7 @@ static int enet_readFusedMac(uint32_t *buf) static uint32_t enet_readCpuId(void) { - volatile uint32_t *va = physmmap(0x21bc000, 0x1000); + volatile uint32_t *va = physmmap(OCOTP_MEMORY_ADDR, 0x1000); uint32_t res = 0; if (va == MAP_FAILED) { @@ -245,7 +289,7 @@ static inline uint8_t get_byte(uint32_t v, int i) static uint8_t enet_readBoardRev(void) { - volatile uint32_t *va = physmmap(0x21bc000, 0x1000); + volatile uint32_t *va = physmmap(OCOTP_MEMORY_ADDR, 0x1000); uint32_t res = 0; if (va == MAP_FAILED) { @@ -270,7 +314,7 @@ static void enet_readCardMac(enet_state_t *state) mac = (void *)&state->netif->hwaddr; - if (state->dev_phys_addr == 0x02188000 /* iMX6ULL.ENET1 */ && enet_readFusedMac(buf) == 0) { + if (state->dev_phys_addr == ENET_ADDR_ENET1 && enet_readFusedMac(buf) == 0) { mac[0] = get_byte(buf[1], 1); mac[1] = get_byte(buf[1], 0); mac[2] = get_byte(buf[0], 3); @@ -278,7 +322,8 @@ static void enet_readCardMac(enet_state_t *state) mac[4] = get_byte(buf[0], 1); mac[5] = get_byte(buf[0], 0); } - else if (state->dev_phys_addr == 0x020B4000 /* iMX6ULL.ENET2 */ && enet_readFusedMac(buf) == 0) { +#if defined(ENET_ADDR_ENET2) + else if (state->dev_phys_addr == ENET_ADDR_ENET2 && enet_readFusedMac(buf) == 0) { mac[0] = get_byte(buf[2], 3); mac[1] = get_byte(buf[2], 2); mac[2] = get_byte(buf[2], 1); @@ -286,6 +331,7 @@ static void enet_readCardMac(enet_state_t *state) mac[4] = get_byte(buf[1], 3); mac[5] = get_byte(buf[1], 2); } +#endif else { buf[0] = state->mmio->PALR; buf[1] = state->mmio->PAUR; @@ -300,6 +346,7 @@ static void enet_readCardMac(enet_state_t *state) if (memcmp(mac, &zero_eth.addr, ETH_HWADDR_LEN) == 0) { uint32_t cpuId = enet_readCpuId(); + enet_debug_printf(state, "MAC address from CPUID"); mac[0] = 0x02; mac[1] = (cpuId >> 24) & 0xFF; mac[2] = (cpuId >> 16) & 0xFF; @@ -309,14 +356,14 @@ static void enet_readCardMac(enet_state_t *state) } state->mmio->PALR = be32toh(*(uint32_t *)mac); - state->mmio->PAUR = (be16toh(*(uint16_t *)(mac + 4)) << 16) | 0x8808; + state->mmio->PAUR = (be16toh(*(uint16_t *)(mac + 4)) << 16) | ENET_PAUR_TYPE_RESET_VAL; } static void enet_showCardId(enet_state_t *state) { uint8_t *mac = (void *)&state->netif->hwaddr; - printf("lwip: %s initialized, MAC=%02x:%02x:%02x:%02x:%02x:%02x\n", state->name, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + enet_printf(state, "initialized, MAC=%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); } @@ -325,7 +372,7 @@ static size_t enet_nextRxBufferSize(const net_bufdesc_ring_t *ring, size_t i) volatile enet_buf_desc_t *desc = (volatile enet_buf_desc_t *)ring->ring + i; size_t sz; -#if 0 && USE_ENET_EXT_DESCRIPTORS +#if 0 && ENET_USE_ENHANCED_DESCRIPTORS if ((desc->dflags & ENET_XDESC_DONE) == 0) { return 0; } @@ -336,8 +383,10 @@ static size_t enet_nextRxBufferSize(const net_bufdesc_ring_t *ring, size_t i) } sz = desc->len; - if (!sz) { // FIXME: hw bug? + if (sz == 0) { // FIXME: hw bug? sz = 1; + printf("lwip: enet: WARNING: This message indicates a potential HW bug:\n"); + printf("lwip: enet: HW provided invalid size: %zu. Setting size to 1\n", sz); } return sz; } @@ -358,7 +407,7 @@ static void enet_fillRxDesc(const net_bufdesc_ring_t *ring, size_t i, addr_t pa, desc->len = sz - ETH_PAD_SIZE; desc->addr = pa; -#if USE_ENET_EXT_DESCRIPTORS +#if ENET_USE_ENHANCED_DESCRIPTORS desc->yflags = ENET_RXDY_INT; #endif atomic_store(&desc->flags, ENET_DESC_RDY | wrap); @@ -385,15 +434,17 @@ static void enet_fillTxDesc(const net_bufdesc_ring_t *ring, size_t i, addr_t pa, desc->len = sz; desc->addr = pa; -#if USE_ENET_EXT_DESCRIPTORS +#if ENET_USE_ENHANCED_DESCRIPTORS unsigned yflags = ENET_TXDY_INT; - if ((oflags & OFLAG_CSUM_IPV4) != 0) { - desc->yflags |= ENET_TXDY_IPCSUM; - } - if ((oflags & (OFLAG_CSUM_UDP | OFLAG_CSUM_TCP)) != 0) { - desc->yflags |= ENET_TXDY_L4CSUM; - } + /* + if ((oflags & OFLAG_CSUM_IPV4) != 0) { + yflags |= ENET_TXDY_IPCSUM; + } + if ((oflags & (OFLAG_CSUM_UDP | OFLAG_CSUM_TCP)) != 0) { + yflags |= ENET_TXDY_L4CSUM; + } + */ desc->yflags = yflags; #endif @@ -409,7 +460,7 @@ static const net_bufdesc_ops_t enet_ring_ops = { .nextTxDone = enet_nextTxDone, .fillTxDesc = enet_fillTxDesc, .desc_size = sizeof(enet_buf_desc_t), - .ring_alignment = 64, + .ring_alignment = ENET_RING_ALIGNMENT, .pkt_buf_sz = ENET_BUFFER_SIZE, .max_tx_frag = 0xFFFF, }; @@ -486,8 +537,11 @@ static void enet_irq_thread(void *arg) static int enet_mdioSetup(void *arg, unsigned max_khz, unsigned min_hold_ns, unsigned opt_preamble) { enet_state_t *state = arg; - int speed, hold, changed = 0; + int speed, hold; /* mdc_freq = enet_clk / 2 / (MDIO_SPEED + 1) */ +#if ENET_DEBUG + int changed = 0; +#endif speed = (ENET_CLK_KHZ / 2 + max_khz - 1) / max_khz - 1; if (speed < 1) { @@ -502,11 +556,13 @@ static int enet_mdioSetup(void *arg, unsigned max_khz, unsigned min_hold_ns, uns if (speed > (state->mscr & ENET_MSCR_MII_SPEED_MASK)) { state->mscr &= ~ENET_MSCR_MII_SPEED_MASK; state->mscr |= speed; +#if ENET_DEBUG changed = 1; +#endif } - if (min_hold_ns < 8 * 1000 * 1000 / ENET_CLK_KHZ) { - hold = (min_hold_ns * ENET_CLK_KHZ + (1000 * 1000 - 1)) / (1000 * 1000) - 1; + if (min_hold_ns < (8 * 1000 * 1000) / ENET_CLK_KHZ) { + hold = (min_hold_ns * ENET_CLK_KHZ + ((1000 * 1000) - 1)) / (1000 * 1000) - 1; } else { hold = ENET_MSCR_HOLDTIME_MASK >> ENET_MSCR_HOLDTIME_SHIFT; @@ -525,14 +581,19 @@ static int enet_mdioSetup(void *arg, unsigned max_khz, unsigned min_hold_ns, uns if (hold > (state->mscr & ENET_MSCR_HOLDTIME_MASK)) { state->mscr &= ~ENET_MSCR_HOLDTIME_MASK; state->mscr |= hold; +#if ENET_DEBUG changed = 1; +#endif } if (!opt_preamble && (state->mscr & ENET_MSCR_DIS_PRE)) { state->mscr &= ~ENET_MSCR_DIS_PRE; +#if ENET_DEBUG changed = 1; +#endif } +#if ENET_DEBUG if (changed) { speed = (state->mscr & ENET_MSCR_MII_SPEED_MASK) >> ENET_MSCR_MII_SPEED_SHIFT; hold = (state->mscr & ENET_MSCR_HOLDTIME_MASK) >> ENET_MSCR_HOLDTIME_SHIFT; @@ -541,6 +602,7 @@ static int enet_mdioSetup(void *arg, unsigned max_khz, unsigned min_hold_ns, uns hold, (hold + 1) * 1000000 / ENET_CLK_KHZ, state->mscr & ENET_MSCR_DIS_PRE ? "no" : "with"); } +#endif #if ENET_MDC_ALWAYS_ON state->mmio->MSCR = state->mscr; @@ -550,40 +612,82 @@ static int enet_mdioSetup(void *arg, unsigned max_khz, unsigned min_hold_ns, uns } -static void enet_mdioWait(enet_state_t *state) +static void enet_mdioWait(enet_state_t *state, struct timespec *timeout) { - // FIXME: timeout - while (!(state->mmio->EIR & ENET_IRQ_MII)) - /* relax */; + struct timespec now, when; + clock_gettime(CLOCK_MONOTONIC, &now); + when = time_add(&now, timeout); + + while ((state->mmio->EIR & ENET_IRQ_MII) == 0) { + if (timeout != NULL) { + clock_gettime(CLOCK_MONOTONIC, &now); + if (time_cmp(&now, &when) >= 0) { + enet_printf(state, "enet_mdioWait: WARN: timeout"); + break; + } + } + } + state->mmio->EIR = ENET_IRQ_MII; } -static uint16_t enet_mdioIO(enet_state_t *state, unsigned addr, unsigned reg, unsigned val, int read) +static uint16_t enet_mdioIO(enet_state_t *state, unsigned addr, unsigned reg, uint16_t val, unsigned op) { - state->mmio->EIR = ENET_IRQ_MII; +#if MDIO_DEBUG + /* clang-format off */ + enet_printf(state, "mdio: op %s, addr=0x%08x, reg=0x%08x, val=0x%04x", + op == ENET_MMFR_OP_READ ? "READ" : + op == ENET_MMFR_OP_WRITE ? "WRITE" : + "WARN: undefined", + addr, reg, val); + /* clang-format on */ +#endif + uint32_t mmfr = 0u; + #if !ENET_MDC_ALWAYS_ON state->mmio->MSCR = state->mscr; #endif + /* clause 45 */ if (addr & NETDEV_MDIO_CLAUSE45) { + /* ST */ + mmfr |= ENET_MMFR_ST_CLAUSE45_VAL; + uint32_t dev = ((addr & NETDEV_MDIO_A_MASK) << 18) | ((addr & NETDEV_MDIO_B_MASK) << (23 - 8)); - state->mmio->MMFR = 0x00020000 | /* extended MDIO address write */ - dev | (reg & 0xFFFF); - enet_mdioWait(state); - state->mmio->MMFR = (read ? 0x20020000 : 0x10020000) | /* extended MDIO data r/w */ - dev | (read ? 0 : val & 0xFFFF); + mmfr |= (ENET_MMFR_OP_ADDR << ENET_MMFR_OP_SHIFT) | + (ENET_MMFR_TA_VAL << ENET_MMFR_OP_SHIFT) | dev | (reg & 0xFFFF); + + state->mmio->MMFR = mmfr; + enet_mdioWait(state, MDIO_TIMEOUT); + + mmfr |= (op << ENET_MMFR_OP_SHIFT) | dev | (val & ENET_MMFR_DATA); } - else { /* clause 22 */ - state->mmio->MMFR = (read ? 0x60020000 : 0x50020000) | /* standard MDIO data r/w */ - ((addr & NETDEV_MDIO_A_MASK) << 23) | - ((reg & 0x1F) << 18) | - (read ? 0 : val & 0xFFFF); + /* clause 22 */ + else { + /* ST */ + mmfr |= ENET_MMFR_ST_CLAUSE22_VAL; + /* OP */ + mmfr |= ((op << ENET_MMFR_OP_SHIFT) & ENET_MMFR_OP); + /* PA - PHY addr */ + mmfr |= + (((addr & NETDEV_MDIO_A_MASK) << ENET_MMFR_PA_SHIFT) & ENET_MMFR_PA); + /* RA - reg addr */ + mmfr |= (((reg & NETDEV_MDIO_A_MASK) << ENET_MMFR_RA_SHIFT) & ENET_MMFR_RA); + /* TA */ + mmfr |= ENET_MMFR_TA_VAL << ENET_MMFR_TA_SHIFT; + /* DATA */ + mmfr |= val & ENET_MMFR_DATA; } - enet_mdioWait(state); - val = state->mmio->MMFR & 0xFFFF; + state->mmio->EIR = ENET_IRQ_MII; + + state->mmio->MMFR = mmfr; + enet_mdioWait(state, MDIO_TIMEOUT); + + val = state->mmio->MMFR & ENET_MMFR_DATA; + #if !ENET_MDC_ALWAYS_ON state->mmio->MSCR = 0; #endif @@ -594,24 +698,14 @@ static uint16_t enet_mdioIO(enet_state_t *state, unsigned addr, unsigned reg, un static uint16_t enet_mdioRead(void *arg, unsigned addr, uint16_t reg) { enet_state_t *state = arg; - uint16_t v; - - v = enet_mdioIO(state, addr, reg, 0, 1); -#if MDIO_DEBUG - enet_printf(state, "MDIO %02x[%02x] ?= %04x", addr, reg, v); -#endif - return v; + return enet_mdioIO(state, addr, reg, 0, ENET_MMFR_OP_READ); } static void enet_mdioWrite(void *arg, unsigned addr, uint16_t reg, uint16_t val) { enet_state_t *state = arg; - - enet_mdioIO(state, addr, reg, val, 0); -#if MDIO_DEBUG - enet_printf(state, "MDIO %02x[%02x] := %04x", addr, reg, val); -#endif + (void)enet_mdioIO(state, addr, reg, val, ENET_MMFR_OP_WRITE); } @@ -636,8 +730,44 @@ static int platformctl_seq(const platformctl_t *pctl, size_t n) } +static inline void enet_warnUnsupportedDeviceAddr(enet_state_t *state) +{ + enet_printf(state, "Unsupported device address 0x%08x\n", state->dev_phys_addr); + enet_printf(state, "Supported addresses:\n"); +#if defined(ENET_ADDR_ENET1) + printf("\tENET1=0x%08x\n", ENET_ADDR_ENET1); +#endif +#if defined(ENET_ADDR_ENET2) + printf("\tENET2=0x%08x\n", ENET_ADDR_ENET2); +#endif +} + + +/* + * Configure MDIO pins for the required ENET module + */ static int enet_initMDIO(enet_state_t *state) { + int err; +#if defined(__CPU_IMXRT106X) + if (state->dev_phys_addr != ENET_ADDR_ENET1) { + enet_printf(state, "Unsupported device addr: 0x%08x", state->dev_phys_addr); + return -1; + } + static const platformctl_t pctl_enet[] = { + /* 0: GPIO_AD_B1_05_ALT1, 1: GPIO_EMC_41_ALT4, 2: GPIO_B1_15_ALT0 */ + { pctl_set, pctl_ioisel, .ioisel = { pctl_isel_enet_mdio, 1 } }, + + { pctl_set, pctl_iopad, + .iopad = { pctl_pad_gpio_emc_40, 0, 2, 1, 1, 0, 3, 5, 1 } }, /* enet_mdc */ + { pctl_set, pctl_iopad, + .iopad = { pctl_pad_gpio_emc_41, 0, 2, 1, 1, 1, 0, 5, 1 } }, /* enet_mdio */ + + { pctl_set, pctl_iomux, .iomux = { pctl_mux_gpio_emc_40, 0, 4 } }, /* enet_mdc */ + { pctl_set, pctl_iomux, .iomux = { pctl_mux_gpio_emc_41, 1, 4 } }, /* enet_mdio */ + }; + err = platformctl_seq(pctl_enet, sizeof(pctl_enet) / sizeof(*pctl_enet)); +#elif defined(__CPU_IMX6ULL) static const platformctl_t pctl_enet1[] = { { pctl_set, pctl_ioisel, .ioisel = { pctl_isel_enet1_mac0mdio, 0 } }, { pctl_set, pctl_iomux, .iomux = { pctl_mux_gpio1_06, 0, 0 } }, @@ -649,43 +779,153 @@ static int enet_initMDIO(enet_state_t *state) { pctl_set, pctl_iomux, .iomux = { pctl_mux_gpio1_07, 0, 1 } }, }; - int err; - - if (state->dev_phys_addr == 0x02188000 /* iMX6ULL.ENET1 */) + if (state->dev_phys_addr == ENET_ADDR_ENET1) { err = platformctl_seq(pctl_enet1, sizeof(pctl_enet1) / sizeof(*pctl_enet1)); - else if (state->dev_phys_addr == 0x020B4000 /* iMX6ULL.ENET2 */) + } + else if (state->dev_phys_addr == ENET_ADDR_ENET2) { err = platformctl_seq(pctl_enet2, sizeof(pctl_enet2) / sizeof(*pctl_enet2)); - else - err = 0; + } + else { + enet_warnUnsupportedDeviceAddr(state); + return -1; + } +#else +#error "Unsupported TARGET" +#endif if (err < 0) { - enet_printf(state, "Can't configure MDIO pins"); + enet_printf(state, "Couldn't configure MDIO pins"); return err; } - state->mscr = (1 << ENET_MSCR_SPEED_SHIFT) | ENET_MSCR_DIS_PRE; - return 0; } +/* + * Enable clock for the required ENET module + */ static int enet_clockEnable(enet_state_t *state) { - static const platformctl_t pctl_enet_clock = { - pctl_set, pctl_devclock, .devclock = { pctl_clk_enet, 3 } +#if defined(__CPU_IMXRT106X) + static const platformctl_t pctl_enet_clock[] = { + { pctl_set, pctl_devclock, + .devclock = { pctl_clk_enet, clk_state_run_wait } }, + + { pctl_set, pctl_devclock, + .devclock = { pctl_clk_gpio1, clk_state_run_wait } }, }; - int err; - err = platformctl_seq(&pctl_enet_clock, 1); - if (err < 0) - enet_printf(state, "Can't enable ENET clock\n"); + if (state->dev_phys_addr != ENET_ADDR_ENET1) { + enet_warnUnsupportedDeviceAddr(state); + return -1; + } + + /* RM 14.8.14 */ +#define REG(addr) (*((volatile uint32_t *)addr)) + REG(CCM_ANALOG_PLL_ENET_SET_ADDR) = + (1 << 0) | /* enet_ref_clk = 50MHz */ + (1 << 12) | /* POWERDOWN */ + (1 << 13); /* enable PLL providing enet ref clk */ + REG(CCM_ANALOG_PLL_ENET_CLR_ADDR) = (1 << 12); /* clear POWERDOWN bit */ +#undef REG +#elif defined(__CPU_IMX6ULL) + static const platformctl_t pctl_enet_clock[] = { + { pctl_set, pctl_devclock, .devclock = { pctl_clk_enet, 3 } } + }; + if (state->dev_phys_addr != ENET_ADDR_ENET1 && state->dev_phys_addr != ENET_ADDR_ENET2) { + enet_warnUnsupportedDeviceAddr(state); + return -1; + } +#else +#error "Unsupported TARGET" +#endif + int err = platformctl_seq(pctl_enet_clock, sizeof(pctl_enet_clock) / sizeof(*pctl_enet_clock)); + if (err < 0) { + enet_printf(state, "Couldn't enable ENET clocks"); + } return err; } +/* + * Set pin config for the required ENET module + */ static int enet_pinConfig(enet_state_t *state) { + int err; +#if defined(__CPU_IMXRT106X) + if (state->dev_phys_addr != ENET_ADDR_ENET1) { + enet_printf(state, "Unsupported device addr: 0x%08x", state->dev_phys_addr); + return -1; + } + const platformctl_t pctl_enet[] = { + /* IOMUXC.GPR (RM 11.3.2) */ + /* 0: ENET1_TX ref clk driven by ref_enetpll (ENET_REF_CLK1), 1: ENET1_TX */ + /* ref clock from ENET_T1_CLK */ + { pctl_set, pctl_iogpr, .iogpr = { pctl_gpr_enet1_clk_sel, 0 } }, + /* 0: disabled, 1: enabled */ + { pctl_set, pctl_iogpr, .iogpr = { pctl_gpr_enet1_tx_clk_dir, 1 } }, + /* 0: ipg_clk gated when no IPS access, 1: ipg_clk always ON */ + { pctl_set, pctl_iogpr, .iogpr = { pctl_gpr_enet_ipg_clk_s_en, 1 } }, + /* 0: GPIO1, 1: GPIO6 */ + { pctl_set, pctl_iogpr, .iogpr = { pctl_gpr_gpio_mux1_gpio_sel, 0 } }, + + /* IOMUXC.DAISY (RM 11.3.6) */ + /* 0: GPIO_EMC_25_ALT3, 1: GPIO_B1_10_ALT3 */ + { pctl_set, pctl_ioisel, .ioisel = { pctl_isel_enet_txclk, 1 } }, + /* 0: GPIO_EMC_26_ALT3, 1: GPIO_B1_11_ALT3 */ + { pctl_set, pctl_ioisel, .ioisel = { pctl_isel_enet_rxerr, 1 } }, + /* 0: GPIO_EMC_23_ALT3, 1: GPIO_B1_06_ALT3 */ + { pctl_set, pctl_ioisel, .ioisel = { pctl_isel_enet_rxen, 1 } }, + /* 0: GPIO_EMC_19_ALT3, 1: GPIO_B1_05_ALT3 */ + { pctl_set, pctl_ioisel, .ioisel = { pctl_isel_enet1_rxdata, 1 } }, + /* 0: GPIO_B0_00_ALT3, 1: GPIO_B1_04_ALT3 */ + { pctl_set, pctl_ioisel, .ioisel = { pctl_isel_enet0_rxdata, 1 } }, + /* 0: GPIO_EMC_25_ALT24, 1: GPIO_B1_10_ALT6 */ + { pctl_set, pctl_ioisel, .ioisel = { pctl_isel_enet_ipg_clk_rmi, 1 } }, + + /* IOMUXC.PAD */ + { pctl_set, pctl_iopad, + .iopad = { pctl_pad_gpio_b1_04, 0, 2, 1, 1, 0, 3, 6, 1 } }, /* enet1_rx0 */ + { pctl_set, pctl_iopad, + .iopad = { pctl_pad_gpio_b1_05, 0, 2, 1, 1, 0, 3, 6, 1 } }, /* enet1_rx1 */ + { pctl_set, pctl_iopad, + .iopad = { pctl_pad_gpio_b1_06, 0, 2, 1, 1, 0, 3, 6, 1 } }, /* enet1_rx_en (crs_dv) */ + { pctl_set, pctl_iopad, + .iopad = { pctl_pad_gpio_b1_07, 0, 2, 1, 1, 0, 3, 6, 1 } }, /* enet1_tx0 */ + { pctl_set, pctl_iopad, + .iopad = { pctl_pad_gpio_b1_08, 0, 2, 1, 1, 0, 3, 6, 1 } }, /* enet1_tx1 */ + { pctl_set, pctl_iopad, + .iopad = { pctl_pad_gpio_b1_09, 0, 2, 1, 1, 0, 3, 6, 1 } }, /* enet1_txen */ + { pctl_set, pctl_iopad, + .iopad = { pctl_pad_gpio_b1_10, 0, 2, 0, 0, 0, 3, 6, 1 } }, /* enet1_txclk */ + { pctl_set, pctl_iopad, + .iopad = { pctl_pad_gpio_b1_11, 0, 2, 1, 1, 0, 3, 6, 1 } }, /* enet1_rxer */ + { pctl_set, pctl_iopad, + .iopad = { pctl_pad_gpio_ad_b0_10, 0, 2, 1, 1, 0, 2, 4, 1 } }, /* irq */ + { pctl_set, pctl_iopad, + .iopad = { pctl_pad_gpio_ad_b0_09, 0, 0, 1, 1, 0, 2, 6, 1 } }, /* rst */ + + // IOMUXC.MUX + { pctl_set, pctl_iomux, .iomux = { pctl_mux_gpio_b1_04, 0, 3 } }, /* enet1_rx0 */ + { pctl_set, pctl_iomux, .iomux = { pctl_mux_gpio_b1_05, 0, 3 } }, /* enet1_rx1 */ + { pctl_set, pctl_iomux, .iomux = { pctl_mux_gpio_b1_06, 0, 3 } }, /* enet1_rx_en (crs_dv) */ + { pctl_set, pctl_iomux, .iomux = { pctl_mux_gpio_b1_07, 0, 3 } }, /* enet1_tx0 */ + { pctl_set, pctl_iomux, .iomux = { pctl_mux_gpio_b1_08, 0, 3 } }, /* enet1_tx1 */ + { pctl_set, pctl_iomux, .iomux = { pctl_mux_gpio_b1_09, 0, 3 } }, /* enet1_txen */ + { pctl_set, pctl_iomux, .iomux = { pctl_mux_gpio_b1_10, 1, 6 } }, /* enet_ref_clk */ + { pctl_set, pctl_iomux, .iomux = { pctl_mux_gpio_b1_11, 0, 3 } }, /* enet1_rxer */ + { pctl_set, pctl_iomux, .iomux = { pctl_mux_gpio_ad_b0_10, 0, 5 } }, /* irq (enet_int) */ + { pctl_set, pctl_iomux, .iomux = { pctl_mux_gpio_ad_b0_09, 0, 5 } }, /* rst (enet_rst) */ + }; + if (state->dev_phys_addr != ENET_ADDR_ENET1) { + enet_warnUnsupportedDeviceAddr(state); + return -1; + } + err = platformctl_seq(pctl_enet, sizeof(pctl_enet) / sizeof(*pctl_enet)); +#elif defined(__CPU_IMX6ULL) static const platformctl_t pctl_enet1[] = { { pctl_set, pctl_iogpr, .iogpr = { pctl_gpr_enet1_clk, 0 } }, { pctl_set, pctl_iogpr, .iogpr = { pctl_gpr_enet1_tx, 1 } }, @@ -731,22 +971,27 @@ static int enet_pinConfig(enet_state_t *state) { pctl_set, pctl_iomux, .iomux = { pctl_mux_enet2_txclk, 1, 4 } }, }; - int err; - - if (state->dev_phys_addr == 0x02188000 /* iMX6ULL.ENET1 */) + if (state->dev_phys_addr == ENET_ADDR_ENET1) { err = platformctl_seq(pctl_enet1, sizeof(pctl_enet1) / sizeof(*pctl_enet1)); - else if (state->dev_phys_addr == 0x020B4000 /* iMX6ULL.ENET2 */) + } + else if (state->dev_phys_addr == ENET_ADDR_ENET2) { err = platformctl_seq(pctl_enet2, sizeof(pctl_enet2) / sizeof(*pctl_enet2)); - else - err = 0; + } + else { + enet_warnUnsupportedDeviceAddr(state); + return -1; + } +#else +#error "Unsupported TARGET" +#endif if (err < 0) { - enet_printf(state, "Can't configure ENET pins\n"); + enet_printf(state, "Couldn't configure ENET pins"); return err; } state->mmio->RCR = -#if USE_RMII +#if ENET_RMII_MODE ENET_RCR_RMII_MODE | #endif ENET_RCR_MII_MODE; @@ -760,10 +1005,9 @@ static int enet_initDevice(enet_state_t *state, int irq, int mdio) // FIXME: cleanup on error int err; - snprintf(state->name, sizeof(state->name), "enet@%08x", state->dev_phys_addr); - state->mmio = physmmap(state->dev_phys_addr, 0x1000); if (state->mmio == MAP_FAILED) { + enet_printf(state, "enet_initDevice: no memory"); return -ENODEV; } @@ -776,38 +1020,60 @@ static int enet_initDevice(enet_state_t *state, int irq, int mdio) if (err < 0) { return err; } + enet_debug_printf(state, "Enabled clock"); + + err = enet_reset(state, NULL); + if (err < 0) { + return err; + } - enet_reset(state); enet_readCardMac(state); - enet_pinConfig(state); + err = enet_pinConfig(state); + + if (err) { + enet_debug_printf(state, "Couldn't configure pins: %s (%d)", strerror(err), err); + } + else { + enet_debug_printf(state, "Pins configured"); + } if (mdio != 0) { err = enet_initMDIO(state); if (err < 0) { return err; } + enet_debug_printf(state, "Initialized MDIO"); } err = enet_initRings(state); if (err != 0) { return err; } + enet_debug_printf(state, "Initialized ENET Rings"); -#ifdef ENET_VERBOSE - enet_printf(state, "mmio 0x%x irq %d", state->dev_phys_addr, irq); -#endif + enet_debug_printf(state, "mmio 0x%x irq %d", state->dev_phys_addr, irq); + + err = interrupt(irq, enet_irq_handler, state, state->irq_cond, &state->irq_handle); + if (err != 0) { + enet_printf(state, "Couldn't register hard interrupt handler: %s (%d)", strerror(err), err); + return err; + } + enet_debug_printf(state, "Hard IRQ thread initialized successfully (err=%d)", err); - interrupt(irq, enet_irq_handler, state, state->irq_cond, &state->irq_handle); - beginthread(enet_irq_thread, 4, state->irq_stack, sizeof(state->irq_stack), state); + err = beginthread(enet_irq_thread, 4, state->irq_stack, sizeof(state->irq_stack), state); + if (err != 0) { + enet_printf(state, "Couldn't begin interrupt thread: %s (%d)", strerror(err), err); + return err; + } - if (state->mscr != 0) { + if (mdio != 0) { err = register_mdio_bus(&enet_mdio_ops, state); if (err < 0) { - enet_printf(state, "Can't register MDIO bus"); + enet_printf(state, "Can't register MDIO bus: %s (%d)", strerror(err), err); return err; } - enet_printf(state, "MDIO bus %d", err); + enet_debug_printf(state, "MDIO bus %d", err); } net_refillRx(&state->rx, ETH_PAD_SIZE); @@ -825,13 +1091,14 @@ static err_t enet_netifOutput(struct netif *netif, struct pbuf *p) size_t nf; if (ETH_PAD_SIZE != 2) { - pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */ + enet_debug_printf(state, "dropping padding word, ETH_PAD_SIZE=%u", ETH_PAD_SIZE); + (void)pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */ } mutexLock(state->tx_lock); nf = net_transmitPacket(&state->tx, p); if (nf) { - state->mmio->TDAR = ~0u; + state->mmio->TDAR = ENET_TDAR_TDAR; } mutexUnlock(state->tx_lock); @@ -842,10 +1109,10 @@ static void enet_setLinkState(void *arg, int state) { struct netif *netif = arg; enet_state_t *priv = netif->state; - int speed, full_duplex; + int speed; if (state != 0) { - speed = ephy_linkSpeed(&priv->phy, &full_duplex); + speed = ephy_linkSpeed(&priv->phy, NULL); if (speed == 10) { priv->mmio->RCR |= ENET_RCR_RMII_10T; @@ -862,9 +1129,12 @@ static void enet_setLinkState(void *arg, int state) } +#if ENET_SELFTEST +#define _TP_DST "dddddd" +#define _TP_SRC "ssssss" #define _TP_ETHTYPE "\x05\xDD" /* eth frame type 0x05DD is undefined */ #define _TP_10DIG "0123456789" -#define TEST_PACKET "ddddddssssss" _TP_ETHTYPE \ +#define TEST_PACKET _TP_DST _TP_SRC _TP_ETHTYPE \ _TP_10DIG _TP_10DIG _TP_10DIG _TP_10DIG _TP_10DIG _TP_10DIG _TP_10DIG #define TEST_PACKET_LEN (sizeof((TEST_PACKET)) - 1) @@ -879,22 +1149,18 @@ static err_t test_netif_input(struct pbuf *p, struct netif *netif) /* verify contents */ if ((p->len != TEST_PACKET_LEN + ETH_PAD_SIZE)) { -#ifdef ENET_VERBOSE - enet_printf(priv, "self-test RX: invalid packet length"); -#endif + enet_debug_printf(priv, "self-test RX: invalid packet length"); is_valid_pkt = false; } uint8_t *data = pbuf_get_contiguous(p, buf, sizeof(buf), TEST_PACKET_LEN, ETH_PAD_SIZE); if (data == NULL || memcmp(TEST_PACKET, data, TEST_PACKET_LEN) != 0) { -#ifdef ENET_VERBOSE if (data == NULL) { data = p->payload; } - enet_printf(priv, "self-test RX: invalid packet contents"); - enet_printf(priv, "0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x", + enet_debug_printf(priv, "self-test RX: invalid packet contents"); + enet_debug_printf(priv, "0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x", data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]); -#endif is_valid_pkt = false; } pbuf_free(p); @@ -912,7 +1178,9 @@ static err_t test_netif_input(struct pbuf *p, struct netif *netif) static int enet_phySelfTest(struct netif *netif) { enet_state_t *state = netif->state; - int err; + int err, was_addins_set; + + enet_printf(state, "Start enet phy tx/rx selftest"); err = create_mutexcond_bulk(SELFTEST_RESOURCES(state)); if (err != 0) { @@ -930,8 +1198,14 @@ static int enet_phySelfTest(struct netif *netif) /* enable promisicious mode to allow invalid MAC in pseudo-ETH test packet */ state->mmio->RCR |= ENET_RCR_PROM; - /* enable MIB counters (mmio->stats) */ + /* disable MAC address addition on TX */ + was_addins_set = state->mmio->TCR & ENET_TCR_ADDINS; + state->mmio->TCR &= ~ENET_TCR_ADDINS; + + /* enable MIB counters (mmio->stats) + clear stats */ state->mmio->MIBC = 0; + state->mmio->MIBC |= ENET_MIBC_MIB_CLEAR; + state->mmio->MIBC &= ~ENET_MIBC_MIB_CLEAR; /* override netif->input */ netif_input_fn old_input = netif->input; @@ -960,19 +1234,21 @@ static int enet_phySelfTest(struct netif *netif) } mutexUnlock(state->selfTest.rx_lock); -#ifdef ENET_VERBOSE - enet_printf(state, "stats: TX: PACKETS=%u CRC_ALIGN=%u OK=%u", + enet_debug_printf(state, "stats: TX: PACKETS=%u CRC_ALIGN=%u OK=%u", state->mmio->stats.RMON_T_PACKETS, state->mmio->stats.RMON_T_CRC_ALIGN, state->mmio->stats.IEEE_T_FRAME_OK); - enet_printf(state, "stats: RX: PACKETS=%u CRC_ALIGN=%u OK=%u", + enet_debug_printf(state, "stats: RX: PACKETS=%u CRC_ALIGN=%u OK=%u", state->mmio->stats.RMON_R_PACKETS, state->mmio->stats.RMON_R_CRC_ALIGN, state->mmio->stats.IEEE_R_FRAME_OK); -#endif + if ((err < 0) || (state->selfTest.rx_valid != 1)) { + enet_debug_printf(state, "Test failed: state->selfTest.rx_valid=%d, %s (%d)", + state->selfTest.rx_valid, strerror(err), err); ret = -1; + break; } /* successfully received */ @@ -981,7 +1257,9 @@ static int enet_phySelfTest(struct netif *netif) /* restore normal mode */ netif->input = old_input; state->mmio->RCR &= ~ENET_RCR_PROM; - state->mmio->MIBC = (1u << 31); + if (was_addins_set) + state->mmio->TCR |= ENET_TCR_ADDINS; + state->mmio->MIBC = ENET_MIBC_MIB_DIS; ephy_enableLoopback(&state->phy, false); /* destroy selftest resources */ @@ -990,6 +1268,7 @@ static int enet_phySelfTest(struct netif *netif) return ret; } +#endif /* ARGS: enet:base:irq[:no-mdio][:PHY:[bus.]addr[:config]] */ @@ -1000,7 +1279,6 @@ static int enet_netifInit(struct netif *netif, char *cfg) int err, irq, mdio = 1; netif->linkoutput = enet_netifOutput; - state = netif->state; state->netif = netif; @@ -1015,17 +1293,18 @@ static int enet_netifInit(struct netif *netif, char *cfg) } /* irq */ - irq = strtoul((cfg = p), &p, 0); + cfg = p; + irq = strtoul(cfg, &p, 0); if (*cfg == '\0' || (*p != '\0' && *p++ != ':') || irq < 0) { return -EINVAL; } /* MDIO and PHY opts */ cfg = NULL; - while (p && *p) { + while (p != NULL && *p != '\0') { cfg = strchr(p, ':'); - if (cfg != 0) { - *cfg++ = 0; + if (cfg != NULL) { + *cfg++ = '\0'; } if (strcmp(p, "no-mdio") == 0) { @@ -1043,22 +1322,34 @@ static int enet_netifInit(struct netif *netif, char *cfg) err = enet_initDevice(state, irq, mdio); if (err != 0) { + enet_printf(state, "Failed to initialize ENET"); return err; } + enet_debug_printf(state, "Initialized ENET"); if (cfg) { uint8_t board_rev = enet_readBoardRev(); + if (board_rev == 0) { + return -EINVAL; + } + enet_debug_printf(state, "Board rev: %d (0x%x)", board_rev, board_rev); err = ephy_init(&state->phy, cfg, board_rev, enet_setLinkState, (void *)state->netif); if (err < 0) { enet_printf(state, "WARN: PHY init failed: %s (%d)", strerror(err), err); return err; } + enet_debug_printf(state, "ephy_init finished, returned %d (%s)", err, strerror(err)); +#if ENET_SELFTEST err = enet_phySelfTest(netif); if (err < 0) { enet_printf(state, "WARN: PHY autotest failed"); } + else { + enet_printf(state, "PHY selftest passed successfully"); + } +#endif } return 0; diff --git a/drivers/imx6ull-gpio.c b/drivers/imx6ull-gpio.c index 456db8e..242079e 100644 --- a/drivers/imx6ull-gpio.c +++ b/drivers/imx6ull-gpio.c @@ -1,18 +1,24 @@ /* * Phoenix-RTOS --- networking stack * - * iMX6ULL GPIO hack (mask missing functionality in gpiosrv) + * GPIO wrapper for iMX 6ULL * - * Copyright 2018 Phoenix Systems - * Author: Michał Mirosław + * Copyright 2018, 2024 Phoenix Systems + * Author: Michał Mirosław, Julian Uziembło * * %LICENSE% */ #include "gpio.h" +#include "time-util.h" +#include +#include #include #include #include +#include +#include +#include struct pin_conf { @@ -99,3 +105,188 @@ int gpio_config(const char *name, uint32_t mask, unsigned flags) return err; } + + +static int gpio_do_set(gpio_info_t *gp, int val) +{ + uint32_t data[2]; + + data[0] = val ? gp->pin : 0; + data[1] = gp->pin; + + val = write(gp->fd, data, sizeof(data)); + + if (val == sizeof(data)) { + return 0; + } + if (val >= 0) { + return -EIO; + } + return -errno; +} + + +int gpio_set(gpio_info_t *gp, int active) +{ + if (!gpio_valid(gp)) { + return -EINVAL; + } + + if ((gp->flags & GPIO_INVERTED) != 0) { + active = !active; + } + + return gpio_do_set(gp, active); +} + + +uint32_t gpio_get(gpio_info_t *gp) +{ + uint32_t data[1]; + int err; + + if (!gpio_valid(gp)) { + return 0; + } + + err = read(gp->fd, data, sizeof(data)); + if (err != sizeof(data)) { + return 0; + } + + if ((gp->flags & GPIO_INVERTED) != 0) { + data[0] = ~data[0]; + } + + return data[0] & gp->pin; +} + + +int gpio_wait(gpio_info_t *gp, int active, struct timespec *timeout) +{ + struct timespec when, now; + uint32_t val; + + if (!gpio_valid(gp)) + return -EINVAL; + + if (gp->flags & GPIO_INVERTED) + active = !active; + + clock_gettime(CLOCK_MONOTONIC, &now); + when = time_add(&now, timeout); + + for (;;) { + val = gpio_get(gp); // FIXME: in gpiosrv + if ((!active ^ !val) != 0) { + return 0; + } + + if (timeout != NULL) { + clock_gettime(CLOCK_MONOTONIC, &now); + if (time_cmp(&now, &when) >= 0) { + return -ETIME; + } + } + usleep(100 * 1000); // 100ms + } +} + + +int gpio_init(gpio_info_t *gp, const char *arg, unsigned flags) +{ + char buf[64]; + char *endp; + int err, fd = -1; + + if (*arg == '-') { + ++arg; + flags ^= GPIO_INVERTED; + } + + gpio_close(gp); + + gp->pin = strtoul(arg, &endp, 0); + if ((*endp != ',' && *endp != ':') || gp->pin >= sizeof(gp->pin) * 8) { + return -EINVAL; + } + gp->pin = 1u << gp->pin; + arg = endp + 1; + if (*arg == '\0') { + return -EINVAL; + } + if (strlen(arg) > sizeof(buf) - 6) { + return -EINVAL; + } + + gp->flags = flags & ~(GPIO_ACTIVE | GPIO_INITIALIZED); + + gpio_config(arg, gp->pin, gp->flags); + snprintf(buf, sizeof(buf), "%s/port", arg); + + gp->fd = open(buf, O_RDWR); + if (gp->fd < 0) { + err = -errno; + printf("gpio: can't open '%s': %s (%d)\n", buf, strerror(err), err); + if (fd >= 0) { + close(fd); + } + return err; + } + + if ((flags & GPIO_OUTPUT) != 0) { + err = gpio_do_set(gp, !!(flags & GPIO_ACTIVE) ^ !!(flags & GPIO_INVERTED)); + if (err != 0) { + printf("gpio: WARN: can't pre-set pin: %s (%d; fd = %d)\n", strerror(err), err, gp->fd); + } + } + + fd = gp->fd; + + snprintf(buf, sizeof(buf), "%s/dir", arg); + gp->fd = open(buf, O_RDWR); + + if (gp->fd < 0) { + err = -errno; + printf("gpio: can't open '%s': %s (%d)\n", buf, strerror(err), err); + if (fd >= 0) { + close(fd); + } + return err; + } + + err = gpio_do_set(gp, flags | GPIO_INPUT | GPIO_OUTPUT); + if (err != 0) { + printf("gpio: can't configure pin direction: %s (%d; fd = %d)\n", strerror(err), err, gp->fd); + printf("gpio: file %s: `", buf); + } + + close(gp->fd); + + if (err != 0) { + if (fd >= 0) { + close(fd); + } + return err; + } + + gp->fd = fd; + gp->flags |= GPIO_INITIALIZED; + + return 0; +} + + +int gpio_close(gpio_info_t *gp) +{ + int err; + + if (!gpio_valid(gp)) { + return -EINVAL; + } + + err = close(gp->fd); + gp->flags = 0; + + return err; +} diff --git a/drivers/imxrt106x-gpio.c b/drivers/imxrt106x-gpio.c new file mode 100644 index 0000000..0b00131 --- /dev/null +++ b/drivers/imxrt106x-gpio.c @@ -0,0 +1,258 @@ +/* + * Phoenix-RTOS --- networking stack + * + * GPIO wrapper for iMX RT106x + * + * Copyright 2024 Phoenix Systems + * Author: Julian Uziembło + * + * %LICENSE% + */ +#include +#include +#include +#include +#include +#include +#include +#include "errno.h" + +#include "gpio.h" +#include "imxrt-multi.h" +#include "lwipopts.h" +#include "time-util.h" + +static oid_t multidrv; + +#if GPIO_DEBUG +#define gpio_debug_printf(...) printf(__VA_ARGS__); +#else +#define gpio_debug_printf(...) +#endif + +#define IMSG_STRUCT_FORMAT_STR "\ +imsg={\n\ + .gpio={\n\ + .type=%d,\n\ + .dir={\n\ + .val=%u,\n\ + .mask=%u,\n\ + },\n\ + },\n\ +}\n" + +#define ID_GPIO(n) (id_gpio1 + n - 1) + +/* + * Extract GPIO number from [cfg] string + * cfg: `/dev/gpioX`, where `X` - gpio number + * returns the GPIO number on success; or -1 on failure + */ +static inline unsigned int gpio_getNum(const char *cfg) +{ + return strtoul(cfg + 9, NULL, 10); +} + + +static inline int itobool(int val) +{ + return !!val; +} + + +static inline void gpio_msgHeader(msg_t *msg, int gpio) +{ + msg->type = mtDevCtl; + msg->oid.id = gpio; + msg->oid.port = multidrv.port; + + msg->i.data = NULL; + msg->i.size = 0; + msg->o.data = NULL; + msg->o.size = 0; +} + + +int gpio_getPin(int gpio, int pin, uint32_t *res) +{ + msg_t msg; + multi_i_t *imsg = NULL; + int err = 0; + gpio_msgHeader(&msg, gpio); + + imsg = (multi_i_t *)msg.i.raw; + imsg->gpio.type = gpio_get_port; + + err = msgSend(multidrv.port, &msg); + if (err < 0) { + return err; + } + + *res = ((multi_o_t *)(msg.o.raw))->val; + + return err; +} + + +int gpio_setPin(int gpio, int pin, int state) +{ + msg_t msg; + multi_i_t *imsg = NULL; + gpio_msgHeader(&msg, gpio); + + imsg = (multi_i_t *)msg.i.raw; + + imsg->gpio.type = gpio_set_port; + imsg->gpio.port.val = itobool(state << pin); + imsg->gpio.port.mask = 1 << pin; + + gpio_debug_printf("lwip: imxrt-gpio: gpio_setPin: gpio=%d, pin=%d, state=%d\n", gpio, pin, state); + gpio_debug_printf("lwip: imxrt-gpio: gpio_setPin: sending message to GPIO:\n"); + gpio_debug_printf(IMSG_STRUCT_FORMAT_STR, imsg->gpio.type, imsg->gpio.dir.val, imsg->gpio.dir.mask); + + return msgSend(multidrv.port, &msg); +} + + +int gpio_setDir(int gpio, int pin, int dir) +{ + msg_t msg; + multi_i_t *imsg = NULL; + gpio_msgHeader(&msg, gpio); + + imsg = (multi_i_t *)msg.i.raw; + + imsg->gpio.type = gpio_set_dir; + imsg->gpio.dir.val = itobool(dir << pin); + imsg->gpio.dir.mask = 1 << pin; + + gpio_debug_printf("lwip: imxrt-gpio: gpio_setDir: gpio=%d, pin=%d, dir=%s (%d)\n", + gpio, pin, + dir == GPIO_INPUT ? + "INPUT" : + "OUTPUT", + dir); + gpio_debug_printf("lwip: imxrt-gpio: gpio_setDir: sending message to GPIO:\n"); + gpio_debug_printf(IMSG_STRUCT_FORMAT_STR, imsg->gpio.type, imsg->gpio.dir.val, imsg->gpio.dir.mask); + + return msgSend(multidrv.port, &msg); +} + +int gpio_set(gpio_info_t *gp, int active) +{ + if (!gpio_valid(gp)) { + return -EINVAL; + } + + if ((gp->flags & GPIO_INVERTED) != 0) { + active = !active; + } + + return gpio_setPin(gp->num, gp->pin, active); +} + + +uint32_t gpio_get(gpio_info_t *gp) +{ + if (!gpio_valid(gp)) { + gpio_debug_printf("imxrt106x-gpio: gpio_get: gpio not valid, returning 0\n"); + return 0; + } + + uint32_t buf = 0; + gpio_getPin(gp->num, gp->pin, &buf); + + return buf; +} + +int gpio_wait(gpio_info_t *gp, int active, struct timespec *timeout) +{ + struct timespec when, now; + uint32_t val; + + if (!gpio_valid(gp)) { + return -EINVAL; + } + + clock_gettime(CLOCK_MONOTONIC, &now); + when = time_add(&now, timeout); + + for (;;) { + val = gpio_get(gp); + + if (itobool((1 << gp->pin) & val) == itobool(active)) { + gpio_debug_printf("imxrt106x-gpio: gpio_wait: gp->pin=%d, 1 << gp->pin=0x%08x, val=0x%08x, (1 << gp->pin) & val=%u, active=%u\n", + gp->pin, 1 << gp->pin, val, (1 << gp->pin) & val, active); + return EOK; + } + + if (timeout != NULL) { + clock_gettime(CLOCK_MONOTONIC, &now); + if (time_cmp(&now, &when) >= 0) { + gpio_debug_printf("imxrt106x-gpio: gpio_wait: timeout\n"); + return -ETIME; + } + } + usleep(100 * 1000); // 100ms + } +} + +int gpio_init(gpio_info_t *gp, const char *arg, unsigned flags) +{ + gpio_debug_printf("lwip: imxrt-gpio: init started, args: `%s`, flags: 0x%08x\n", arg, flags); + + char buf[64]; + char *endp; + int err; + + if (*arg == '-') { + ++arg; + flags |= GPIO_INVERTED; + } + + gp->pin = strtoul(arg, &endp, 0); + + if ((*endp != ',' && *endp != ':') || gp->pin >= (sizeof(gp->pin) * 8)) { + return -EINVAL; + } + + arg = endp + 1; + + if (*arg == '\0') { + return -EINVAL; + } + + if (strlen(arg) > sizeof(buf) - 6) { + return -EINVAL; + } + + gp->flags = flags & ~(GPIO_ACTIVE | GPIO_INITIALIZED); + + if (multidrv.port == 0) { + while (lookup(arg, NULL, &multidrv) < 0) { + usleep(100 * 1000); // 100ms + } + } + + gp->num = ID_GPIO(gpio_getNum(arg)); + + gpio_debug_printf("lwip: imxrt-gpio: got multidrv, oid=%u (0x%08x), port=%d (0x%08x)\n", + multidrv.id, multidrv.id, multidrv.port, multidrv.port); + gpio_debug_printf("lwip: imxrt-gpio: before sending messages, gp={.num=%u, .pin=%u, .flags=0x%08x}\n", + gp->num, gp->pin, gp->flags); + + err = gpio_setDir(gp->num, gp->pin, itobool(flags & GPIO_OUTPUT)); + if (err != 0) { + printf("lwip: imxrt-gpio: WARN: can't configure pin direction: %s (%d)\n", strerror(err), err); + return err; + } + + err = gpio_setPin(gp->num, gp->pin, itobool(flags & GPIO_ACTIVE) ^ itobool(flags & GPIO_INVERTED)); + if (err != 0) { + printf("lwip: imxrt-gpio: WARN: can't pre-set pin: %s (%d)\n", strerror(err), err); + return err; + } + + gp->flags |= GPIO_INITIALIZED; + return 0; +} diff --git a/drivers/time-util.c b/drivers/time-util.c new file mode 100644 index 0000000..7c84e7c --- /dev/null +++ b/drivers/time-util.c @@ -0,0 +1,42 @@ +/* + * Phoenix-RTOS --- networking stack + * + * Utilities: time math helpers + * + * Copyright 2024 Phoenix Systems + * Author: Julian Uziembło + * + * %LICENSE% + */ +#include "time-util.h" + +#define ONE_SECOND_NS (1000 * 1000 * 1000) + + +int time_cmp(const struct timespec *t1, const struct timespec *t2) +{ + if (t1->tv_sec == t2->tv_sec) { + return t1->tv_nsec - t2->tv_nsec; + } + return t1->tv_sec - t2->tv_sec; +} + + +struct timespec time_add(const struct timespec *t1, const struct timespec *t2) +{ + struct timespec t = { .tv_sec = 0, .tv_nsec = 0 }; + if (t1 != NULL) { + t.tv_sec += t1->tv_sec; + t.tv_nsec += t1->tv_nsec; + } + if (t2 != NULL) { + t.tv_sec += t2->tv_sec; + t.tv_nsec += t2->tv_nsec; + } + if (t.tv_nsec >= ONE_SECOND_NS) { + t.tv_sec += t.tv_nsec / ONE_SECOND_NS; + t.tv_nsec %= ONE_SECOND_NS; + } + + return t; +} diff --git a/drivers/time-util.h b/drivers/time-util.h new file mode 100644 index 0000000..7c3736e --- /dev/null +++ b/drivers/time-util.h @@ -0,0 +1,33 @@ +/* + * Phoenix-RTOS --- networking stack + * + * Utilities: time math helpers + * + * Copyright 2024 Phoenix Systems + * Author: Julian Uziembło + * + * %LICENSE% + */ +#ifndef NETLIB_TIME_UTIL_H_ +#define NETLIB_TIME_UTIL_H_ + +#include + + +/* + * Compares 2 timespecs + * return: + * < 0 if t1 < t2 + * 0 if t1 == t2 + * > 0 if t1 > t2 + */ +int time_cmp(const struct timespec *t1, const struct timespec *t2); + + +/* + * Adds t1 and t2 (skipping if NULL) + */ +struct timespec time_add(const struct timespec *t1, const struct timespec *t2); + + +#endif /* NETLIB_TIME_UTIL_H_ */