drivers: ethernet: adin2111: add Open Alliance SPI support
Add Open Alliance spi protocol support. Open Alliance is a chunk-based SPI protocol, based on sending over SPI an ethernet frame divided in smaller chunks, using a specific 32-bit header for each chunk transferred. All chunks can be sent or received by a single dma transfer. Default mode is set to Open Alliance SPI without protection, since the adin2111 dev. board comes shipped this way. Tested: - Open Alliance SPI, no protection (default board shipped) - Open Alliance SPI, protection - Generic SPI, no crc - Generic SPI, with crc8 Signed-off-by: Angelo Dureghello <adureghello@baylibre.com>
This commit is contained in:
parent
06c8460a9e
commit
0ca8b0756b
7 changed files with 488 additions and 45 deletions
|
@ -5,9 +5,11 @@
|
|||
|
||||
if BOARD_ADI_EVAL_ADIN1110EBZ
|
||||
|
||||
config SPI_STM32_INTERRUPT
|
||||
config BOARD
|
||||
default "adi_eval_adin1110ebz"
|
||||
|
||||
config SPI_STM32_DMA
|
||||
default y
|
||||
depends on SPI
|
||||
|
||||
config MDIO_INIT_PRIORITY
|
||||
default 81
|
||||
|
|
|
@ -197,6 +197,11 @@
|
|||
pinctrl-0 = <&spi2_sck_pb13 &spi2_miso_pb14 &spi2_mosi_pb15>;
|
||||
pinctrl-names = "default";
|
||||
cs-gpios = <&gpiob 12 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
|
||||
dmas = <&dmamux1 2 13 (STM32_DMA_MEMORY_TO_PERIPH | STM32_DMA_MEM_INC |
|
||||
STM32_DMA_MEM_8BITS | STM32_DMA_PERIPH_8BITS)>,
|
||||
<&dmamux1 3 12 (STM32_DMA_PERIPH_TO_MEMORY | STM32_DMA_MEM_INC |
|
||||
STM32_DMA_MEM_8BITS | STM32_DMA_PERIPH_8BITS)>;
|
||||
dma-names = "tx", "rx";
|
||||
status = "okay";
|
||||
|
||||
adin1110: adin1110@0 {
|
||||
|
@ -205,6 +210,9 @@
|
|||
spi-max-frequency = <25000000>;
|
||||
int-gpios = <&gpiob 11 GPIO_ACTIVE_LOW>;
|
||||
reset-gpios = <&gpioc 7 GPIO_ACTIVE_LOW>;
|
||||
status = "okay";
|
||||
spi-oa;
|
||||
spi-oa-protection;
|
||||
|
||||
port1 {
|
||||
local-mac-address = [ 00 E0 22 FE DA C8 ];
|
||||
|
|
|
@ -5,9 +5,8 @@
|
|||
|
||||
if BOARD_ADI_EVAL_ADIN2111EBZ
|
||||
|
||||
config SPI_STM32_INTERRUPT
|
||||
default y
|
||||
depends on SPI
|
||||
config BOARD
|
||||
default "adi_eval_adin2111ebz"
|
||||
|
||||
config MDIO_INIT_PRIORITY
|
||||
default 81
|
||||
|
@ -17,6 +16,9 @@ config PHY_INIT_PRIORITY
|
|||
default 82
|
||||
depends on NET_L2_ETHERNET && ETH_DRIVER
|
||||
|
||||
config SPI_STM32_DMA
|
||||
default y
|
||||
|
||||
if NETWORKING
|
||||
|
||||
config NET_L2_ETHERNET
|
||||
|
|
|
@ -162,6 +162,11 @@
|
|||
pinctrl-names = "default";
|
||||
cs-gpios = <&gpiob 12 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
|
||||
status = "okay";
|
||||
dmas = <&dmamux1 2 13 (STM32_DMA_MEMORY_TO_PERIPH | STM32_DMA_MEM_INC |
|
||||
STM32_DMA_MEM_8BITS | STM32_DMA_PERIPH_8BITS)>,
|
||||
<&dmamux1 3 12 (STM32_DMA_PERIPH_TO_MEMORY | STM32_DMA_MEM_INC |
|
||||
STM32_DMA_MEM_8BITS | STM32_DMA_PERIPH_8BITS)>;
|
||||
dma-names = "tx", "rx";
|
||||
|
||||
adin2111: adin2111@0 {
|
||||
compatible = "adi,adin2111";
|
||||
|
@ -169,6 +174,8 @@
|
|||
spi-max-frequency = <25000000>;
|
||||
int-gpios = <&gpioa 12 GPIO_ACTIVE_LOW>;
|
||||
status = "okay";
|
||||
spi-oa;
|
||||
spi-oa-protection;
|
||||
|
||||
port1 {
|
||||
local-mac-address = [ 00 E0 22 FE DA C9 ];
|
||||
|
|
|
@ -25,6 +25,8 @@ LOG_MODULE_REGISTER(eth_adin2111, CONFIG_ETHERNET_LOG_LEVEL);
|
|||
#include "phy/phy_adin2111_priv.h"
|
||||
#include "eth_adin2111_priv.h"
|
||||
|
||||
#define DT_DRV_COMPAT adi_adin2111
|
||||
|
||||
/* SPI Communication check retry delay */
|
||||
#define ADIN2111_DEV_AWAIT_DELAY_POLL_US 100U
|
||||
/* Number of retries SPI Communication check */
|
||||
|
@ -46,6 +48,10 @@ 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
|
||||
/* 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 */
|
||||
#define ADIN2111_SW_RESET_DELAY_MS 10U
|
||||
|
||||
int eth_adin2111_lock(const struct device *dev, k_timeout_t timeout)
|
||||
{
|
||||
|
@ -61,43 +67,288 @@ int eth_adin2111_unlock(const struct device *dev)
|
|||
return k_mutex_unlock(&ctx->lock);
|
||||
}
|
||||
|
||||
int eth_adin2111_reg_write(const struct device *dev, const uint16_t reg,
|
||||
const uint32_t val)
|
||||
static inline bool eth_adin2111_oa_get_parity(const uint32_t x)
|
||||
{
|
||||
const struct adin2111_config *cfg = dev->config;
|
||||
size_t header_size = ADIN2111_WRITE_HEADER_SIZE;
|
||||
size_t data_size = sizeof(uint32_t);
|
||||
#if CONFIG_ETH_ADIN2111_SPI_CFG0
|
||||
uint8_t buf[ADIN2111_REG_WRITE_BUF_SIZE_CRC] = { 0 };
|
||||
#else
|
||||
uint8_t buf[ADIN2111_REG_WRITE_BUF_SIZE] = { 0 };
|
||||
#endif /* CONFIG_ETH_ADIN2111_SPI_CFG0 */
|
||||
uint32_t y;
|
||||
|
||||
/* spi header */
|
||||
*(uint16_t *)buf = htons((ADIN2111_WRITE_TXN_CTRL | reg));
|
||||
#if CONFIG_ETH_ADIN2111_SPI_CFG0
|
||||
buf[2] = crc8_ccitt(0, buf, header_size);
|
||||
++header_size;
|
||||
#endif /* CONFIG_ETH_ADIN2111_SPI_CFG0 */
|
||||
y = x ^ (x >> 1);
|
||||
y = y ^ (y >> 2);
|
||||
y = y ^ (y >> 4);
|
||||
y = y ^ (y >> 8);
|
||||
y = y ^ (y >> 16);
|
||||
|
||||
/* reg */
|
||||
*(uint32_t *)(buf + header_size) = htonl(val);
|
||||
#if CONFIG_ETH_ADIN2111_SPI_CFG0
|
||||
buf[header_size + data_size] = crc8_ccitt(0, &buf[header_size], data_size);
|
||||
++data_size;
|
||||
#endif /* CONFIG_ETH_ADIN2111_SPI_CFG0 */
|
||||
|
||||
const struct spi_buf spi_tx_buf = {
|
||||
.buf = buf,
|
||||
.len = header_size + data_size
|
||||
};
|
||||
const struct spi_buf_set tx = { .buffers = &spi_tx_buf, .count = 1U };
|
||||
|
||||
return spi_write_dt(&cfg->spi, &tx);
|
||||
return !(y & 1);
|
||||
}
|
||||
|
||||
int eth_adin2111_reg_read(const struct device *dev, const uint16_t reg,
|
||||
uint32_t *val)
|
||||
int eth_adin2111_oa_spi_xfer(const struct device *dev, uint8_t *buf_rx, uint8_t *buf_tx, int len)
|
||||
{
|
||||
const struct adin2111_config *cfg = dev->config;
|
||||
|
||||
struct spi_buf tx_buf[1];
|
||||
struct spi_buf rx_buf[1];
|
||||
struct spi_buf_set tx;
|
||||
struct spi_buf_set rx;
|
||||
int ret;
|
||||
|
||||
tx_buf[0].buf = buf_tx;
|
||||
tx_buf[0].len = len;
|
||||
rx_buf[0].buf = buf_rx;
|
||||
rx_buf[0].len = len;
|
||||
|
||||
rx.buffers = rx_buf;
|
||||
rx.count = 1;
|
||||
tx.buffers = tx_buf;
|
||||
tx.count = 1;
|
||||
|
||||
ret = spi_transceive_dt(&cfg->spi, &tx, &rx);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("ERRR dma!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int eth_adin2111_reg_read_oa(const struct device *dev, const uint16_t reg,
|
||||
uint32_t *val)
|
||||
{
|
||||
struct adin2111_data *ctx = dev->data;
|
||||
uint32_t pval;
|
||||
uint32_t *hdr = (uint32_t *)ctx->oa_tx_buf;
|
||||
int len;
|
||||
int ret;
|
||||
|
||||
*hdr = reg << 8;
|
||||
if (reg >= 0x30) {
|
||||
*hdr |= ADIN2111_OA_CTL_MMS;
|
||||
}
|
||||
|
||||
*hdr |= eth_adin2111_oa_get_parity(*hdr);
|
||||
*hdr = sys_cpu_to_be32(*hdr);
|
||||
|
||||
len = (ctx->oa_prot) ? ADIN2111_OA_CTL_LEN_PROT : ADIN2111_OA_CTL_LEN;
|
||||
|
||||
ret = eth_adin2111_oa_spi_xfer(dev, ctx->oa_rx_buf, ctx->oa_tx_buf, len);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
*val = sys_be32_to_cpu(*(uint32_t *)&ctx->oa_rx_buf[8]);
|
||||
|
||||
/* In protected mode read data is followed by its compliment value */
|
||||
if (ctx->oa_prot) {
|
||||
pval = sys_be32_to_cpu(*(uint32_t *)&ctx->oa_rx_buf[12]);
|
||||
if (*val != ~pval) {
|
||||
LOG_ERR("OA protected mode rx error !");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int eth_adin2111_reg_write_oa(const struct device *dev, const uint16_t reg,
|
||||
uint32_t val)
|
||||
{
|
||||
struct adin2111_data *ctx = dev->data;
|
||||
uint32_t pval;
|
||||
uint32_t *hdr = (uint32_t *)ctx->oa_tx_buf;
|
||||
int len;
|
||||
int ret;
|
||||
|
||||
*hdr = reg << 8 | ADIN2111_OA_CTL_WNR;
|
||||
if (reg >= 0x30) {
|
||||
*hdr |= ADIN2111_OA_CTL_MMS;
|
||||
}
|
||||
|
||||
*hdr |= eth_adin2111_oa_get_parity(*hdr);
|
||||
*hdr = sys_cpu_to_be32(*hdr);
|
||||
|
||||
len = (ctx->oa_prot) ? ADIN2111_OA_CTL_LEN_PROT : ADIN2111_OA_CTL_LEN;
|
||||
|
||||
*(uint32_t *)&ctx->oa_tx_buf[4] = sys_cpu_to_be32(val);
|
||||
if (ctx->oa_prot) {
|
||||
*(uint32_t *)&ctx->oa_tx_buf[8] = sys_cpu_to_be32(~val);
|
||||
}
|
||||
|
||||
ret = eth_adin2111_oa_spi_xfer(dev, ctx->oa_rx_buf, ctx->oa_tx_buf, len);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ctx->oa_prot) {
|
||||
pval = sys_be32_to_cpu(*(uint32_t *)&ctx->oa_rx_buf[12]);
|
||||
if (val != ~pval) {
|
||||
LOG_ERR("OA protected mode tx error !");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int eth_adin2111_oa_data_read(const struct device *dev, int port)
|
||||
{
|
||||
struct adin2111_data *ctx = dev->data;
|
||||
struct net_if *iface = ((struct adin2111_port_data *)ctx->port[port]->data)->iface;
|
||||
struct net_pkt *pkt;
|
||||
uint32_t hdr, ftr;
|
||||
int i, len, rx_pos, ret, rca, swo;
|
||||
|
||||
ret = eth_adin2111_reg_read(dev, ADIN2111_BUFSTS, &rca);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("can't read BUFSTS");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
rca &= ADIN2111_BUFSTS_RCA_MASK;
|
||||
|
||||
/* Preare all tx headers */
|
||||
for (i = 0, len = 0; i < rca; ++i) {
|
||||
hdr = ADIN2111_OA_DATA_HDR_DNC;
|
||||
hdr |= eth_adin2111_oa_get_parity(hdr);
|
||||
|
||||
*(uint32_t *)&ctx->oa_tx_buf[len] = sys_cpu_to_be32(hdr);
|
||||
|
||||
len += sizeof(uint32_t) + ctx->oa_cps;
|
||||
}
|
||||
|
||||
ret = eth_adin2111_oa_spi_xfer(dev, ctx->oa_rx_buf, ctx->oa_tx_buf, len);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("SPI xfer failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0, rx_pos = 0; i < rca; ++i) {
|
||||
|
||||
ftr = sys_be32_to_cpu(*(uint32_t *)&ctx->oa_rx_buf[rx_pos + ctx->oa_cps]);
|
||||
|
||||
if (eth_adin2111_oa_get_parity(ftr)) {
|
||||
LOG_ERR("OA RX: Footer parity error !");
|
||||
return -EIO;
|
||||
}
|
||||
if (!(ftr & ADIN2111_OA_DATA_FTR_SYNC)) {
|
||||
LOG_ERR("OA RX: Configuration not in sync !");
|
||||
return -EIO;
|
||||
}
|
||||
if (!(ftr & ADIN2111_OA_DATA_FTR_DV)) {
|
||||
LOG_DBG("OA RX: Data chunk not valid, skip !");
|
||||
goto update_pos;
|
||||
}
|
||||
if (ftr & ADIN2111_OA_DATA_FTR_SV) {
|
||||
swo = (ftr & ADIN2111_OA_DATA_FTR_SWO_MSK) >> ADIN2111_OA_DATA_FTR_SWO;
|
||||
if (swo != 0) {
|
||||
LOG_ERR("OA RX: Misalignbed start of frame !");
|
||||
return -EIO;
|
||||
}
|
||||
/* Reset store cursor */
|
||||
ctx->scur = 0;
|
||||
}
|
||||
|
||||
len = (ftr & ADIN2111_OA_DATA_FTR_EV) ?
|
||||
((ftr & ADIN2111_OA_DATA_FTR_EBO_MSK) >> ADIN2111_OA_DATA_FTR_EBO) + 1 :
|
||||
ctx->oa_cps;
|
||||
memcpy(&ctx->buf[ctx->scur], &ctx->oa_rx_buf[rx_pos], len);
|
||||
ctx->scur += len;
|
||||
|
||||
if (ftr & ADIN2111_OA_DATA_FTR_EV) {
|
||||
pkt = net_pkt_rx_alloc_with_buffer(iface, CONFIG_ETH_ADIN2111_BUFFER_SIZE,
|
||||
AF_UNSPEC, 0,
|
||||
K_MSEC(CONFIG_ETH_ADIN2111_TIMEOUT));
|
||||
if (!pkt) {
|
||||
LOG_ERR("OA RX: cannot allcate packet space, skipping.");
|
||||
return -EIO;
|
||||
}
|
||||
/* Skipping CRC32 */
|
||||
ret = net_pkt_write(pkt, ctx->buf, ctx->scur - sizeof(uint32_t));
|
||||
if (ret < 0) {
|
||||
net_pkt_unref(pkt);
|
||||
LOG_ERR("Failed to write pkt, scur %d, err %d", ctx->scur, ret);
|
||||
return ret;
|
||||
}
|
||||
ret = net_recv_data(iface, pkt);
|
||||
if (ret < 0) {
|
||||
net_pkt_unref(pkt);
|
||||
LOG_ERR("Port %u failed to enqueue frame to RX queue, %d",
|
||||
port, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
update_pos:
|
||||
rx_pos += ctx->oa_cps + sizeof(uint32_t);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setting up for a single dma transfer.
|
||||
*/
|
||||
static int eth_adin2111_send_oa_frame(const struct device *dev, struct net_pkt *pkt, int port)
|
||||
{
|
||||
struct adin2111_data *ctx = dev->data;
|
||||
uint16_t clen, len = net_pkt_get_len(pkt);
|
||||
uint32_t hdr;
|
||||
uint8_t chunks, i;
|
||||
int ret, txc, cur;
|
||||
|
||||
chunks = len / ctx->oa_cps;
|
||||
|
||||
if (len % ctx->oa_cps) {
|
||||
chunks++;
|
||||
}
|
||||
|
||||
ret = eth_adin2111_reg_read(dev, ADIN2111_BUFSTS, &txc);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Cannot read txc");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
txc = (txc & ADIN2111_BUFSTS_TXC_MASK) >> ADIN2111_BUFSTS_TXC;
|
||||
if (txc < chunks) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Prepare for single dma transfer */
|
||||
for (i = 1, cur = 0; i <= chunks; i++) {
|
||||
hdr = ADIN2111_OA_DATA_HDR_DNC | ADIN2111_OA_DATA_HDR_DV |
|
||||
ADIN2111_OA_DATA_HDR_NORX;
|
||||
hdr |= (!!port << ADIN2111_OA_DATA_HDR_VS);
|
||||
if (i == 1) {
|
||||
hdr |= ADIN2111_OA_DATA_HDR_SV;
|
||||
}
|
||||
if (i == chunks) {
|
||||
hdr |= ADIN2111_OA_DATA_HDR_EV;
|
||||
hdr |= (ctx->oa_cps - 1) << ADIN2111_OA_DATA_HDR_EBO;
|
||||
}
|
||||
|
||||
hdr |= eth_adin2111_oa_get_parity(hdr);
|
||||
|
||||
*(uint32_t *)&ctx->oa_tx_buf[cur] = sys_cpu_to_be32(hdr);
|
||||
cur += sizeof(uint32_t);
|
||||
|
||||
clen = len > ctx->oa_cps ? ctx->oa_cps : len;
|
||||
ret = net_pkt_read(pkt, &ctx->oa_tx_buf[cur], clen);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Cannot read from tx packet");
|
||||
return ret;
|
||||
}
|
||||
cur += ctx->oa_cps;
|
||||
len -= clen;
|
||||
}
|
||||
|
||||
ret = eth_adin2111_oa_spi_xfer(dev, ctx->oa_rx_buf, ctx->oa_tx_buf, cur);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Error on SPI xfer");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int eth_adin2111_reg_read_generic(const struct device *dev,
|
||||
const uint16_t reg,
|
||||
uint32_t *val)
|
||||
{
|
||||
const struct adin2111_config *cfg = dev->config;
|
||||
size_t header_len = ADIN2111_READ_HEADER_SIZE;
|
||||
|
@ -149,6 +400,72 @@ int eth_adin2111_reg_read(const struct device *dev, const uint16_t reg,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int eth_adin2111_reg_write_generic(const struct device *dev,
|
||||
const uint16_t reg,
|
||||
const uint32_t val)
|
||||
{
|
||||
const struct adin2111_config *cfg = dev->config;
|
||||
size_t header_size = ADIN2111_WRITE_HEADER_SIZE;
|
||||
size_t data_size = sizeof(uint32_t);
|
||||
#if CONFIG_ETH_ADIN2111_SPI_CFG0
|
||||
uint8_t buf[ADIN2111_REG_WRITE_BUF_SIZE_CRC] = { 0 };
|
||||
#else
|
||||
uint8_t buf[ADIN2111_REG_WRITE_BUF_SIZE] = { 0 };
|
||||
#endif /* CONFIG_ETH_ADIN2111_SPI_CFG0 */
|
||||
|
||||
/* spi header */
|
||||
*(uint16_t *)buf = htons((ADIN2111_WRITE_TXN_CTRL | reg));
|
||||
#if CONFIG_ETH_ADIN2111_SPI_CFG0
|
||||
buf[2] = crc8_ccitt(0, buf, header_size);
|
||||
++header_size;
|
||||
#endif /* CONFIG_ETH_ADIN2111_SPI_CFG0 */
|
||||
|
||||
/* reg */
|
||||
*(uint32_t *)(buf + header_size) = htonl(val);
|
||||
#if CONFIG_ETH_ADIN2111_SPI_CFG0
|
||||
buf[header_size + data_size] = crc8_ccitt(0, &buf[header_size], data_size);
|
||||
++data_size;
|
||||
#endif /* CONFIG_ETH_ADIN2111_SPI_CFG0 */
|
||||
|
||||
const struct spi_buf spi_tx_buf = {
|
||||
.buf = buf,
|
||||
.len = header_size + data_size
|
||||
};
|
||||
const struct spi_buf_set tx = { .buffers = &spi_tx_buf, .count = 1U };
|
||||
|
||||
return spi_write_dt(&cfg->spi, &tx);
|
||||
}
|
||||
|
||||
int eth_adin2111_reg_read(const struct device *dev, const uint16_t reg,
|
||||
uint32_t *val)
|
||||
{
|
||||
struct adin2111_data *ctx = dev->data;
|
||||
int rval;
|
||||
|
||||
if (ctx->oa) {
|
||||
rval = eth_adin2111_reg_read_oa(dev, reg, val);
|
||||
} else {
|
||||
rval = eth_adin2111_reg_read_generic(dev, reg, val);
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
int eth_adin2111_reg_write(const struct device *dev, const uint16_t reg,
|
||||
const uint32_t val)
|
||||
{
|
||||
struct adin2111_data *ctx = dev->data;
|
||||
int rval;
|
||||
|
||||
if (ctx->oa) {
|
||||
rval = eth_adin2111_reg_write_oa(dev, reg, val);
|
||||
} else {
|
||||
rval = eth_adin2111_reg_write_generic(dev, reg, val);
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
static int adin2111_read_fifo(const struct device *dev, const uint8_t port)
|
||||
{
|
||||
const struct adin2111_config *cfg = dev->config;
|
||||
|
@ -302,11 +619,13 @@ static void adin2111_offload_thread(void *p1, void *p2, void *p3)
|
|||
goto continue_unlock;
|
||||
}
|
||||
|
||||
if (!ctx->oa) {
|
||||
#if CONFIG_ETH_ADIN2111_SPI_CFG0
|
||||
if (status0 & ADIN2111_STATUS1_SPI_ERR) {
|
||||
LOG_WRN("Detected TX SPI CRC error");
|
||||
if (status0 & ADIN2111_STATUS1_SPI_ERR) {
|
||||
LOG_WRN("Detected TX SPI CRC error");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif /* CONFIG_ETH_ADIN2111_SPI_CFG0 */
|
||||
|
||||
/* handle port 1 phy interrupts */
|
||||
if (status0 & ADIN2111_STATUS0_PHYINT) {
|
||||
|
@ -318,6 +637,22 @@ static void adin2111_offload_thread(void *p1, void *p2, void *p3)
|
|||
adin2111_port_on_phyint(ctx->port[1]);
|
||||
}
|
||||
|
||||
if (ctx->oa) {
|
||||
if (status1 & ADIN2111_STATUS1_P1_RX_RDY) {
|
||||
ret = eth_adin2111_oa_data_read(dev, 0);
|
||||
if (ret < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (status1 & ADIN2111_STATUS1_P2_RX_RDY) {
|
||||
ret = eth_adin2111_oa_data_read(dev, 1);
|
||||
if (ret < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
goto continue_unlock;
|
||||
}
|
||||
|
||||
/* handle port 1 rx */
|
||||
if (status1 & ADIN2111_STATUS1_P1_RX_RDY) {
|
||||
do {
|
||||
|
@ -368,7 +703,7 @@ continue_unlock:
|
|||
LOG_ERR("Failed to write IMASK1, %d", ret);
|
||||
}
|
||||
eth_adin2111_unlock(dev);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
static void adin2111_int_callback(const struct device *dev,
|
||||
|
@ -416,6 +751,31 @@ static int adin2111_port_send(const struct device *dev, struct net_pkt *pkt)
|
|||
|
||||
eth_adin2111_lock(adin, K_FOREVER);
|
||||
|
||||
if (ctx->oa) {
|
||||
uint32_t val, rca = 0;
|
||||
/*
|
||||
* By high-traffic zperf test, noted that ADIN2111 does not like we send
|
||||
* if there is something to be received. It stops to issue rx interrupts
|
||||
* and zperf transfer hangs. Forcing a receive for this case.
|
||||
*/
|
||||
ret = eth_adin2111_reg_read(adin, ADIN2111_BUFSTS, &val);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
rca = val & ADIN2111_BUFSTS_RCA_MASK;
|
||||
|
||||
if (rca > 0) {
|
||||
eth_adin2111_unlock(adin);
|
||||
k_sem_give(&ctx->offload_sem);
|
||||
k_yield();
|
||||
eth_adin2111_lock(adin, K_FOREVER);
|
||||
}
|
||||
|
||||
ret = eth_adin2111_send_oa_frame(cfg->adin, pkt, htons(cfg->port_idx));
|
||||
|
||||
goto end_check;
|
||||
}
|
||||
|
||||
/* query remaining tx fifo space */
|
||||
ret = adin2111_read_tx_space(adin, &tx_space);
|
||||
if (ret < 0) {
|
||||
|
@ -497,6 +857,7 @@ static int adin2111_port_send(const struct device *dev, struct net_pkt *pkt)
|
|||
|
||||
ret = spi_write_dt(&((const struct adin2111_config *) adin->config)->spi,
|
||||
&tx);
|
||||
end_check:
|
||||
if (ret < 0) {
|
||||
eth_stats_update_errors_tx(data->iface);
|
||||
LOG_ERR("Port %u frame SPI write failed, %d", cfg->port_idx, ret);
|
||||
|
@ -670,7 +1031,7 @@ static void adin2111_port_iface_init(struct net_if *iface)
|
|||
|
||||
/* all ifaces are done, start INT processing */
|
||||
k_thread_create(&ctx->rx_thread, ctx->rx_thread_stack,
|
||||
CONFIG_ETH_ADIN2111_IRQ_THREAD_STACK_SIZE,
|
||||
K_KERNEL_STACK_SIZEOF(ctx->rx_thread_stack),
|
||||
adin2111_offload_thread,
|
||||
(void *)adin, NULL, NULL,
|
||||
CONFIG_ETH_ADIN2111_IRQ_THREAD_PRIO,
|
||||
|
@ -839,6 +1200,8 @@ static int adin2111_init(const struct device *dev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
k_msleep(ADIN2111_SPI_ACTIVE_DELAY_MS);
|
||||
|
||||
ret = adin2111_check_spi(dev);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to communicate over SPI, %d", ret);
|
||||
|
@ -852,6 +1215,8 @@ static int adin2111_init(const struct device *dev)
|
|||
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);
|
||||
|
@ -871,6 +1236,10 @@ static int adin2111_init(const struct device *dev)
|
|||
val &= ~ADIN2111_CONFIG0_RXCTE;
|
||||
val &= ~(ADIN2111_CONFIG0_TXCTE | ADIN2111_CONFIG0_TXFCSVE);
|
||||
|
||||
if (ctx->oa) {
|
||||
val |= ADIN2111_CONFIG0_ZARFE;
|
||||
}
|
||||
|
||||
ret = eth_adin2111_reg_write(dev, ADIN2111_CONFIG0, val);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to write CONFIG0, %d", ret);
|
||||
|
@ -966,12 +1335,11 @@ static const struct ethernet_api adin2111_port_api = {
|
|||
NET_L2_GET_CTX_TYPE(ETHERNET_L2), NET_ETH_MTU);
|
||||
|
||||
#define ADIN2111_SPI_OPERATION ((uint16_t)(SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_WORD_SET(8)))
|
||||
|
||||
#define ADIN2111_MAC_INITIALIZE(inst, dev_id, ifaces, name) \
|
||||
static uint8_t __aligned(4) name##_buffer_##inst[CONFIG_ETH_ADIN2111_BUFFER_SIZE]; \
|
||||
static const struct adin2111_config name##_config_##inst = { \
|
||||
.id = dev_id, \
|
||||
.spi = SPI_DT_SPEC_INST_GET(inst, ADIN2111_SPI_OPERATION, 1), \
|
||||
.id = dev_id, \
|
||||
.spi = SPI_DT_SPEC_INST_GET(inst, ADIN2111_SPI_OPERATION, 0), \
|
||||
.interrupt = GPIO_DT_SPEC_INST_GET(inst, int_gpios), \
|
||||
.reset = GPIO_DT_SPEC_INST_GET_OR(inst, reset_gpios, { 0 }), \
|
||||
}; \
|
||||
|
@ -981,6 +1349,9 @@ static const struct ethernet_api adin2111_port_api = {
|
|||
.offload_sem = Z_SEM_INITIALIZER(name##_data_##inst.offload_sem, 0, 1), \
|
||||
.lock = Z_MUTEX_INITIALIZER(name##_data_##inst.lock), \
|
||||
.buf = name##_buffer_##inst, \
|
||||
.oa = DT_INST_PROP(inst, spi_oa), \
|
||||
.oa_prot = DT_INST_PROP(inst, spi_oa_protection), \
|
||||
.oa_cps = 64, \
|
||||
}; \
|
||||
/* adin */ \
|
||||
DEVICE_DT_DEFINE(DT_DRV_INST(inst), adin2111_init, NULL, \
|
||||
|
|
|
@ -33,6 +33,10 @@
|
|||
#define ADIN2111_CONFIG0_SYNC BIT(15)
|
||||
/* Transmit Frame Check Sequence Validation Enable */
|
||||
#define ADIN2111_CONFIG0_TXFCSVE BIT(14)
|
||||
/* Zero Align Receive Frame Enable */
|
||||
#define ADIN2111_CONFIG0_ZARFE BIT(12)
|
||||
/* New packet received only after a new CS assertion */
|
||||
#define ADIN2111_CONFIG0_CSARFE BIT(13)
|
||||
/* Transmit Cut Through Enable */
|
||||
#define ADIN2111_CONFIG0_TXCTE BIT(9)
|
||||
/* Receive Cut Through Enable. Must be 0 for Generic SPI */
|
||||
|
@ -72,9 +76,18 @@
|
|||
#define ADIN2111_STATUS1_SPI_ERR BIT(10)
|
||||
/* Port 1 RX FIFO Contains Data */
|
||||
#define ADIN2111_STATUS1_P1_RX_RDY BIT(4)
|
||||
/* Frame transmitted */
|
||||
#define ADIN2111_STATUS1_TX_RDY BIT(3)
|
||||
/* Value to completely clear status register 1 */
|
||||
#define ADIN2111_STATUS1_CLEAR 0xFFF01F08U
|
||||
|
||||
/* Buffer Status Register */
|
||||
#define ADIN2111_BUFSTS 0x0BU
|
||||
/* Rx chunks available */
|
||||
#define ADIN2111_BUFSTS_RCA_MASK GENMASK(7, 0)
|
||||
/* Tx credits */
|
||||
#define ADIN2111_BUFSTS_TXC 8U
|
||||
#define ADIN2111_BUFSTS_TXC_MASK GENMASK(15, 8)
|
||||
|
||||
/* Interrupt Mask Register 0 */
|
||||
#define ADIN2111_IMASK0 0x0CU
|
||||
|
@ -161,6 +174,34 @@
|
|||
/* Manufacturer unique ID */
|
||||
#define ADIN2111_PHYID_OUI 0xa0ef
|
||||
|
||||
/* Open Alliance definitions */
|
||||
#define ADIN2111_OA_ALLOC_TIMEOUT K_MSEC(10)
|
||||
/* Max setting to a max RCA of 255 68-bytes ckunks */
|
||||
#define ADIN2111_OA_BUF_SZ (255U * 64U)
|
||||
|
||||
#define ADIN2111_OA_CTL_LEN_PROT 16U
|
||||
#define ADIN2111_OA_CTL_LEN 12U
|
||||
#define ADIN2111_OA_CTL_MMS BIT(24)
|
||||
#define ADIN2111_OA_CTL_WNR BIT(29)
|
||||
|
||||
#define ADIN2111_OA_DATA_HDR_DNC BIT(31)
|
||||
#define ADIN2111_OA_DATA_HDR_NORX BIT(29)
|
||||
#define ADIN2111_OA_DATA_HDR_VS 22U
|
||||
#define ADIN2111_OA_DATA_HDR_DV BIT(21)
|
||||
#define ADIN2111_OA_DATA_HDR_SV BIT(20)
|
||||
#define ADIN2111_OA_DATA_HDR_EV BIT(14)
|
||||
#define ADIN2111_OA_DATA_HDR_EBO 8U
|
||||
|
||||
#define ADIN2111_OA_DATA_FTR_SYNC BIT(29)
|
||||
#define ADIN2111_OA_DATA_FTR_EBO 8U
|
||||
#define ADIN2111_OA_DATA_FTR_DV BIT(21)
|
||||
#define ADIN2111_OA_DATA_FTR_SV BIT(20)
|
||||
#define ADIN2111_OA_DATA_FTR_EV BIT(14)
|
||||
#define ADIN2111_OA_DATA_FTR_SWO 16U
|
||||
#define ADIN2111_OA_DATA_FTR_SWO_MSK GENMASK(19, 16)
|
||||
#define ADIN2111_OA_DATA_FTR_EBO 8U
|
||||
#define ADIN2111_OA_DATA_FTR_EBO_MSK GENMASK(13, 8)
|
||||
|
||||
enum adin2111_chips_id {
|
||||
ADIN2111_MAC = 0,
|
||||
ADIN1110_MAC,
|
||||
|
@ -183,6 +224,12 @@ struct adin2111_data {
|
|||
uint32_t imask1;
|
||||
uint16_t ifaces_left_to_init;
|
||||
uint8_t *buf;
|
||||
uint16_t scur;
|
||||
bool oa;
|
||||
bool oa_prot;
|
||||
uint8_t oa_cps;
|
||||
uint8_t oa_tx_buf[ADIN2111_OA_BUF_SZ];
|
||||
uint8_t oa_rx_buf[ADIN2111_OA_BUF_SZ];
|
||||
|
||||
K_KERNEL_STACK_MEMBER(rx_thread_stack, CONFIG_ETH_ADIN2111_IRQ_THREAD_STACK_SIZE);
|
||||
struct k_thread rx_thread;
|
||||
|
|
|
@ -53,6 +53,12 @@ properties:
|
|||
reset-gpios:
|
||||
type: phandle-array
|
||||
description: The reset pin of ADIN2111.
|
||||
spi-oa:
|
||||
type: boolean
|
||||
description: Enables Open Alliance SPI protocol.
|
||||
spi-oa-protection:
|
||||
type: boolean
|
||||
description: Enables Open Alliance SPI protocol protection.
|
||||
|
||||
child-binding:
|
||||
description: Port properties
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue