2020-03-26 15:21:48 +01:00
|
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2020 DENX Software Engineering GmbH
|
|
|
|
|
* Lukasz Majewski <lukma@denx.de>
|
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#define LOG_MODULE_NAME dsa
|
|
|
|
|
|
|
|
|
|
#include <logging/log.h>
|
|
|
|
|
LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_ETHERNET_LOG_LEVEL);
|
|
|
|
|
|
|
|
|
|
#include <device.h>
|
|
|
|
|
#include <kernel.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <sys/util.h>
|
|
|
|
|
#include <net/ethernet.h>
|
|
|
|
|
#include <linker/sections.h>
|
2021-05-05 23:14:01 +02:00
|
|
|
|
|
|
|
|
|
#if defined(CONFIG_DSA_SPI)
|
|
|
|
|
#include <drivers/spi.h>
|
|
|
|
|
#else
|
|
|
|
|
#error "No communication bus defined"
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if CONFIG_DSA_KSZ8863
|
|
|
|
|
#define DT_DRV_COMPAT microchip_ksz8863
|
|
|
|
|
#include "dsa_ksz8863.h"
|
|
|
|
|
#elif CONFIG_DSA_KSZ8794
|
|
|
|
|
#define DT_DRV_COMPAT microchip_ksz8794
|
2020-03-26 15:21:48 +01:00
|
|
|
|
#include "dsa_ksz8794.h"
|
2021-05-05 23:14:01 +02:00
|
|
|
|
#else
|
|
|
|
|
#error "Unsupported KSZ chipset"
|
|
|
|
|
#endif
|
2020-03-26 15:21:48 +01:00
|
|
|
|
|
2021-05-06 00:17:09 +02:00
|
|
|
|
struct ksz8xxx_data {
|
2021-05-01 21:47:00 +02:00
|
|
|
|
int iface_init_count;
|
2021-05-01 21:42:26 +02:00
|
|
|
|
bool is_init;
|
2021-05-05 23:14:01 +02:00
|
|
|
|
#if defined(CONFIG_DSA_SPI)
|
2021-12-08 22:25:26 +01:00
|
|
|
|
struct spi_dt_spec spi;
|
2021-05-05 23:14:01 +02:00
|
|
|
|
#endif
|
2021-05-01 21:42:26 +02:00
|
|
|
|
};
|
|
|
|
|
|
2021-05-06 00:17:09 +02:00
|
|
|
|
#define PRV_DATA(ctx) ((struct ksz8xxx_data *const)(ctx)->prv_data)
|
2021-05-01 21:42:26 +02:00
|
|
|
|
|
2021-05-06 00:17:09 +02:00
|
|
|
|
static void dsa_ksz8xxx_write_reg(const struct ksz8xxx_data *pdev,
|
2020-03-26 15:21:48 +01:00
|
|
|
|
uint16_t reg_addr, uint8_t value)
|
|
|
|
|
{
|
2021-05-05 23:14:01 +02:00
|
|
|
|
#if defined(CONFIG_DSA_SPI)
|
2020-03-26 15:21:48 +01:00
|
|
|
|
uint8_t buf[3];
|
|
|
|
|
|
|
|
|
|
const struct spi_buf tx_buf = {
|
|
|
|
|
.buf = buf,
|
|
|
|
|
.len = 3
|
|
|
|
|
};
|
|
|
|
|
const struct spi_buf_set tx = {
|
|
|
|
|
.buffers = &tx_buf,
|
|
|
|
|
.count = 1
|
|
|
|
|
};
|
|
|
|
|
|
2021-05-05 23:14:01 +02:00
|
|
|
|
buf[0] = KSZ8XXX_SPI_CMD_WR | ((reg_addr >> 7) & 0x1F);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
buf[1] = (reg_addr << 1) & 0xFE;
|
|
|
|
|
buf[2] = value;
|
|
|
|
|
|
2021-12-08 22:25:26 +01:00
|
|
|
|
spi_write_dt(&pdev->spi, &tx);
|
2021-05-05 23:14:01 +02:00
|
|
|
|
#endif
|
2020-03-26 15:21:48 +01:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-06 00:17:09 +02:00
|
|
|
|
static void dsa_ksz8xxx_read_reg(const struct ksz8xxx_data *pdev,
|
2020-03-26 15:21:48 +01:00
|
|
|
|
uint16_t reg_addr, uint8_t *value)
|
|
|
|
|
{
|
2021-05-05 23:14:01 +02:00
|
|
|
|
#if defined(CONFIG_DSA_SPI)
|
2020-03-26 15:21:48 +01:00
|
|
|
|
uint8_t buf[3];
|
|
|
|
|
|
|
|
|
|
const struct spi_buf tx_buf = {
|
|
|
|
|
.buf = buf,
|
|
|
|
|
.len = 3
|
|
|
|
|
};
|
|
|
|
|
const struct spi_buf_set tx = {
|
|
|
|
|
.buffers = &tx_buf,
|
|
|
|
|
.count = 1
|
|
|
|
|
};
|
|
|
|
|
struct spi_buf rx_buf = {
|
|
|
|
|
.buf = buf,
|
|
|
|
|
.len = 3
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const struct spi_buf_set rx = {
|
|
|
|
|
.buffers = &rx_buf,
|
|
|
|
|
.count = 1
|
|
|
|
|
};
|
|
|
|
|
|
2021-05-05 23:14:01 +02:00
|
|
|
|
buf[0] = KSZ8XXX_SPI_CMD_RD | ((reg_addr >> 7) & 0x1F);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
buf[1] = (reg_addr << 1) & 0xFE;
|
|
|
|
|
buf[2] = 0x0;
|
|
|
|
|
|
2021-12-08 22:25:26 +01:00
|
|
|
|
if (!spi_transceive_dt(&pdev->spi, &tx, &rx)) {
|
2020-03-26 15:21:48 +01:00
|
|
|
|
*value = buf[2];
|
|
|
|
|
} else {
|
|
|
|
|
LOG_DBG("Failure while reading register 0x%04x", reg_addr);
|
|
|
|
|
*value = 0U;
|
|
|
|
|
}
|
2021-05-05 23:14:01 +02:00
|
|
|
|
#endif
|
2020-03-26 15:21:48 +01:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-06 00:17:09 +02:00
|
|
|
|
static bool dsa_ksz8xxx_port_link_status(struct ksz8xxx_data *pdev,
|
2020-03-26 15:21:48 +01:00
|
|
|
|
uint8_t port)
|
|
|
|
|
{
|
|
|
|
|
uint8_t tmp;
|
|
|
|
|
|
2021-05-05 23:14:01 +02:00
|
|
|
|
if (port < KSZ8XXX_FIRST_PORT || port > KSZ8XXX_LAST_PORT ||
|
|
|
|
|
port == KSZ8XXX_CPU_PORT) {
|
2020-03-26 15:21:48 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-06 00:17:09 +02:00
|
|
|
|
dsa_ksz8xxx_read_reg(pdev, KSZ8XXX_STAT2_PORTn(port), &tmp);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
|
2021-05-05 23:14:01 +02:00
|
|
|
|
return tmp & KSZ8XXX_STAT2_LINK_GOOD;
|
2020-03-26 15:21:48 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if !DT_INST_NODE_HAS_PROP(0, reset_gpios)
|
2021-05-06 00:17:09 +02:00
|
|
|
|
static void dsa_ksz8xxx_soft_reset(struct ksz8xxx_data *pdev)
|
2020-03-26 15:21:48 +01:00
|
|
|
|
{
|
|
|
|
|
/* reset switch */
|
2021-05-06 00:17:09 +02:00
|
|
|
|
dsa_ksz8xxx_write_reg(pdev, KSZ8XXX_RESET_REG,
|
2021-05-05 23:14:01 +02:00
|
|
|
|
KSZ8XXX_RESET_SET);
|
|
|
|
|
k_busy_wait(KSZ8XXX_SOFT_RESET_DURATION);
|
2021-05-06 00:17:09 +02:00
|
|
|
|
dsa_ksz8xxx_write_reg(pdev, KSZ8XXX_RESET_REG, KSZ8XXX_RESET_CLEAR);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2021-05-06 00:17:09 +02:00
|
|
|
|
static int dsa_ksz8xxx_probe(struct ksz8xxx_data *pdev)
|
2020-03-26 15:21:48 +01:00
|
|
|
|
{
|
|
|
|
|
uint16_t timeout = 100;
|
|
|
|
|
uint8_t val[2], tmp;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Wait for SPI of KSZ8794 being fully operational - up to 10 ms
|
|
|
|
|
*/
|
|
|
|
|
for (timeout = 100, tmp = 0;
|
2021-05-05 23:14:01 +02:00
|
|
|
|
tmp != KSZ8XXX_CHIP_ID0_ID_DEFAULT && timeout > 0; timeout--) {
|
2021-05-06 00:17:09 +02:00
|
|
|
|
dsa_ksz8xxx_read_reg(pdev, KSZ8XXX_CHIP_ID0, &tmp);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
k_busy_wait(100);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (timeout == 0) {
|
|
|
|
|
LOG_ERR("KSZ8794: No SPI communication!");
|
|
|
|
|
return -ENODEV;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-06 00:17:09 +02:00
|
|
|
|
dsa_ksz8xxx_read_reg(pdev, KSZ8XXX_CHIP_ID0, &val[0]);
|
|
|
|
|
dsa_ksz8xxx_read_reg(pdev, KSZ8XXX_CHIP_ID1, &val[1]);
|
2021-05-05 23:14:01 +02:00
|
|
|
|
|
|
|
|
|
if (val[0] != KSZ8XXX_CHIP_ID0_ID_DEFAULT ||
|
|
|
|
|
val[1] != KSZ8XXX_CHIP_ID1_ID_DEFAULT) {
|
|
|
|
|
LOG_ERR("Chip ID mismatch. "
|
|
|
|
|
"Expected %02x%02x but found %02x%02x",
|
|
|
|
|
KSZ8XXX_CHIP_ID0_ID_DEFAULT,
|
|
|
|
|
KSZ8XXX_CHIP_ID1_ID_DEFAULT,
|
|
|
|
|
val[0],
|
|
|
|
|
val[1]);
|
|
|
|
|
return -ENODEV;
|
|
|
|
|
}
|
2020-03-26 15:21:48 +01:00
|
|
|
|
|
|
|
|
|
LOG_DBG("KSZ8794: ID0: 0x%x ID1: 0x%x timeout: %d", val[1], val[0],
|
|
|
|
|
timeout);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-06 00:17:09 +02:00
|
|
|
|
static int dsa_ksz8xxx_write_static_mac_table(struct ksz8xxx_data *pdev,
|
2020-03-26 15:21:48 +01:00
|
|
|
|
uint16_t entry_addr, uint8_t *p)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* According to KSZ8794 manual - write to static mac address table
|
|
|
|
|
* requires write to indirect registers:
|
|
|
|
|
* Write register 0x71 (113)
|
|
|
|
|
* ....
|
|
|
|
|
* Write register 0x78 (120)
|
|
|
|
|
*
|
|
|
|
|
* Then:
|
|
|
|
|
* Write to Register 110 with 0x00 (write static table selected)
|
|
|
|
|
* Write to Register 111 with 0x0x (trigger the write operation, to
|
|
|
|
|
* table entry x)
|
|
|
|
|
*/
|
2021-05-06 00:17:09 +02:00
|
|
|
|
dsa_ksz8xxx_write_reg(pdev, KSZ8XXX_REG_IND_DATA_7, p[7]);
|
|
|
|
|
dsa_ksz8xxx_write_reg(pdev, KSZ8XXX_REG_IND_DATA_6, p[6]);
|
|
|
|
|
dsa_ksz8xxx_write_reg(pdev, KSZ8XXX_REG_IND_DATA_5, p[5]);
|
|
|
|
|
dsa_ksz8xxx_write_reg(pdev, KSZ8XXX_REG_IND_DATA_4, p[4]);
|
|
|
|
|
dsa_ksz8xxx_write_reg(pdev, KSZ8XXX_REG_IND_DATA_3, p[3]);
|
|
|
|
|
dsa_ksz8xxx_write_reg(pdev, KSZ8XXX_REG_IND_DATA_2, p[2]);
|
|
|
|
|
dsa_ksz8xxx_write_reg(pdev, KSZ8XXX_REG_IND_DATA_1, p[1]);
|
|
|
|
|
dsa_ksz8xxx_write_reg(pdev, KSZ8XXX_REG_IND_DATA_0, p[0]);
|
|
|
|
|
|
|
|
|
|
dsa_ksz8xxx_write_reg(pdev, KSZ8XXX_REG_IND_CTRL_0, 0x00);
|
|
|
|
|
dsa_ksz8xxx_write_reg(pdev, KSZ8XXX_REG_IND_CTRL_1, entry_addr);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-06 00:17:09 +02:00
|
|
|
|
static int dsa_ksz8xxx_set_static_mac_table(struct ksz8xxx_data *pdev,
|
2020-03-26 15:21:48 +01:00
|
|
|
|
const uint8_t *mac, uint8_t fw_port,
|
|
|
|
|
uint16_t entry_idx)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* The data in uint8_t buf[] buffer is stored in the little endian
|
|
|
|
|
* format, as it eases programming proper KSZ8794 registers.
|
|
|
|
|
*/
|
|
|
|
|
uint8_t buf[8];
|
|
|
|
|
|
|
|
|
|
buf[7] = 0;
|
|
|
|
|
/* Prepare entry for static MAC address table */
|
|
|
|
|
buf[5] = mac[0];
|
|
|
|
|
buf[4] = mac[1];
|
|
|
|
|
buf[3] = mac[2];
|
|
|
|
|
buf[2] = mac[3];
|
|
|
|
|
buf[1] = mac[4];
|
|
|
|
|
buf[0] = mac[5];
|
|
|
|
|
|
|
|
|
|
buf[6] = fw_port;
|
2021-05-05 23:14:01 +02:00
|
|
|
|
buf[6] |= KSZ8XXX_STATIC_MAC_TABLE_VALID;
|
|
|
|
|
buf[6] |= KSZ8XXX_STATIC_MAC_TABLE_OVRD;
|
2020-03-26 15:21:48 +01:00
|
|
|
|
|
2021-05-06 00:17:09 +02:00
|
|
|
|
dsa_ksz8xxx_write_static_mac_table(pdev, entry_idx, buf);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-06 00:17:09 +02:00
|
|
|
|
static int dsa_ksz8xxx_read_static_mac_table(struct ksz8xxx_data *pdev,
|
2020-03-26 15:21:48 +01:00
|
|
|
|
uint16_t entry_addr, uint8_t *p)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* According to KSZ8794 manual - read from static mac address table
|
|
|
|
|
* requires reads from indirect registers:
|
|
|
|
|
*
|
|
|
|
|
* Write to Register 110 with 0x10 (read static table selected)
|
|
|
|
|
* Write to Register 111 with 0x0x (trigger the read operation, to
|
|
|
|
|
* table entry x)
|
|
|
|
|
*
|
|
|
|
|
* Then:
|
|
|
|
|
* Write register 0x71 (113)
|
|
|
|
|
* ....
|
|
|
|
|
* Write register 0x78 (120)
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
2021-05-06 00:17:09 +02:00
|
|
|
|
dsa_ksz8xxx_write_reg(pdev, KSZ8XXX_REG_IND_CTRL_0, 0x10);
|
|
|
|
|
dsa_ksz8xxx_write_reg(pdev, KSZ8XXX_REG_IND_CTRL_1, entry_addr);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
|
2021-05-06 00:17:09 +02:00
|
|
|
|
dsa_ksz8xxx_read_reg(pdev, KSZ8XXX_REG_IND_DATA_7, &p[7]);
|
|
|
|
|
dsa_ksz8xxx_read_reg(pdev, KSZ8XXX_REG_IND_DATA_6, &p[6]);
|
|
|
|
|
dsa_ksz8xxx_read_reg(pdev, KSZ8XXX_REG_IND_DATA_5, &p[5]);
|
|
|
|
|
dsa_ksz8xxx_read_reg(pdev, KSZ8XXX_REG_IND_DATA_4, &p[4]);
|
|
|
|
|
dsa_ksz8xxx_read_reg(pdev, KSZ8XXX_REG_IND_DATA_3, &p[3]);
|
|
|
|
|
dsa_ksz8xxx_read_reg(pdev, KSZ8XXX_REG_IND_DATA_2, &p[2]);
|
|
|
|
|
dsa_ksz8xxx_read_reg(pdev, KSZ8XXX_REG_IND_DATA_1, &p[1]);
|
|
|
|
|
dsa_ksz8xxx_read_reg(pdev, KSZ8XXX_REG_IND_DATA_0, &p[0]);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-05 23:14:01 +02:00
|
|
|
|
#if CONFIG_DSA_KSZ8863
|
2021-05-06 00:17:09 +02:00
|
|
|
|
static int dsa_ksz8xxx_switch_setup(const struct ksz8xxx_data *pdev)
|
2021-05-05 23:14:01 +02:00
|
|
|
|
{
|
|
|
|
|
uint8_t tmp, i;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Loop through ports - The same setup when tail tagging is enabled or
|
|
|
|
|
* disabled.
|
|
|
|
|
*/
|
|
|
|
|
for (i = KSZ8XXX_FIRST_PORT; i <= KSZ8XXX_LAST_PORT; i++) {
|
|
|
|
|
|
|
|
|
|
/* Enable transmission, reception and switch address learning */
|
2021-05-06 00:17:09 +02:00
|
|
|
|
dsa_ksz8xxx_read_reg(pdev, KSZ8863_CTRL2_PORTn(i), &tmp);
|
2021-05-05 23:14:01 +02:00
|
|
|
|
tmp |= KSZ8863_CTRL2_TRANSMIT_EN;
|
|
|
|
|
tmp |= KSZ8863_CTRL2_RECEIVE_EN;
|
|
|
|
|
tmp &= ~KSZ8863_CTRL2_LEARNING_DIS;
|
2021-05-06 00:17:09 +02:00
|
|
|
|
dsa_ksz8xxx_write_reg(pdev, KSZ8863_CTRL2_PORTn(i), tmp);
|
2021-05-05 23:14:01 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if defined(CONFIG_DSA_KSZ_TAIL_TAGGING)
|
|
|
|
|
/* Enable tail tag feature */
|
2021-05-06 00:17:09 +02:00
|
|
|
|
dsa_ksz8xxx_read_reg(pdev, KSZ8863_GLOBAL_CTRL10, &tmp);
|
2021-05-05 23:14:01 +02:00
|
|
|
|
tmp |= KSZ8863_GLOBAL_CTRL1_TAIL_TAG_EN;
|
2021-05-06 00:17:09 +02:00
|
|
|
|
dsa_ksz8xxx_write_reg(pdev, KSZ8863_GLOBAL_CTRL10, tmp);
|
2021-05-05 23:14:01 +02:00
|
|
|
|
#else
|
|
|
|
|
/* Disable tail tag feature */
|
2021-05-06 00:17:09 +02:00
|
|
|
|
dsa_ksz8xxx_read_reg(pdev, KSZ8863_GLOBAL_CTRL1, &tmp);
|
2021-05-05 23:14:01 +02:00
|
|
|
|
tmp &= ~KSZ8863_GLOBAL_CTRL1_TAIL_TAG_EN;
|
2021-05-06 00:17:09 +02:00
|
|
|
|
dsa_ksz8xxx_write_reg(pdev, KSZ8863_GLOBAL_CTRL1, tmp);
|
2021-05-05 23:14:01 +02:00
|
|
|
|
#endif
|
|
|
|
|
|
2021-05-06 00:17:09 +02:00
|
|
|
|
dsa_ksz8xxx_read_reg(pdev, KSZ8863_GLOBAL_CTRL2, &tmp);
|
2021-05-05 23:14:01 +02:00
|
|
|
|
tmp &= ~KSZ8863_GLOBAL_CTRL2_LEG_MAX_PKT_SIZ_CHK_ENA;
|
2021-05-06 00:17:09 +02:00
|
|
|
|
dsa_ksz8xxx_write_reg(pdev, KSZ8863_GLOBAL_CTRL2, tmp);
|
2021-05-05 23:14:01 +02:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if CONFIG_DSA_KSZ8794
|
2021-05-06 00:17:09 +02:00
|
|
|
|
static int dsa_ksz8xxx_switch_setup(struct ksz8xxx_data *pdev)
|
2020-03-26 15:21:48 +01:00
|
|
|
|
{
|
|
|
|
|
uint8_t tmp, i;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Loop through ports - The same setup when tail tagging is enabled or
|
|
|
|
|
* disabled.
|
|
|
|
|
*/
|
2021-05-05 23:14:01 +02:00
|
|
|
|
for (i = KSZ8XXX_FIRST_PORT; i <= KSZ8XXX_LAST_PORT; i++) {
|
2020-03-26 15:21:48 +01:00
|
|
|
|
/* Enable transmission, reception and switch address learning */
|
2021-05-06 00:17:09 +02:00
|
|
|
|
dsa_ksz8xxx_read_reg(pdev, KSZ8794_CTRL2_PORTn(i), &tmp);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
tmp |= KSZ8794_CTRL2_TRANSMIT_EN;
|
|
|
|
|
tmp |= KSZ8794_CTRL2_RECEIVE_EN;
|
|
|
|
|
tmp &= ~KSZ8794_CTRL2_LEARNING_DIS;
|
2021-05-06 00:17:09 +02:00
|
|
|
|
dsa_ksz8xxx_write_reg(pdev, KSZ8794_CTRL2_PORTn(i), tmp);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-10 15:05:46 +01:00
|
|
|
|
#if defined(CONFIG_DSA_KSZ_TAIL_TAGGING)
|
2020-03-26 15:21:48 +01:00
|
|
|
|
/* Enable tail tag feature */
|
2021-05-06 00:17:09 +02:00
|
|
|
|
dsa_ksz8xxx_read_reg(pdev, KSZ8794_GLOBAL_CTRL10, &tmp);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
tmp |= KSZ8794_GLOBAL_CTRL10_TAIL_TAG_EN;
|
2021-05-06 00:17:09 +02:00
|
|
|
|
dsa_ksz8xxx_write_reg(pdev, KSZ8794_GLOBAL_CTRL10, tmp);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
#else
|
|
|
|
|
/* Disable tail tag feature */
|
2021-05-06 00:17:09 +02:00
|
|
|
|
dsa_ksz8xxx_read_reg(pdev, KSZ8794_GLOBAL_CTRL10, &tmp);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
tmp &= ~KSZ8794_GLOBAL_CTRL10_TAIL_TAG_EN;
|
2021-05-06 00:17:09 +02:00
|
|
|
|
dsa_ksz8xxx_write_reg(pdev, KSZ8794_GLOBAL_CTRL10, tmp);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
#endif
|
|
|
|
|
|
2021-05-06 00:17:09 +02:00
|
|
|
|
dsa_ksz8xxx_read_reg(pdev, KSZ8794_PORT4_IF_CTRL6, &tmp);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
LOG_DBG("KSZ8794: CONTROL6: 0x%x port4", tmp);
|
|
|
|
|
|
2021-05-06 00:17:09 +02:00
|
|
|
|
dsa_ksz8xxx_read_reg(pdev, KSZ8794_PORT4_CTRL2, &tmp);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
LOG_DBG("KSZ8794: CONTROL2: 0x%x port4", tmp);
|
|
|
|
|
|
2021-05-06 00:17:09 +02:00
|
|
|
|
dsa_ksz8xxx_read_reg(pdev, KSZ8794_GLOBAL_CTRL2, &tmp);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
tmp |= KSZ8794_GLOBAL_CTRL2_LEG_MAX_PKT_SIZ_CHK_DIS;
|
2021-05-06 00:17:09 +02:00
|
|
|
|
dsa_ksz8xxx_write_reg(pdev, KSZ8794_GLOBAL_CTRL2, tmp);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if DT_INST_NODE_HAS_PROP(0, workaround)
|
|
|
|
|
/*
|
|
|
|
|
* Workaround 0x01
|
|
|
|
|
* Solution for Short Cable Problems with the KSZ8795 Family
|
|
|
|
|
*
|
|
|
|
|
* Title
|
|
|
|
|
* Solution for Short Cable Problems with the KSZ8795 Family
|
|
|
|
|
*
|
|
|
|
|
* https://microchipsupport.force.com/s/article/Solution-for-Short-Cable-
|
|
|
|
|
* Problems-with-the-KSZ8795-Family
|
|
|
|
|
*
|
|
|
|
|
* Problem Description:
|
|
|
|
|
* 1) The KSZ8795 family parts might be not link when connected through a few
|
|
|
|
|
* type of short cable (<3m).
|
|
|
|
|
* 2) There may be a link-up issue in the capacitor AC coupling mode for port
|
|
|
|
|
* to port or board to board cases.
|
|
|
|
|
*
|
|
|
|
|
* Answer
|
|
|
|
|
* Root Cause:
|
|
|
|
|
* KSZ8795 family switches with integrated Ethernet PHY that has a DSP based
|
|
|
|
|
* equalizer EQ that can balance the signal received to adapt various cable
|
|
|
|
|
* length characteristics. The equalizer default settings amplify the signal
|
|
|
|
|
* coming in to get more accurate readings from low amplitude signals.
|
|
|
|
|
* When using some type of short cable (for example, CAT-6 cable with low
|
|
|
|
|
* attenuation to high frequencies signal vs. CAT-5 cable) or board to board
|
|
|
|
|
* connection, or port to port with capacitor AC coupling connection, the signal
|
|
|
|
|
* is amplified too much and cause the link-up failed with same boost setting in
|
|
|
|
|
* the equalizer EQ.
|
|
|
|
|
*
|
|
|
|
|
* Solution/Workaround:
|
|
|
|
|
* Write a DSP control register that is indirect register (0x3c) to optimize the
|
|
|
|
|
* equalizer EQ to cover above corner cases.
|
|
|
|
|
* w 6e a0 //write the indirect register
|
|
|
|
|
* w 6f 3c //assign the indirect hidden register address (0x3c)
|
|
|
|
|
* w a0 15 //write 0x15 to REG (0x3c) to optimize the EQ. The default is 0x0a.
|
|
|
|
|
* Based on testing and practical application, this register setting above can
|
|
|
|
|
* solve the issue for all type of the short cables and the capacitor AC
|
|
|
|
|
* coupling mode.
|
|
|
|
|
*
|
|
|
|
|
* The indirect DSP register (0x3c) is an 8-bit register, the bits describe as
|
|
|
|
|
* follows,
|
|
|
|
|
*
|
|
|
|
|
* Bits Bit Name Description Mode Default Setting
|
|
|
|
|
* 0x0a 0x15
|
|
|
|
|
* 7-5 Reserved RO 000 000
|
|
|
|
|
* 4 Cpu_EQ_Done_Cond1 How to judge EQ is finished,
|
|
|
|
|
* there are two ways to judge
|
|
|
|
|
* if EQ is finished, can set
|
|
|
|
|
* either way R/W 0 1
|
|
|
|
|
* 3-1 Cpu_EQ_CP_Points Control of EQ training is
|
|
|
|
|
* over-boosted or
|
|
|
|
|
* [2:0] under-boosted, that means to
|
|
|
|
|
* compensate signal attenuation
|
|
|
|
|
* more or less. R/W 101 010
|
|
|
|
|
* 0 Cpu_STOP_RUN after EQ training completed,
|
|
|
|
|
* stop adaptation R/W 0 1
|
|
|
|
|
*
|
|
|
|
|
* Explanation:
|
|
|
|
|
* The above register change makes equalizer’s compensation range wider, and
|
|
|
|
|
* therefore cables with various characteristics can be tolerated. Adjust
|
|
|
|
|
* equalizer EQ training algorithm to cover a few type of short cables issue.
|
|
|
|
|
* Also is appropriate for the board to board connection and port to port
|
|
|
|
|
* connection with the capacitor AC coupling mode.
|
|
|
|
|
*
|
|
|
|
|
* Basically, it decides how much signal amplitude to compensate accurately
|
|
|
|
|
* to the different type of short cable’s characteristics. The current default
|
|
|
|
|
* value in the indirect register (0x3c) can cover all general standard
|
|
|
|
|
* Ethernet short cables like CAT-5, CAT-5e without any problem.
|
|
|
|
|
* Based on tests, a more optimized equalizer adjustment value 0x15 is better
|
|
|
|
|
* for all corner cases of the short cable and short distance connection for
|
|
|
|
|
* port to port or board to board cases.
|
|
|
|
|
*/
|
2021-05-06 00:17:09 +02:00
|
|
|
|
static int dsa_ksz8794_phy_workaround_0x01(struct ksz8xxx_data *pdev)
|
2020-03-26 15:21:48 +01:00
|
|
|
|
{
|
|
|
|
|
uint8_t indirect_type = 0x0a;
|
|
|
|
|
uint8_t indirect_addr = 0x3c;
|
|
|
|
|
uint8_t indirect_data = 0x15;
|
|
|
|
|
|
2021-05-06 00:17:09 +02:00
|
|
|
|
dsa_ksz8xxx_write_reg(pdev, KSZ8794_REG_IND_CTRL_0, indirect_type);
|
|
|
|
|
dsa_ksz8xxx_write_reg(pdev, KSZ8794_REG_IND_CTRL_1, indirect_addr);
|
|
|
|
|
dsa_ksz8xxx_write_reg(pdev, KSZ8794_IND_BYTE, indirect_data);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
LOG_INF("apply workarkound 0x01 for short connections on KSZ8794");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Workaround 0x02 and 0x4
|
|
|
|
|
* Solution for Using CAT-5E or CAT-6 Short Cable with a Link Issue for the
|
|
|
|
|
* KSZ8795 Family
|
|
|
|
|
*
|
|
|
|
|
* Title
|
|
|
|
|
* Solution for Using CAT-5E or CAT-6 Short Cable with a Link Issue for the
|
|
|
|
|
* KSZ8795 Family
|
|
|
|
|
* https://microchipsupport.force.com/s/article/Solution-for-Using-CAT-5E-or
|
|
|
|
|
* -CAT-6-Short-Cable- with-a-Link-Issue-for-the-KSZ8795-Family
|
|
|
|
|
*
|
|
|
|
|
* Question
|
|
|
|
|
* Possible Problem Description:
|
|
|
|
|
* 1) KSZ8795 family includes KSZ8795CLX, KSZ8775CLX, KSZ8765CLX and KSZ8794CNX.
|
|
|
|
|
* 2) The KSZ8795 family copper parts may not link well when connected through a
|
|
|
|
|
* short CAT-5E or CAT-6 cable (about <=30 meter). The failure rate may be about
|
|
|
|
|
* 2-5%.
|
|
|
|
|
*
|
|
|
|
|
* Answer
|
|
|
|
|
* Root Cause:
|
|
|
|
|
* Basically, KSZ8795 10/100 Ethernet switch family was designed based on CAT-5
|
|
|
|
|
* cable. With the application of more type of cables, specially two types
|
|
|
|
|
* cables of CAT-5E and CAT-6, both cables have wider bandwidth that has
|
|
|
|
|
* different frequency characteristics than CAT-5 cable. More higher frequency
|
|
|
|
|
* component of the CAT-5E or CAT-6 will be amplified in the receiving amplifier
|
|
|
|
|
* and will cause the received signal distortion due to too much high frequency
|
|
|
|
|
* components receiving signal amplitude and cause the link-up failure with
|
|
|
|
|
* short cables.
|
|
|
|
|
*
|
|
|
|
|
* Solution/Workaround:
|
|
|
|
|
* 1) dsa_ksz8794_phy_workaround_0x02()
|
|
|
|
|
* Based on the root cause above, adjust the receiver low pass filter to reduce
|
|
|
|
|
* the high frequency component to keep the receive signal within a reasonable
|
|
|
|
|
* range when using CAT-5E and CAT-6 cable.
|
|
|
|
|
*
|
|
|
|
|
* Set the indirect register as follows for the receiver low pass filter.
|
|
|
|
|
* Format is w [Register address] [8-bit data]
|
|
|
|
|
* w 6e a0 //write the indirect register
|
|
|
|
|
* w 6f 4c //write/assign the internal used indirect register address (0x4c)
|
|
|
|
|
* w a0 40 //write 0x40 to indirect register (0x4c) to reduce low pass filter
|
|
|
|
|
* bandwidth.
|
|
|
|
|
*
|
|
|
|
|
* The register 0x4c bits [7:6] for receiver low pass filter bandwidth control.
|
|
|
|
|
*
|
|
|
|
|
* The default value is ‘00’, change to ‘01’.
|
|
|
|
|
* Based on testing and practical application, this register setting above can
|
|
|
|
|
* solve the link issue if using CAT-5E and CAT-6 short cables.
|
|
|
|
|
*
|
|
|
|
|
* The indirect register (0x4C) is an 8-bit register. The bits [7:6] are
|
|
|
|
|
* described in the table below.
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* Bits Bit Name Description Mode Default Setting
|
|
|
|
|
* 0x00 0x40
|
|
|
|
|
* 7-6 RX BW control Low pass filter bandwidth R/W 00 01
|
|
|
|
|
* 00 = 90MHz
|
|
|
|
|
* 01 = 62MHz
|
|
|
|
|
* 10 = 55MHz
|
|
|
|
|
* 11 = 44MHz
|
|
|
|
|
* 5 Enable Near-end loopback R/W 0 0
|
|
|
|
|
* 4-3 BTRT Additional reduce R/W 00 00
|
|
|
|
|
* 2 SD Ext register R/W 0 0
|
|
|
|
|
* 1-0 FXD reference setting 1.7V, 2V,
|
|
|
|
|
* 1.4V
|
|
|
|
|
* R/W 00 00
|
|
|
|
|
*
|
|
|
|
|
* Solution/Workaround:
|
|
|
|
|
* 2) dsa_ksz8794_phy_workaround_0x04()
|
|
|
|
|
* For the wider bandwidth cables or on-board capacitor AC coupling
|
|
|
|
|
* application, we recommend adding/setting the indirect register (0x08) from
|
|
|
|
|
* default 0x0f to 0x00 that means to change register (0x08) bits [5:0] from
|
|
|
|
|
* 0x0f to 0x00 to reduce equalizer’s (EQ) initial value to 0x00 for more
|
|
|
|
|
* short cable or on-board capacitors AC coupling application.
|
|
|
|
|
*
|
|
|
|
|
* Set the indirect register as follows for EQ with 0x00 initial value.
|
|
|
|
|
* Format is w [Register address] [8-bit data]
|
|
|
|
|
* w 6e a0 //write the indirect register
|
|
|
|
|
* w 6f 08 //write/assign the internal used indirect register address (0x08)
|
|
|
|
|
* w a0 00 //write 0x00 to indirect register (0x08) to make EQ initial value
|
|
|
|
|
* equal to 0x00 for very short cable (For example, 0.1m or less)
|
|
|
|
|
* or connect two ports directly through capacitors for a capacitor
|
|
|
|
|
* AC couple.
|
|
|
|
|
*
|
|
|
|
|
* The indirect DSP register (0x08) is an 8-bit register. The bits [5:0] are
|
|
|
|
|
* described in the table below.
|
|
|
|
|
*
|
|
|
|
|
* Bits Bit Name Description Mode Default Setting
|
|
|
|
|
* 0x0f 0x00
|
|
|
|
|
* 7 Park EQ Enable Park Equalizer function enable R/W 0 0
|
|
|
|
|
* 6 Reserved R 0 0
|
|
|
|
|
* 5-0 Cpu_EQ_Index Equalizer index control
|
|
|
|
|
* interface R/W 001111 000000
|
|
|
|
|
* from 0 to 55, set EQ initial value
|
|
|
|
|
* Conclusion:
|
|
|
|
|
* Due to CAT-5E and CAT-6 cable having wider bandwidth, more high frequency
|
|
|
|
|
* components will pass the low pass filter into the receiving amplifier and
|
|
|
|
|
* cause the received signal amplitude to be too high.
|
|
|
|
|
* Reducing the receiver low pass filter bandwidth will be the best way to
|
|
|
|
|
* reduce the high frequency components to meet CAT-5E and CAT-6 short cable
|
|
|
|
|
* link issue and doesn’t affect CAT-5 cable because CAT-5 is not a wider
|
|
|
|
|
* bandwidth cable.
|
|
|
|
|
*
|
|
|
|
|
* The DSP register (0X08) bits [5:0] are for EQ initial value. Its current
|
|
|
|
|
* default value is 0x0F, which assumes the need to equalize regardless of the
|
|
|
|
|
* cable length. This 0x0f initial equalize value in EQ isn’t needed when
|
|
|
|
|
* using very short cable or an on-board direct connection like capacitors AC
|
|
|
|
|
* coupling mode. As the cable length increases, the device will equalize
|
|
|
|
|
* automatic accordingly from 0x00 EQ initial value.
|
|
|
|
|
*
|
|
|
|
|
* So, it is better to set both register (0x4c) to 0x40 and register (0x08) to
|
|
|
|
|
* 0x00 for compatibility with all Ethernet cable types and Ethernet cable
|
|
|
|
|
* lengths.
|
|
|
|
|
*/
|
|
|
|
|
|
2021-05-06 00:17:09 +02:00
|
|
|
|
static int dsa_ksz8794_phy_workaround_0x02(struct ksz8xxx_data *pdev)
|
2020-03-26 15:21:48 +01:00
|
|
|
|
{
|
|
|
|
|
uint8_t indirect_type = 0x0a;
|
|
|
|
|
uint8_t indirect_addr = 0x4c;
|
|
|
|
|
uint8_t indirect_data = 0x40;
|
|
|
|
|
|
2021-05-06 00:17:09 +02:00
|
|
|
|
dsa_ksz8xxx_write_reg(pdev, KSZ8794_REG_IND_CTRL_0, indirect_type);
|
|
|
|
|
dsa_ksz8xxx_write_reg(pdev, KSZ8794_REG_IND_CTRL_1, indirect_addr);
|
|
|
|
|
dsa_ksz8xxx_write_reg(pdev, KSZ8794_IND_BYTE, indirect_data);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
LOG_INF("apply workarkound 0x02 link issue CAT-5E/6 on KSZ8794");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-06 00:17:09 +02:00
|
|
|
|
static int dsa_ksz8794_phy_workaround_0x04(struct ksz8xxx_data *pdev)
|
2020-03-26 15:21:48 +01:00
|
|
|
|
{
|
|
|
|
|
uint8_t indirect_type = 0x0a;
|
|
|
|
|
uint8_t indirect_addr = 0x08;
|
|
|
|
|
uint8_t indirect_data = 0x00;
|
|
|
|
|
|
2021-05-06 00:17:09 +02:00
|
|
|
|
dsa_ksz8xxx_write_reg(pdev, KSZ8794_REG_IND_CTRL_0, indirect_type);
|
|
|
|
|
dsa_ksz8xxx_write_reg(pdev, KSZ8794_REG_IND_CTRL_1, indirect_addr);
|
|
|
|
|
dsa_ksz8xxx_write_reg(pdev, KSZ8794_IND_BYTE, indirect_data);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
LOG_INF("apply workarkound 0x04 link issue CAT-5E/6 on KSZ8794");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-06 00:17:09 +02:00
|
|
|
|
static int dsa_ksz8794_apply_workarounds(struct ksz8xxx_data *pdev)
|
2020-03-26 15:21:48 +01:00
|
|
|
|
{
|
|
|
|
|
int workaround = DT_INST_PROP(0, workaround);
|
|
|
|
|
|
|
|
|
|
if (workaround & 0x01) {
|
2021-05-01 21:42:26 +02:00
|
|
|
|
dsa_ksz8794_phy_workaround_0x01(pdev);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
}
|
|
|
|
|
if (workaround & 0x02) {
|
2021-05-01 21:42:26 +02:00
|
|
|
|
dsa_ksz8794_phy_workaround_0x02(pdev);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
}
|
|
|
|
|
if (workaround & 0x04) {
|
2021-05-01 21:42:26 +02:00
|
|
|
|
dsa_ksz8794_phy_workaround_0x04(pdev);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if DT_INST_NODE_HAS_PROP(0, mii_lowspeed_drivestrength)
|
2021-05-06 00:17:09 +02:00
|
|
|
|
static int dsa_ksz8794_set_lowspeed_drivestrength(struct ksz8xxx_data *pdev)
|
2020-03-26 15:21:48 +01:00
|
|
|
|
{
|
|
|
|
|
int mii_lowspeed_drivestrength =
|
|
|
|
|
DT_INST_PROP(0, mii_lowspeed_drivestrength);
|
|
|
|
|
|
|
|
|
|
uint8_t tmp, val;
|
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
|
|
switch (mii_lowspeed_drivestrength) {
|
|
|
|
|
case 2:
|
|
|
|
|
val = KSZ8794_GLOBAL_CTRL20_LOWSPEED_2MA;
|
|
|
|
|
break;
|
|
|
|
|
case 4:
|
|
|
|
|
val = KSZ8794_GLOBAL_CTRL20_LOWSPEED_4MA;
|
|
|
|
|
break;
|
|
|
|
|
case 8:
|
|
|
|
|
val = KSZ8794_GLOBAL_CTRL20_LOWSPEED_8MA;
|
|
|
|
|
break;
|
|
|
|
|
case 12:
|
|
|
|
|
val = KSZ8794_GLOBAL_CTRL20_LOWSPEED_12MA;
|
|
|
|
|
break;
|
|
|
|
|
case 16:
|
|
|
|
|
val = KSZ8794_GLOBAL_CTRL20_LOWSPEED_16MA;
|
|
|
|
|
break;
|
|
|
|
|
case 20:
|
|
|
|
|
val = KSZ8794_GLOBAL_CTRL20_LOWSPEED_20MA;
|
|
|
|
|
break;
|
|
|
|
|
case 24:
|
|
|
|
|
val = KSZ8794_GLOBAL_CTRL20_LOWSPEED_24MA;
|
|
|
|
|
break;
|
|
|
|
|
case 28:
|
|
|
|
|
val = KSZ8794_GLOBAL_CTRL20_LOWSPEED_28MA;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
ret = -1;
|
|
|
|
|
LOG_ERR("KSZ8794: unsupported drive strength %dmA",
|
|
|
|
|
mii_lowspeed_drivestrength);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ret == 0) {
|
|
|
|
|
/* set Low-Speed Interface Drive Strength for MII and RMMI */
|
2021-05-06 00:17:09 +02:00
|
|
|
|
dsa_ksz8xxx_read_reg(pdev, KSZ8794_GLOBAL_CTRL20, &tmp);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
tmp &= ~KSZ8794_GLOBAL_CTRL20_LOWSPEED_MASK;
|
|
|
|
|
tmp |= val;
|
2021-05-06 00:17:09 +02:00
|
|
|
|
dsa_ksz8xxx_write_reg(pdev, KSZ8794_GLOBAL_CTRL20, tmp);
|
|
|
|
|
dsa_ksz8xxx_read_reg(pdev, KSZ8794_GLOBAL_CTRL20, &tmp);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
LOG_INF("KSZ8794: set drive strength %dmA",
|
|
|
|
|
mii_lowspeed_drivestrength);
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2021-05-05 23:14:01 +02:00
|
|
|
|
#endif
|
2020-03-26 15:21:48 +01:00
|
|
|
|
|
2021-05-01 21:26:02 +02:00
|
|
|
|
#if DT_INST_NODE_HAS_PROP(0, reset_gpios)
|
2021-05-06 00:17:09 +02:00
|
|
|
|
static int dsa_ksz8xxx_gpio_reset(struct ksz8xxx_data *pdev)
|
2020-03-26 15:21:48 +01:00
|
|
|
|
{
|
|
|
|
|
const struct device *reset_dev =
|
|
|
|
|
device_get_binding(DT_INST_GPIO_LABEL(0, reset_gpios));
|
|
|
|
|
const uint32_t reset_pin = DT_INST_GPIO_PIN(0, reset_gpios);
|
|
|
|
|
|
|
|
|
|
if (reset_dev == NULL) {
|
|
|
|
|
LOG_ERR("Could not get RESET device for KSZ8794");
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
gpio_pin_configure(reset_dev, reset_pin,
|
|
|
|
|
DT_INST_GPIO_FLAGS(0, reset_gpios) |
|
|
|
|
|
GPIO_OUTPUT_ACTIVE);
|
|
|
|
|
k_msleep(10);
|
|
|
|
|
|
|
|
|
|
gpio_pin_set(reset_dev, reset_pin, 0);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2021-05-01 21:26:02 +02:00
|
|
|
|
#endif
|
2020-03-26 15:21:48 +01:00
|
|
|
|
|
2021-05-06 00:17:09 +02:00
|
|
|
|
static int dsa_ksz8xxx_configure_bus(struct ksz8xxx_data *pdev)
|
2021-05-01 21:42:26 +02:00
|
|
|
|
{
|
2021-05-05 23:14:01 +02:00
|
|
|
|
#if defined(CONFIG_DSA_SPI)
|
2021-05-01 21:42:26 +02:00
|
|
|
|
/* SPI config */
|
2021-12-08 22:25:26 +01:00
|
|
|
|
pdev->spi = (struct spi_dt_spec) SPI_DT_SPEC_INST_GET(0,
|
2021-05-01 21:42:26 +02:00
|
|
|
|
#if DT_INST_PROP(0, spi_cpol)
|
|
|
|
|
SPI_MODE_CPOL |
|
|
|
|
|
#endif
|
|
|
|
|
#if DT_INST_PROP(0, spi_cpha)
|
|
|
|
|
SPI_MODE_CPHA |
|
|
|
|
|
#endif
|
2021-12-08 22:25:26 +01:00
|
|
|
|
SPI_WORD_SET(8),
|
|
|
|
|
0U);
|
|
|
|
|
|
|
|
|
|
if (!spi_is_ready(&pdev->spi)) {
|
|
|
|
|
LOG_ERR("SPI bus %s is not ready",
|
|
|
|
|
pdev->spi.bus->name);
|
2021-05-01 21:42:26 +02:00
|
|
|
|
return -ENODEV;
|
|
|
|
|
}
|
2021-05-05 23:14:01 +02:00
|
|
|
|
#endif
|
2021-05-01 21:42:26 +02:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-26 15:21:48 +01:00
|
|
|
|
/* Low level initialization code for DSA PHY */
|
2021-05-06 00:17:09 +02:00
|
|
|
|
int dsa_hw_init(struct ksz8xxx_data *pdev)
|
2020-03-26 15:21:48 +01:00
|
|
|
|
{
|
2021-05-01 21:42:26 +02:00
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
|
|
if (pdev->is_init) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2020-03-26 15:21:48 +01:00
|
|
|
|
|
|
|
|
|
/* Hard reset */
|
|
|
|
|
#if DT_INST_NODE_HAS_PROP(0, reset_gpios)
|
2021-05-06 00:17:09 +02:00
|
|
|
|
dsa_ksz8xxx_gpio_reset(pdev);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
|
2021-05-05 23:14:01 +02:00
|
|
|
|
/* Time needed for chip to completely power up (100ms) */
|
|
|
|
|
k_busy_wait(KSZ8XXX_HARD_RESET_WAIT);
|
|
|
|
|
#endif
|
2020-03-26 15:21:48 +01:00
|
|
|
|
|
2021-05-01 21:42:26 +02:00
|
|
|
|
/* Configure communication bus */
|
2021-05-06 00:17:09 +02:00
|
|
|
|
rc = dsa_ksz8xxx_configure_bus(pdev);
|
2021-05-01 21:42:26 +02:00
|
|
|
|
if (rc < 0) {
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Probe attached PHY */
|
2021-05-06 00:17:09 +02:00
|
|
|
|
rc = dsa_ksz8xxx_probe(pdev);
|
2021-05-01 21:42:26 +02:00
|
|
|
|
if (rc < 0) {
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
2020-03-26 15:21:48 +01:00
|
|
|
|
|
|
|
|
|
#if !DT_INST_NODE_HAS_PROP(0, reset_gpios)
|
|
|
|
|
/* Soft reset */
|
2021-05-06 00:17:09 +02:00
|
|
|
|
dsa_ksz8xxx_soft_reset(pdev);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* Setup KSZ8794 */
|
2021-05-06 00:17:09 +02:00
|
|
|
|
dsa_ksz8xxx_switch_setup(pdev);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
|
|
|
|
|
#if DT_INST_NODE_HAS_PROP(0, mii_lowspeed_drivestrength)
|
2021-05-01 21:42:26 +02:00
|
|
|
|
dsa_ksz8794_set_lowspeed_drivestrength(pdev);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if DT_INST_NODE_HAS_PROP(0, workaround)
|
|
|
|
|
/* apply workarounds */
|
2021-05-01 21:42:26 +02:00
|
|
|
|
dsa_ksz8794_apply_workarounds(pdev);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
#endif
|
|
|
|
|
|
2021-05-01 21:42:26 +02:00
|
|
|
|
pdev->is_init = true;
|
2020-03-26 15:21:48 +01:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void dsa_delayed_work(struct k_work *item)
|
|
|
|
|
{
|
|
|
|
|
struct dsa_context *context =
|
|
|
|
|
CONTAINER_OF(item, struct dsa_context, dsa_work);
|
2021-05-06 00:17:09 +02:00
|
|
|
|
struct ksz8xxx_data *pdev = PRV_DATA(context);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
bool link_state;
|
|
|
|
|
uint8_t i;
|
|
|
|
|
|
2021-05-05 23:14:01 +02:00
|
|
|
|
for (i = KSZ8XXX_FIRST_PORT; i <= KSZ8XXX_LAST_PORT; i++) {
|
|
|
|
|
/* Skip Switch <-> CPU Port */
|
|
|
|
|
if (i == KSZ8XXX_CPU_PORT) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-06 00:17:09 +02:00
|
|
|
|
link_state = dsa_ksz8xxx_port_link_status(pdev, i);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
if (link_state && !context->link_up[i]) {
|
|
|
|
|
LOG_INF("DSA port: %d link UP!", i);
|
|
|
|
|
net_eth_carrier_on(context->iface_slave[i]);
|
|
|
|
|
} else if (!link_state && context->link_up[i]) {
|
|
|
|
|
LOG_INF("DSA port: %d link DOWN!", i);
|
|
|
|
|
net_eth_carrier_off(context->iface_slave[i]);
|
|
|
|
|
}
|
|
|
|
|
context->link_up[i] = link_state;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-31 17:31:29 +02:00
|
|
|
|
k_work_reschedule(&context->dsa_work, DSA_STATUS_PERIOD_MS);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int dsa_port_init(const struct device *dev)
|
|
|
|
|
{
|
2022-01-18 15:48:18 +01:00
|
|
|
|
struct dsa_context *data = dev->data;
|
|
|
|
|
struct ksz8xxx_data *pdev = PRV_DATA(data);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
|
2021-05-05 23:14:01 +02:00
|
|
|
|
dsa_hw_init(pdev);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Generic implementation of writing value to DSA register */
|
2021-05-06 00:14:09 +02:00
|
|
|
|
static int dsa_ksz8xxx_sw_write_reg(const struct device *dev, uint16_t reg_addr,
|
2020-03-26 15:21:48 +01:00
|
|
|
|
uint8_t value)
|
|
|
|
|
{
|
2022-01-18 15:48:18 +01:00
|
|
|
|
struct dsa_context *data = dev->data;
|
|
|
|
|
struct ksz8xxx_data *pdev = PRV_DATA(data);
|
2021-05-06 00:14:09 +02:00
|
|
|
|
|
|
|
|
|
dsa_ksz8xxx_write_reg(pdev, reg_addr, value);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Generic implementation of reading value from DSA register */
|
2021-05-06 00:14:09 +02:00
|
|
|
|
static int dsa_ksz8xxx_sw_read_reg(const struct device *dev, uint16_t reg_addr,
|
2020-03-26 15:21:48 +01:00
|
|
|
|
uint8_t *value)
|
|
|
|
|
{
|
2022-01-18 15:48:18 +01:00
|
|
|
|
struct dsa_context *data = dev->data;
|
|
|
|
|
struct ksz8xxx_data *pdev = PRV_DATA(data);
|
2021-05-06 00:14:09 +02:00
|
|
|
|
|
|
|
|
|
dsa_ksz8xxx_read_reg(pdev, reg_addr, value);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Set entry to DSA MAC address table
|
|
|
|
|
*
|
2021-05-06 00:14:09 +02:00
|
|
|
|
* @param dev DSA device
|
2020-03-26 15:21:48 +01:00
|
|
|
|
* @param mac The MAC address to be set in the table
|
|
|
|
|
* @param fw_port Port number to forward packets
|
|
|
|
|
* @param tbl_entry_idx The index of entry in the table
|
|
|
|
|
* @param flags Flags to be set in the entry
|
|
|
|
|
*
|
|
|
|
|
* @return 0 if ok, < 0 if error
|
|
|
|
|
*/
|
2021-05-06 00:14:09 +02:00
|
|
|
|
static int dsa_ksz8xxx_set_mac_table_entry(const struct device *dev,
|
|
|
|
|
const uint8_t *mac,
|
|
|
|
|
uint8_t fw_port,
|
|
|
|
|
uint16_t tbl_entry_idx,
|
|
|
|
|
uint16_t flags)
|
2020-03-26 15:21:48 +01:00
|
|
|
|
{
|
2022-01-18 15:48:18 +01:00
|
|
|
|
struct dsa_context *data = dev->data;
|
|
|
|
|
struct ksz8xxx_data *pdev = PRV_DATA(data);
|
2021-05-06 00:14:09 +02:00
|
|
|
|
|
2020-03-26 15:21:48 +01:00
|
|
|
|
if (flags != 0) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-06 00:14:09 +02:00
|
|
|
|
dsa_ksz8xxx_set_static_mac_table(pdev, mac, fw_port,
|
2021-05-01 21:42:26 +02:00
|
|
|
|
tbl_entry_idx);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Get DSA MAC address table entry
|
|
|
|
|
*
|
2021-05-06 00:14:09 +02:00
|
|
|
|
* @param dev DSA device
|
2020-03-26 15:21:48 +01:00
|
|
|
|
* @param buf The buffer for data read from the table
|
|
|
|
|
* @param tbl_entry_idx The index of entry in the table
|
|
|
|
|
*
|
|
|
|
|
* @return 0 if ok, < 0 if error
|
|
|
|
|
*/
|
2021-05-06 00:14:09 +02:00
|
|
|
|
static int dsa_ksz8xxx_get_mac_table_entry(const struct device *dev,
|
|
|
|
|
uint8_t *buf,
|
|
|
|
|
uint16_t tbl_entry_idx)
|
2020-03-26 15:21:48 +01:00
|
|
|
|
{
|
2022-01-18 15:48:18 +01:00
|
|
|
|
struct dsa_context *data = dev->data;
|
|
|
|
|
struct ksz8xxx_data *pdev = PRV_DATA(data);
|
2021-05-06 00:14:09 +02:00
|
|
|
|
|
|
|
|
|
dsa_ksz8xxx_read_static_mac_table(pdev, tbl_entry_idx, buf);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-10 15:05:46 +01:00
|
|
|
|
#if defined(CONFIG_DSA_KSZ_TAIL_TAGGING)
|
2021-05-05 23:14:01 +02:00
|
|
|
|
#define DSA_KSZ8795_TAIL_TAG_OVRD BIT(6)
|
2020-03-26 15:21:48 +01:00
|
|
|
|
#define DSA_KSZ8795_TAIL_TAG_LOOKUP BIT(7)
|
|
|
|
|
|
|
|
|
|
#define DSA_KSZ8794_EGRESS_TAG_LEN 1
|
|
|
|
|
#define DSA_KSZ8794_INGRESS_TAG_LEN 1
|
|
|
|
|
|
|
|
|
|
#define DSA_MIN_L2_FRAME_SIZE 64
|
|
|
|
|
#define DSA_L2_FCS_SIZE 4
|
|
|
|
|
|
2021-05-06 00:17:09 +02:00
|
|
|
|
struct net_pkt *dsa_ksz8xxx_xmit_pkt(struct net_if *iface, struct net_pkt *pkt)
|
2020-03-26 15:21:48 +01:00
|
|
|
|
{
|
|
|
|
|
struct ethernet_context *ctx = net_if_l2_data(iface);
|
|
|
|
|
struct net_eth_hdr *hdr = NET_ETH_HDR(pkt);
|
|
|
|
|
struct net_linkaddr lladst;
|
|
|
|
|
uint8_t port_idx, *dbuf;
|
|
|
|
|
struct net_buf *buf;
|
|
|
|
|
size_t len, pad = 0;
|
|
|
|
|
|
|
|
|
|
lladst.len = sizeof(hdr->dst.addr);
|
|
|
|
|
lladst.addr = &hdr->dst.addr[0];
|
|
|
|
|
|
|
|
|
|
len = net_pkt_get_len(pkt);
|
|
|
|
|
/*
|
|
|
|
|
* For KSZ8794 one needs to 'pad' the L2 frame to its minimal size
|
|
|
|
|
* (64B) before appending TAIL TAG and FCS
|
|
|
|
|
*/
|
|
|
|
|
if (len < (DSA_MIN_L2_FRAME_SIZE - DSA_L2_FCS_SIZE)) {
|
|
|
|
|
/* Calculate number of bytes needed for padding */
|
|
|
|
|
pad = DSA_MIN_L2_FRAME_SIZE - DSA_L2_FCS_SIZE - len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
buf = net_buf_alloc_len(net_buf_pool_get(pkt->buffer->pool_id),
|
|
|
|
|
pad + DSA_KSZ8794_INGRESS_TAG_LEN, K_NO_WAIT);
|
|
|
|
|
if (!buf) {
|
|
|
|
|
LOG_ERR("DSA cannot allocate new data buffer");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Get the pointer to struct's net_buf_simple data and zero out the
|
|
|
|
|
* padding and tag byte placeholder
|
|
|
|
|
*/
|
|
|
|
|
dbuf = net_buf_simple_tail(&(buf->b));
|
|
|
|
|
memset(dbuf, 0x0, pad + DSA_KSZ8794_INGRESS_TAG_LEN);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* For master port (eth0) set the bit 7 to use look-up table to pass
|
|
|
|
|
* packet to correct interface (bits [0..6] _are_ ignored).
|
|
|
|
|
*
|
|
|
|
|
* For slave ports (lan1..3) just set the tag properly:
|
|
|
|
|
* bit 0 -> eth1, bit 1 -> eth2. bit 2 -> eth3
|
|
|
|
|
* It may be also necessary to set bit 6 to "anyhow send packets to
|
|
|
|
|
* specified port in Bits[3:0]". This may be needed for RSTP
|
|
|
|
|
* implementation (when the switch port is disabled, but shall handle
|
|
|
|
|
* LLDP packets).
|
|
|
|
|
*/
|
|
|
|
|
if (dsa_is_port_master(iface)) {
|
|
|
|
|
port_idx = DSA_KSZ8795_TAIL_TAG_LOOKUP;
|
|
|
|
|
} else {
|
2021-11-15 16:12:31 +01:00
|
|
|
|
port_idx = (1 << (ctx->dsa_port_idx));
|
2020-03-26 15:21:48 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NET_DBG("TT - port: 0x%x[%p] LEN: %d 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x",
|
|
|
|
|
port_idx, iface, len, lladst.addr[0], lladst.addr[1],
|
|
|
|
|
lladst.addr[2], lladst.addr[3], lladst.addr[4], lladst.addr[5]);
|
|
|
|
|
|
|
|
|
|
/* The tail tag shall be placed after the padding (if present) */
|
|
|
|
|
dbuf[pad] = port_idx;
|
|
|
|
|
|
|
|
|
|
/* Set proper len member for the actual struct net_buf_simple */
|
|
|
|
|
net_buf_add(buf, pad + DSA_KSZ8794_INGRESS_TAG_LEN);
|
|
|
|
|
|
|
|
|
|
/* Append struct net_buf to packet data */
|
|
|
|
|
net_buf_frag_add(pkt->buffer, buf);
|
|
|
|
|
|
|
|
|
|
return pkt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief DSA function to get proper interface
|
|
|
|
|
*
|
|
|
|
|
* This is the function for assigning proper slave interface after receiving
|
|
|
|
|
* the packet on master.
|
|
|
|
|
*
|
|
|
|
|
* @param iface Network interface
|
|
|
|
|
* @param pkt Network packet
|
|
|
|
|
*
|
|
|
|
|
* Returns:
|
|
|
|
|
* - Pointer to struct net_if
|
|
|
|
|
*/
|
2021-05-06 00:17:09 +02:00
|
|
|
|
static struct net_if *dsa_ksz8xxx_get_iface(struct net_if *iface,
|
2020-03-26 15:21:48 +01:00
|
|
|
|
struct net_pkt *pkt)
|
|
|
|
|
{
|
|
|
|
|
struct ethernet_context *ctx;
|
|
|
|
|
struct net_if *iface_sw;
|
|
|
|
|
size_t plen;
|
|
|
|
|
uint8_t pnum;
|
|
|
|
|
|
|
|
|
|
if (!(net_eth_get_hw_capabilities(iface) &
|
|
|
|
|
(ETHERNET_DSA_SLAVE_PORT | ETHERNET_DSA_MASTER_PORT))) {
|
|
|
|
|
return iface;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
net_pkt_set_overwrite(pkt, true);
|
|
|
|
|
net_pkt_cursor_init(pkt);
|
|
|
|
|
plen = net_pkt_get_len(pkt);
|
|
|
|
|
|
|
|
|
|
net_pkt_skip(pkt, plen - DSA_KSZ8794_EGRESS_TAG_LEN);
|
|
|
|
|
net_pkt_read_u8(pkt, &pnum);
|
|
|
|
|
|
|
|
|
|
net_pkt_update_length(pkt, plen - DSA_KSZ8794_EGRESS_TAG_LEN);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* NOTE:
|
|
|
|
|
* The below approach is only for ip_k66f board as we do know
|
|
|
|
|
* that eth0 is on position (index) 1, then we do have lan1 with
|
|
|
|
|
* index 2, lan2 with 3 and lan3 with 4.
|
|
|
|
|
*
|
|
|
|
|
* This is caused by eth interfaces placing order by linker and
|
|
|
|
|
* may vary on other boards, where are for example two eth
|
|
|
|
|
* interfaces available.
|
|
|
|
|
*/
|
|
|
|
|
iface_sw = net_if_get_by_index(pnum + 2);
|
|
|
|
|
|
|
|
|
|
ctx = net_if_l2_data(iface);
|
|
|
|
|
NET_DBG("TT - plen: %d pnum: %d pos: 0x%p dsa_port_idx: %d",
|
|
|
|
|
plen - DSA_KSZ8794_EGRESS_TAG_LEN, pnum,
|
|
|
|
|
net_pkt_cursor_get_pos(pkt), ctx->dsa_port_idx);
|
|
|
|
|
|
|
|
|
|
return iface_sw;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static void dsa_iface_init(struct net_if *iface)
|
|
|
|
|
{
|
|
|
|
|
struct dsa_slave_config *cfg = (struct dsa_slave_config *)
|
|
|
|
|
net_if_get_device(iface)->config;
|
|
|
|
|
struct ethernet_context *ctx = net_if_l2_data(iface);
|
|
|
|
|
const struct device *dm, *dev = net_if_get_device(iface);
|
|
|
|
|
struct dsa_context *context = dev->data;
|
2021-05-06 00:17:09 +02:00
|
|
|
|
struct ksz8xxx_data *pdev = PRV_DATA(context);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
struct ethernet_context *ctx_master;
|
2021-05-01 21:47:00 +02:00
|
|
|
|
int i = pdev->iface_init_count;
|
2020-03-26 15:21:48 +01:00
|
|
|
|
|
|
|
|
|
/* Find master port for ksz8794 switch */
|
|
|
|
|
if (context->iface_master == NULL) {
|
|
|
|
|
dm = device_get_binding(DT_INST_PROP_BY_PHANDLE(0,
|
|
|
|
|
dsa_master_port,
|
|
|
|
|
label));
|
|
|
|
|
context->iface_master = net_if_lookup_by_dev(dm);
|
|
|
|
|
if (context->iface_master == NULL) {
|
|
|
|
|
LOG_ERR("DSA: Master iface NOT found!");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Provide pointer to DSA context to master's eth interface
|
|
|
|
|
* struct ethernet_context
|
|
|
|
|
*/
|
|
|
|
|
ctx_master = net_if_l2_data(context->iface_master);
|
|
|
|
|
ctx_master->dsa_ctx = context;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (context->iface_slave[i] == NULL) {
|
|
|
|
|
context->iface_slave[i] = iface;
|
|
|
|
|
net_if_set_link_addr(iface, cfg->mac_addr,
|
|
|
|
|
sizeof(cfg->mac_addr),
|
|
|
|
|
NET_LINK_ETHERNET);
|
|
|
|
|
ctx->dsa_port_idx = i;
|
|
|
|
|
ctx->dsa_ctx = context;
|
2021-03-26 16:17:47 +01:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Initialize ethernet context 'work' for this iface to
|
|
|
|
|
* be able to monitor the carrier status.
|
|
|
|
|
*/
|
|
|
|
|
ethernet_init(iface);
|
2020-03-26 15:21:48 +01:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-01 21:47:00 +02:00
|
|
|
|
pdev->iface_init_count++;
|
2020-03-26 15:21:48 +01:00
|
|
|
|
net_if_flag_set(iface, NET_IF_NO_AUTO_START);
|
2021-03-26 16:17:47 +01:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Start DSA work to monitor status of ports (read from switch IC)
|
2021-05-01 21:47:00 +02:00
|
|
|
|
* only when carrier_work is properly initialized for all slave
|
|
|
|
|
* interfaces.
|
2021-03-26 16:17:47 +01:00
|
|
|
|
*/
|
2021-05-01 21:47:00 +02:00
|
|
|
|
if (pdev->iface_init_count == context->num_slave_ports) {
|
2021-03-31 17:31:29 +02:00
|
|
|
|
k_work_init_delayable(&context->dsa_work, dsa_delayed_work);
|
|
|
|
|
k_work_reschedule(&context->dsa_work, DSA_STATUS_PERIOD_MS);
|
2021-03-26 16:17:47 +01:00
|
|
|
|
}
|
2020-03-26 15:21:48 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum ethernet_hw_caps dsa_port_get_capabilities(const struct device *dev)
|
|
|
|
|
{
|
|
|
|
|
ARG_UNUSED(dev);
|
|
|
|
|
|
|
|
|
|
return ETHERNET_DSA_SLAVE_PORT | ETHERNET_LINK_10BASE_T |
|
|
|
|
|
ETHERNET_LINK_100BASE_T;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const struct ethernet_api dsa_eth_api_funcs = {
|
|
|
|
|
.iface_api.init = dsa_iface_init,
|
|
|
|
|
.get_capabilities = dsa_port_get_capabilities,
|
|
|
|
|
.send = dsa_tx,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct dsa_api dsa_api_f = {
|
2021-05-06 00:17:09 +02:00
|
|
|
|
.switch_read = dsa_ksz8xxx_sw_read_reg,
|
|
|
|
|
.switch_write = dsa_ksz8xxx_sw_write_reg,
|
|
|
|
|
.switch_set_mac_table_entry = dsa_ksz8xxx_set_mac_table_entry,
|
|
|
|
|
.switch_get_mac_table_entry = dsa_ksz8xxx_get_mac_table_entry,
|
2021-11-10 15:05:46 +01:00
|
|
|
|
#if defined(CONFIG_DSA_KSZ_TAIL_TAGGING)
|
2021-05-06 00:17:09 +02:00
|
|
|
|
.dsa_xmit_pkt = dsa_ksz8xxx_xmit_pkt,
|
|
|
|
|
.dsa_get_iface = dsa_ksz8xxx_get_iface,
|
2021-05-01 21:26:02 +02:00
|
|
|
|
#endif
|
2020-03-26 15:21:48 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The order of NET_DEVICE_INIT_INSTANCE() placement IS important.
|
|
|
|
|
*
|
|
|
|
|
* To make the code simpler - the special care needs to be put on
|
|
|
|
|
* the proper placement of eth0, lan1, lan2, lan3, etc - to avoid
|
|
|
|
|
* the need to search for proper interface when each packet is
|
|
|
|
|
* received or sent.
|
|
|
|
|
* The net_if.c has a very fast API to provide access to linked by
|
|
|
|
|
* the linker struct net_if(s) via device or index. As it is already
|
|
|
|
|
* available for use - let's use it.
|
|
|
|
|
*
|
|
|
|
|
* To do that one needs to check how linker places the interfaces.
|
|
|
|
|
* To inspect:
|
|
|
|
|
* objdump -dst ./zephyr/CMakeFiles/zephyr.dir/drivers/ethernet/eth_mcux.c.obj\
|
|
|
|
|
* | grep "__net_if"
|
|
|
|
|
* (The real problem is with eth0 and lanX order)
|
|
|
|
|
*
|
|
|
|
|
* If this approach is not enough for a simple system (like e.g. ip_k66f, one
|
|
|
|
|
* can prepare dedicated linker script for the board to force the
|
|
|
|
|
* order for complicated designs (like ones with eth0, eth1, and lanX).
|
|
|
|
|
*
|
|
|
|
|
* For simple cases it is just good enough.
|
|
|
|
|
*/
|
|
|
|
|
|
2021-05-06 00:14:09 +02:00
|
|
|
|
#define NET_SLAVE_DEVICE_INIT_INSTANCE(slave, n) \
|
2020-03-26 15:21:48 +01:00
|
|
|
|
const struct dsa_slave_config dsa_0_slave_##slave##_config = { \
|
|
|
|
|
.mac_addr = DT_PROP_OR(slave, local_mac_address, {0}) \
|
|
|
|
|
}; \
|
|
|
|
|
NET_DEVICE_INIT_INSTANCE(dsa_slave_port_##slave, \
|
|
|
|
|
DT_LABEL(slave), \
|
2021-05-06 00:14:09 +02:00
|
|
|
|
n, \
|
2020-03-26 15:21:48 +01:00
|
|
|
|
dsa_port_init, \
|
2021-04-28 10:43:35 +02:00
|
|
|
|
NULL, \
|
2021-05-06 00:14:09 +02:00
|
|
|
|
&dsa_context_##n, \
|
2020-03-26 15:21:48 +01:00
|
|
|
|
&dsa_0_slave_##slave##_config, \
|
|
|
|
|
CONFIG_ETH_INIT_PRIORITY, \
|
|
|
|
|
&dsa_eth_api_funcs, \
|
|
|
|
|
ETHERNET_L2, \
|
|
|
|
|
NET_L2_GET_CTX_TYPE(ETHERNET_L2), \
|
|
|
|
|
NET_ETH_MTU);
|
|
|
|
|
|
2021-05-06 00:14:09 +02:00
|
|
|
|
#define NET_SLAVE_DEVICE_0_INIT_INSTANCE(slave) \
|
|
|
|
|
NET_SLAVE_DEVICE_INIT_INSTANCE(slave, 0)
|
|
|
|
|
#define NET_SLAVE_DEVICE_1_INIT_INSTANCE(slave) \
|
|
|
|
|
NET_SLAVE_DEVICE_INIT_INSTANCE(slave, 1)
|
|
|
|
|
#define NET_SLAVE_DEVICE_2_INIT_INSTANCE(slave) \
|
|
|
|
|
NET_SLAVE_DEVICE_INIT_INSTANCE(slave, 2)
|
|
|
|
|
#define NET_SLAVE_DEVICE_3_INIT_INSTANCE(slave) \
|
|
|
|
|
NET_SLAVE_DEVICE_INIT_INSTANCE(slave, 3)
|
|
|
|
|
#define NET_SLAVE_DEVICE_4_INIT_INSTANCE(slave) \
|
|
|
|
|
NET_SLAVE_DEVICE_INIT_INSTANCE(slave, 4)
|
|
|
|
|
|
|
|
|
|
#define DSA_DEVICE(n) \
|
|
|
|
|
static struct ksz8xxx_data dsa_device_prv_data_##n = { \
|
|
|
|
|
.iface_init_count = 0, \
|
|
|
|
|
.is_init = false, \
|
|
|
|
|
}; \
|
|
|
|
|
static struct dsa_context dsa_context_##n = { \
|
|
|
|
|
.num_slave_ports = DT_INST_PROP(0, dsa_slave_ports), \
|
|
|
|
|
.dapi = &dsa_api_f, \
|
|
|
|
|
.prv_data = (void *)&dsa_device_prv_data_##n, \
|
|
|
|
|
}; \
|
|
|
|
|
DT_INST_FOREACH_CHILD_VARGS(n, NET_SLAVE_DEVICE_INIT_INSTANCE, n);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DT_INST_FOREACH_STATUS_OKAY(DSA_DEVICE);
|