drivers: ethernet: eth_adin2111: extend api support

Extend api support with new functionalities.

Signed-off-by: Angelo Dureghello <adureghello@baylibre.com>
This commit is contained in:
Angelo Dureghello 2024-03-15 13:11:15 +01:00 committed by Fabio Baltieri
parent dd8072abb3
commit 2cebc0f307
3 changed files with 272 additions and 11 deletions

View file

@ -48,11 +48,71 @@ LOG_MODULE_REGISTER(eth_adin2111, CONFIG_ETHERNET_LOG_LEVEL);
#define ADIN2111_UNICAST_P1_ADDR_SLOT 2U
/* MAC Address Rule and DA Filter Port 2 slot/idx */
#define ADIN2111_UNICAST_P2_ADDR_SLOT 3U
/* Free slots for further filtering */
#define ADIN2111_FILTER_FIRST_SLOT 4U
#define ADIN2111_FILTER_SLOTS 16U
/* As per RM rev. A table 3, t3 >= 50ms, delay for SPI interface to be ready */
#define ADIN2111_SPI_ACTIVE_DELAY_MS 50U
/* As per RM rev. A page 20: approximately 10 ms (maximum) for internal logic to be ready */
/* As per RM rev. A page 20: approximately 10 ms (maximum) for internal logic to be ready. */
#define ADIN2111_SW_RESET_DELAY_MS 10U
int eth_adin2111_mac_reset(const struct device *dev)
{
uint32_t val;
int ret;
ret = eth_adin2111_reg_write(dev, ADIN2111_SOFT_RST_REG, ADIN2111_SWRESET_KEY1);
if (ret < 0) {
return ret;
}
ret = eth_adin2111_reg_write(dev, ADIN2111_SOFT_RST_REG, ADIN2111_SWRESET_KEY2);
if (ret < 0) {
return ret;
}
ret = eth_adin2111_reg_write(dev, ADIN2111_SOFT_RST_REG, ADIN2111_SWRELEASE_KEY1);
if (ret < 0) {
return ret;
}
ret = eth_adin2111_reg_write(dev, ADIN2111_SOFT_RST_REG, ADIN2111_SWRELEASE_KEY2);
if (ret < 0) {
return ret;
}
ret = eth_adin2111_reg_read(dev, ADIN1110_MAC_RST_STATUS_REG, &val);
if (ret < 0) {
return ret;
}
if (val == 0) {
return -EBUSY;
}
return 0;
}
int eth_adin2111_reg_update(const struct device *dev, const uint16_t reg,
uint32_t mask, uint32_t data)
{
uint32_t val;
int ret;
ret = eth_adin2111_reg_read(dev, reg, &val);
if (ret < 0) {
return ret;
}
val &= ~mask;
val |= mask & data;
return eth_adin2111_reg_write(dev, reg, val);
}
struct net_if *eth_adin2111_get_iface(const struct device *dev, const uint16_t port_idx)
{
struct adin2111_data *ctx = dev->data;
return ((struct adin2111_port_data *)ctx->port[port_idx]->data)->iface;
}
int eth_adin2111_lock(const struct device *dev, k_timeout_t timeout)
{
struct adin2111_data *ctx = dev->data;
@ -972,6 +1032,97 @@ static int adin2111_filter_unicast(const struct device *dev, uint8_t *addr,
return adin2111_write_filter_address(dev, addr, NULL, rules, slot);
}
int eth_adin2111_broadcast_filter(const struct device *dev, bool enable)
{
if (!enable) {
/* Clean up */
uint8_t mac[NET_ETH_ADDR_LEN] = {0};
return adin2111_write_filter_address(dev, mac, mac, 0,
ADIN2111_BROADCAST_ADDR_SLOT);
}
return adin2111_filter_broadcast(dev);
}
/*
* Check if a filter exists already.
*/
static int eth_adin2111_find_filter(const struct device *dev, uint8_t *mac, const uint16_t port_idx)
{
int i, offset, reg, ret;
for (i = ADIN2111_FILTER_FIRST_SLOT; i < ADIN2111_FILTER_SLOTS; i++) {
offset = i << 1;
ret = eth_adin2111_reg_read(dev, ADIN2111_ADDR_FILT_UPR + offset, &reg);
if (ret < 0) {
return ret;
}
if ((reg & UINT16_MAX) == sys_get_be16(&mac[0])) {
if ((port_idx == 0 && !(reg & ADIN2111_ADDR_APPLY2PORT1)) ||
(port_idx == 1 && !(reg & ADIN2111_ADDR_APPLY2PORT2)))
continue;
ret = eth_adin2111_reg_read(dev, ADIN2111_ADDR_FILT_LWR + offset, &reg);
if (ret < 0) {
return ret;
}
if (reg == sys_get_be32(&mac[2])) {
return i;
}
}
}
return -ENOENT;
}
static int eth_adin2111_set_mac_filter(const struct device *dev, uint8_t *mac,
const uint16_t port_idx)
{
int i, ret, offset;
uint32_t reg;
ret = eth_adin2111_find_filter(dev, mac, port_idx);
if (ret >= 0) {
LOG_WRN("MAC filter already set at pos %d, not setting it.", ret);
return ret;
}
if (ret != -ENOENT) {
return ret;
}
for (i = ADIN2111_FILTER_FIRST_SLOT; i < ADIN2111_FILTER_SLOTS; i++) {
offset = i << 1;
ret = eth_adin2111_reg_read(dev, ADIN2111_ADDR_FILT_UPR + offset, &reg);
if (ret < 0) {
return ret;
}
if (reg == 0) {
uint32_t rules = (port_idx == 0 ? ADIN2111_ADDR_APPLY2PORT1
: ADIN2111_ADDR_APPLY2PORT2)
| ADIN2111_ADDR_TO_HOST;
return adin2111_write_filter_address(dev, mac, NULL, rules, i);
}
}
return -ENOSPC;
}
static int eth_adin2111_clear_mac_filter(const struct device *dev, uint8_t *mac,
const uint16_t port_idx)
{
int i;
uint8_t cmac[NET_ETH_ADDR_LEN] = {0};
i = eth_adin2111_find_filter(dev, mac, port_idx);
if (i < 0) {
return i;
}
return adin2111_write_filter_address(dev, cmac, cmac, 0, i);
}
static void adin2111_port_iface_init(struct net_if *iface)
{
const struct device *dev = net_if_get_device(iface);
@ -1044,7 +1195,8 @@ static void adin2111_port_iface_init(struct net_if *iface)
static enum ethernet_hw_caps adin2111_port_get_capabilities(const struct device *dev)
{
ARG_UNUSED(dev);
return ETHERNET_LINK_10BASE_T
return ETHERNET_LINK_10BASE_T |
ETHERNET_HW_FILTERING
#if defined(CONFIG_NET_LLDP)
| ETHERNET_LLDP
#endif
@ -1075,6 +1227,19 @@ static int adin2111_port_set_config(const struct device *dev,
NET_LINK_ETHERNET);
}
if (type == ETHERNET_CONFIG_TYPE_FILTER) {
/* Filtering for DA only */
if (config->filter.type & ETHERNET_FILTER_TYPE_DST_MAC_ADDRESS) {
uint8_t *mac = (uint8_t *)config->filter.mac_address.addr;
if (config->filter.set) {
ret = eth_adin2111_set_mac_filter(adin, mac, cfg->port_idx);
} else {
ret = eth_adin2111_clear_mac_filter(adin, mac, cfg->port_idx);
}
}
}
end_unlock:
(void)eth_adin2111_unlock(adin);
return ret;
@ -1141,6 +1306,26 @@ static int adin2111_await_device(const struct device *dev)
return ret;
}
int eth_adin2111_sw_reset(const struct device *dev, uint16_t delay)
{
int ret;
ret = eth_adin2111_reg_write(dev, ADIN2111_RESET, ADIN2111_RESET_SWRESET);
if (ret < 0) {
return ret;
}
k_msleep(delay);
ret = adin2111_await_device(dev);
if (ret < 0) {
LOG_ERR("ADIN did't come out of the reset, %d", ret);
return ret;
}
return ret;
}
static int adin2111_init(const struct device *dev)
{
const struct adin2111_config *cfg = dev->config;
@ -1210,20 +1395,12 @@ static int adin2111_init(const struct device *dev)
}
/* perform MACPHY soft reset */
ret = eth_adin2111_reg_write(dev, ADIN2111_RESET, ADIN2111_RESET_SWRESET);
ret = eth_adin2111_sw_reset(dev, ADIN2111_SW_RESET_DELAY_MS);
if (ret < 0) {
LOG_ERR("MACPHY software reset failed, %d", ret);
return ret;
}
k_msleep(ADIN2111_SW_RESET_DELAY_MS);
ret = adin2111_await_device(dev);
if (ret < 0) {
LOG_ERR("ADIN did't come out of the reset, %d", ret);
return ret;
}
/* CONFIG 0 */
/* disable Frame Check Sequence validation on the host */
/* if that is enabled, then CONFIG_ETH_ADIN2111_SPI_CFG0 must be off */

View file

@ -48,10 +48,14 @@
#define ADIN2111_CONFIG2_P2_FWD_UNK2P1 BIT(14)
/* Forward Frames from Port 1 Not Matching a MAC Address to Port 2 */
#define ADIN2111_CONFIG2_P1_FWD_UNK2P2 BIT(13)
/* Forward Frames Not Matching Any MAC Address to the Host */
#define ADIN2111_CONFIG2_P2_FWD_UNK2HOST BIT(12)
/* Enable Cut Through from Port to Port */
#define ADIN2111_CONFIG2_PORT_CUT_THRU_EN BIT(11)
/* Enable CRC Append */
#define ADIN2111_CONFIG2_CRC_APPEND BIT(5)
/* Forward Frames Not Matching Any MAC Address to the Host */
#define ADIN2111_CONFIG2_P1_FWD_UNK2HOST BIT(2)
/* Status Register 0 */
#define ADIN2111_STATUS0 0x08U
@ -136,6 +140,16 @@
/* P2 MAC Receive Register */
#define ADIN2111_P2_RX 0xC1U
/* MAC reset status */
#define ADIN1110_MAC_RST_STATUS_REG 0x3BU
/* MAC reset */
#define ADIN2111_SOFT_RST_REG 0x3CU
#define ADIN2111_SWRESET_KEY1 0x4F1CU
#define ADIN2111_SWRESET_KEY2 0xC1F4U
#define ADIN2111_SWRELEASE_KEY1 0x6F1AU
#define ADIN2111_SWRELEASE_KEY2 0xA1F6U
/* SPI header size in bytes */
#define ADIN2111_SPI_HEADER_SIZE 2U
/* SPI header size for write transaction */

View file

@ -70,6 +70,76 @@ int eth_adin2111_reg_write(const struct device *dev, const uint16_t reg, uint32_
*/
int eth_adin2111_reg_read(const struct device *dev, const uint16_t reg, uint32_t *val);
/**
* @brief Update host MAC interface register over SPI
*
* @note The caller is responsible for device lock.
* Shall not be called from ISR.
*
* @param[in] dev ADIN2111 device.
* @param reg Register address.
* @param mask Bitmask for bits that may be modified.
* @param data Data to apply in the masked range.
*
* @retval 0 Successful write.
* @retval <0 Error, a negative errno code.
*/
int eth_adin2111_reg_update(const struct device *dev, const uint16_t reg,
uint32_t mask, uint32_t data);
/**
* @brief Reset both the MAC and PHY.
*
* @param[in] dev ADIN2111 device.
* @param delay Delay in milliseconds.
*
* @note The caller is responsible for device lock.
* Shall not be called from ISR.
*
* @retval 0 Successful write.
* @retval <0 Error, a negative errno code.
*/
int eth_adin2111_sw_reset(const struct device *dev, uint16_t delay);
/**
* @brief Reset the MAC device. Note that PHY 1 must be out of software power-down for the MAC
* subsystem reset to take effect.
*
* @note The caller is responsible for device lock.
* Shall not be called from ISR.
*
* @param[in] dev ADIN2111 device.
*
* @retval 0 Successful write.
* @retval <0 Error, a negative errno code.
*/
int eth_adin2111_mac_reset(const struct device *dev);
/**
* @brief Enable/disable the forwarding (to host) of broadcast frames. Frames who's DA
* doesn't match are dropped.
*
* @note The caller is responsible for device lock.
* Shall not be called from ISR.
*
* @param[in] dev ADIN2111 device.
* @param enable Set to 0 to disable and to nonzero to enable.
*
* @retval 0 Successful write.
* @retval <0 Error, a negative errno code.
*/
int eth_adin2111_broadcast_filter(const struct device *dev, bool enable);
/**
* @brief Get the port-related net_if reference.
*
* @param[in] dev ADIN2111 device.
* @param port_idx Port index.
*
* @retval a struct net_if pointer, or NULL on error.
*/
struct net_if *eth_adin2111_get_iface(const struct device *dev, const uint16_t port_idx);
#ifdef __cplusplus
}
#endif