Commit 77fc3eaf authored by Volodymyr Shymanskyy's avatar Volodymyr Shymanskyy Committed by Alberto Escolar
Browse files

drivers: ethernet: w5500: Add link status detection



Implemented link status detection using the W5500 built-in registers.
Added periodic link status polling, configurable via Kconfig.
Speeds up DHCPv4 from 9 seconds at best, to ~2 seconds after power-up.

Signed-off-by: default avatarVolodymyr Shymanskyy <vshymanskyi@gmail.com>
parent 487a14c0
Loading
Loading
Loading
Loading
+58 −18
Original line number Diff line number Diff line
@@ -275,6 +275,30 @@ static void w5500_rx(const struct device *dev)
	w5500_command(dev, S0_CR_RECV);
}

static void w5500_update_link_status(const struct device *dev)
{
	uint8_t phycfgr;
	struct w5500_runtime *ctx = dev->data;

	if (w5500_spi_read(dev, W5500_PHYCFGR, &phycfgr, 1) < 0) {
		return;
	}

	if (phycfgr & 0x01) {
		if (ctx->link_up != true) {
			LOG_INF("%s: Link up", dev->name);
			ctx->link_up = true;
			net_eth_carrier_on(ctx->iface);
		}
	} else {
		if (ctx->link_up != false) {
			LOG_INF("%s: Link down", dev->name);
			ctx->link_up = false;
			net_eth_carrier_off(ctx->iface);
		}
	}
}

static void w5500_thread(void *p1, void *p2, void *p3)
{
	ARG_UNUSED(p2);
@@ -282,11 +306,18 @@ static void w5500_thread(void *p1, void *p2, void *p3)

	const struct device *dev = p1;
	uint8_t ir;
	int res;
	struct w5500_runtime *ctx = dev->data;
	const struct w5500_config *config = dev->config;

	while (true) {
		k_sem_take(&ctx->int_sem, K_FOREVER);
		res = k_sem_take(&ctx->int_sem, K_MSEC(CONFIG_PHY_MONITOR_PERIOD));

		if (res == 0) {
			/* semaphore taken, update link status and receive packets */
			if (ctx->link_up != true) {
				w5500_update_link_status(dev);
			}

			while (gpio_pin_get_dt(&(config->interrupt))) {
				/* Read interrupt */
@@ -309,6 +340,10 @@ static void w5500_thread(void *p1, void *p2, void *p3)
					}
				}
			}
		} else if (res == -EAGAIN) {
			/* semaphore timeout period expired, check link status */
			w5500_update_link_status(dev);
		}
	}
}

@@ -326,6 +361,9 @@ static void w5500_iface_init(struct net_if *iface)
	}

	ethernet_init(iface);

	/* Do not start the interface until PHY link is up */
	net_if_carrier_off(iface);
}

static enum ethernet_hw_caps w5500_get_capabilities(const struct device *dev)
@@ -505,6 +543,8 @@ static int w5500_init(const struct device *dev)
	const struct w5500_config *config = dev->config;
	struct w5500_runtime *ctx = dev->data;

	ctx->link_up = false;

	if (!spi_is_ready_dt(&config->spi)) {
		LOG_ERR("SPI master port %s not ready", config->spi.bus->name);
		return -EINVAL;
+2 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@
#define W5500_SHAR		0x0009 /* Source MAC address */
#define W5500_IR		0x0015 /* Interrupt Register */
#define W5500_COMMON_REGS_LEN	0x0040
#define W5500_PHYCFGR		0x002E /* PHY Configuration register */

#define W5500_Sn_MR		0x0000 /* Sn Mode Register */
#define W5500_Sn_CR		0x0001 /* Sn Command Register */
@@ -97,6 +98,7 @@ struct w5500_runtime {
	struct gpio_callback gpio_cb;
	struct k_sem tx_sem;
	struct k_sem int_sem;
	bool link_up;
	void (*generate_mac)(uint8_t *mac);
	uint8_t buf[NET_ETH_MAX_FRAME_SIZE];
};