Commit a526a3cc authored by Alexandru Tachici's avatar Alexandru Tachici Committed by David S. Miller
Browse files

net: ethernet: adi: adin1110: Fix SPI transfers



No need to use more than one SPI transfer for reads.
Use only one from now as ADIN1110/2111 does not tolerate
CS changes during reads.

The BCM2711/2708 SPI controllers worked fine, but the NXP
IMX8MM could not keep CS lowered during SPI bursts.

This change aims to make the ADIN1110/2111 driver compatible
with both SPI controllers, without any loss of bandwidth/other
capabilities.

Fixes: bc93e19d ("net: ethernet: adi: Add ADIN1110 support")
Signed-off-by: default avatarAlexandru Tachici <alexandru.tachici@analog.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent ac3208fb
Loading
Loading
Loading
Loading
+16 −21
Original line number Diff line number Diff line
@@ -196,7 +196,7 @@ static int adin1110_read_reg(struct adin1110_priv *priv, u16 reg, u32 *val)
{
	u32 header_len = ADIN1110_RD_HEADER_LEN;
	u32 read_len = ADIN1110_REG_LEN;
	struct spi_transfer t[2] = {0};
	struct spi_transfer t = {0};
	int ret;

	priv->data[0] = ADIN1110_CD | FIELD_GET(GENMASK(12, 8), reg);
@@ -209,17 +209,15 @@ static int adin1110_read_reg(struct adin1110_priv *priv, u16 reg, u32 *val)
		header_len++;
	}

	t[0].tx_buf = &priv->data[0];
	t[0].len = header_len;

	if (priv->append_crc)
		read_len++;

	memset(&priv->data[header_len], 0, read_len);
	t[1].rx_buf = &priv->data[header_len];
	t[1].len = read_len;
	t.tx_buf = &priv->data[0];
	t.rx_buf = &priv->data[0];
	t.len = read_len + header_len;

	ret = spi_sync_transfer(priv->spidev, t, 2);
	ret = spi_sync_transfer(priv->spidev, &t, 1);
	if (ret)
		return ret;

@@ -296,7 +294,7 @@ static int adin1110_read_fifo(struct adin1110_port_priv *port_priv)
{
	struct adin1110_priv *priv = port_priv->priv;
	u32 header_len = ADIN1110_RD_HEADER_LEN;
	struct spi_transfer t[2] = {0};
	struct spi_transfer t;
	u32 frame_size_no_fcs;
	struct sk_buff *rxb;
	u32 frame_size;
@@ -327,12 +325,7 @@ static int adin1110_read_fifo(struct adin1110_port_priv *port_priv)
		return ret;

	frame_size_no_fcs = frame_size - ADIN1110_FRAME_HEADER_LEN - ADIN1110_FEC_LEN;

	rxb = netdev_alloc_skb(port_priv->netdev, round_len);
	if (!rxb)
		return -ENOMEM;

	memset(priv->data, 0, round_len + ADIN1110_RD_HEADER_LEN);
	memset(priv->data, 0, ADIN1110_RD_HEADER_LEN);

	priv->data[0] = ADIN1110_CD | FIELD_GET(GENMASK(12, 8), reg);
	priv->data[1] = FIELD_GET(GENMASK(7, 0), reg);
@@ -342,21 +335,23 @@ static int adin1110_read_fifo(struct adin1110_port_priv *port_priv)
		header_len++;
	}

	skb_put(rxb, frame_size_no_fcs + ADIN1110_FRAME_HEADER_LEN);
	rxb = netdev_alloc_skb(port_priv->netdev, round_len + header_len);
	if (!rxb)
		return -ENOMEM;

	t[0].tx_buf = &priv->data[0];
	t[0].len = header_len;
	skb_put(rxb, frame_size_no_fcs + header_len + ADIN1110_FRAME_HEADER_LEN);

	t[1].rx_buf = &rxb->data[0];
	t[1].len = round_len;
	t.tx_buf = &priv->data[0];
	t.rx_buf = &rxb->data[0];
	t.len = header_len + round_len;

	ret = spi_sync_transfer(priv->spidev, t, 2);
	ret = spi_sync_transfer(priv->spidev, &t, 1);
	if (ret) {
		kfree_skb(rxb);
		return ret;
	}

	skb_pull(rxb, ADIN1110_FRAME_HEADER_LEN);
	skb_pull(rxb, header_len + ADIN1110_FRAME_HEADER_LEN);
	rxb->protocol = eth_type_trans(rxb, port_priv->netdev);

	if ((port_priv->flags & IFF_ALLMULTI && rxb->pkt_type == PACKET_MULTICAST) ||