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:
parent
dd8072abb3
commit
2cebc0f307
|
@ -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, ®);
|
||||
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, ®);
|
||||
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, ®);
|
||||
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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue