lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <cdf502cd-1ed1-427c-9de0-743315568118@iscas.ac.cn>
Date: Thu, 3 Jul 2025 15:58:04 +0800
From: Vivian Wang <wangruikang@...as.ac.cn>
To: Simon Horman <horms@...nel.org>
Cc: Andrew Lunn <andrew+netdev@...n.ch>, "David S. Miller"
 <davem@...emloft.net>, Eric Dumazet <edumazet@...gle.com>,
 Jakub Kicinski <kuba@...nel.org>, Paolo Abeni <pabeni@...hat.com>,
 Rob Herring <robh@...nel.org>, Krzysztof Kozlowski <krzk+dt@...nel.org>,
 Conor Dooley <conor+dt@...nel.org>, Yixun Lan <dlan@...too.org>,
 Philipp Zabel <p.zabel@...gutronix.de>,
 Paul Walmsley <paul.walmsley@...ive.com>, Palmer Dabbelt
 <palmer@...belt.com>, Albert Ou <aou@...s.berkeley.edu>,
 Alexandre Ghiti <alex@...ti.fr>, Vivian Wang <uwu@...m.page>,
 Lukas Bulwahn <lukas.bulwahn@...hat.com>,
 Geert Uytterhoeven <geert+renesas@...der.be>,
 Parthiban Veerasooran <Parthiban.Veerasooran@...rochip.com>,
 netdev@...r.kernel.org, devicetree@...r.kernel.org,
 linux-riscv@...ts.infradead.org, spacemit@...ts.linux.dev,
 linux-kernel@...r.kernel.org
Subject: Re: [PATCH net-next v3 2/5] net: spacemit: Add K1 Ethernet MAC

Hi Simon,

Thanks for the suggestions.

On 7/3/25 15:19, Simon Horman wrote:
> On Wed, Jul 02, 2025 at 02:01:41PM +0800, Vivian Wang wrote:
>
> ...
>
>> diff --git a/drivers/net/ethernet/spacemit/k1_emac.c b/drivers/net/ethernet/spacemit/k1_emac.c
> ...
>
>> +static int emac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
>> +{
>> +	struct emac_priv *priv = netdev_priv(ndev);
>> +	int nfrags = skb_shinfo(skb)->nr_frags;
>> +	struct device *dev = &priv->pdev->dev;
>> +
>> +	if (unlikely(emac_tx_avail(priv) < nfrags + 1)) {
>> +		if (!netif_queue_stopped(ndev)) {
>> +			netif_stop_queue(ndev);
>> +			dev_err_ratelimited(dev, "TX ring full, stop TX queue\n");
>> +		}
>> +		return NETDEV_TX_BUSY;
>> +	}
>> +
>> +	emac_tx_mem_map(priv, skb);
>> +
>> +	ndev->stats.tx_packets++;
>> +	ndev->stats.tx_bytes += skb->len;
>> +
>> +	/* Make sure there is space in the ring for the next TX. */
>> +	if (unlikely(emac_tx_avail(priv) <= MAX_SKB_FRAGS + 2))
>> +		netif_stop_queue(ndev);
>> +
>> +	return NETDEV_TX_OK;
>> +}
>> +
>> +static u32 emac_tx_read_stat_cnt(struct emac_priv *priv, u8 cnt)
>> +{
>> +	u32 val, tmp;
>> +	int ret;
>> +
>> +	val = 0x8000 | cnt;
>> +	emac_wr(priv, MAC_TX_STATCTR_CONTROL, val);
>> +	val = emac_rd(priv, MAC_TX_STATCTR_CONTROL);
>> +
>> +	ret = readl_poll_timeout_atomic(priv->iobase + MAC_TX_STATCTR_CONTROL,
>> +					val, !(val & 0x8000), 100, 10000);
>> +
>> +	if (ret) {
>> +		netdev_err(priv->ndev, "read TX stat timeout\n");
>> +		return ret;
>> +	}
>> +
>> +	tmp = emac_rd(priv, MAC_TX_STATCTR_DATA_HIGH);
>> +	val = tmp << 16;
>> +	tmp = emac_rd(priv, MAC_TX_STATCTR_DATA_LOW);
>> +	val |= tmp;
>> +
>> +	return val;
>> +}
>> +
>> +static u32 emac_rx_read_stat_cnt(struct emac_priv *priv, u8 cnt)
>> +{
>> +	u32 val, tmp;
>> +	int ret;
>> +
>> +	val = 0x8000 | cnt;
>> +	emac_wr(priv, MAC_RX_STATCTR_CONTROL, val);
>> +	val = emac_rd(priv, MAC_RX_STATCTR_CONTROL);
>> +
>> +	ret = readl_poll_timeout_atomic(priv->iobase + MAC_RX_STATCTR_CONTROL,
>> +					val, !(val & 0x8000), 100, 10000);
>> +
>> +	if (ret) {
>> +		netdev_err(priv->ndev, "read RX stat timeout\n");
>> +		return ret;
>> +	}
>> +
>> +	tmp = emac_rd(priv, MAC_RX_STATCTR_DATA_HIGH);
>> +	val = tmp << 16;
>> +	tmp = emac_rd(priv, MAC_RX_STATCTR_DATA_LOW);
>> +	val |= tmp;
>> +
>> +	return val;
>> +}
>> +
>> +static int emac_set_mac_address(struct net_device *ndev, void *addr)
>> +{
>> +	struct emac_priv *priv = netdev_priv(ndev);
>> +	int ret = eth_mac_addr(ndev, addr);
>> +
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* If running, set now; if not running it will be set in emac_up. */
>> +	if (netif_running(ndev))
>> +		emac_set_mac_addr(priv, ndev->dev_addr);
>> +
>> +	return 0;
>> +}
>> +
>> +static void emac_mac_multicast_filter_clear(struct emac_priv *priv)
>> +{
>> +	emac_wr(priv, MAC_MULTICAST_HASH_TABLE1, 0x0);
>> +	emac_wr(priv, MAC_MULTICAST_HASH_TABLE2, 0x0);
>> +	emac_wr(priv, MAC_MULTICAST_HASH_TABLE3, 0x0);
>> +	emac_wr(priv, MAC_MULTICAST_HASH_TABLE4, 0x0);
>> +}
>> +
>> +/* Configure Multicast and Promiscuous modes */
>> +static void emac_rx_mode_set(struct net_device *ndev)
>> +{
>> +	struct emac_priv *priv = netdev_priv(ndev);
>> +	u32 crc32, bit, reg, hash, val;
>> +	struct netdev_hw_addr *ha;
>> +	u32 mc_filter[4] = { 0 };
>> +
>> +	val = emac_rd(priv, MAC_ADDRESS_CONTROL);
>> +
>> +	val &= ~MREGBIT_PROMISCUOUS_MODE;
>> +
>> +	if (ndev->flags & IFF_PROMISC) {
>> +		/* Enable promisc mode */
>> +		val |= MREGBIT_PROMISCUOUS_MODE;
>> +	} else if ((ndev->flags & IFF_ALLMULTI) ||
>> +		   (netdev_mc_count(ndev) > HASH_TABLE_SIZE)) {
>> +		/* Accept all multicast frames by setting every bit */
>> +		emac_wr(priv, MAC_MULTICAST_HASH_TABLE1, 0xffff);
>> +		emac_wr(priv, MAC_MULTICAST_HASH_TABLE2, 0xffff);
>> +		emac_wr(priv, MAC_MULTICAST_HASH_TABLE3, 0xffff);
>> +		emac_wr(priv, MAC_MULTICAST_HASH_TABLE4, 0xffff);
>> +	} else if (!netdev_mc_empty(ndev)) {
>> +		emac_mac_multicast_filter_clear(priv);
>> +		netdev_for_each_mc_addr(ha, ndev) {
>> +			/* Calculate the CRC of the MAC address */
>> +			crc32 = ether_crc(ETH_ALEN, ha->addr);
>> +
>> +			/*
>> +			 * The hash table is an array of 4 16-bit registers. It
>> +			 * is treated like an array of 64 bits (bits[hash]). Use
>> +			 * the upper 6 bits of the above CRC as the hash value.
>> +			 */
>> +			hash = (crc32 >> 26) & 0x3F;
>> +			reg = hash / 16;
>> +			bit = hash % 16;
>> +			mc_filter[reg] |= BIT(bit);
>> +		}
>> +		emac_wr(priv, MAC_MULTICAST_HASH_TABLE1, mc_filter[0]);
>> +		emac_wr(priv, MAC_MULTICAST_HASH_TABLE2, mc_filter[1]);
>> +		emac_wr(priv, MAC_MULTICAST_HASH_TABLE3, mc_filter[2]);
>> +		emac_wr(priv, MAC_MULTICAST_HASH_TABLE4, mc_filter[3]);
>> +	}
>> +
>> +	emac_wr(priv, MAC_ADDRESS_CONTROL, val);
>> +}
>> +
>> +static int emac_change_mtu(struct net_device *ndev, int mtu)
>> +{
>> +	struct emac_priv *priv = netdev_priv(ndev);
>> +	u32 frame_len;
>> +
>> +	if (netif_running(ndev)) {
>> +		netdev_err(ndev, "must be stopped to change MTU\n");
>> +		return -EBUSY;
>> +	}
>> +
>> +	frame_len = mtu + ETH_HLEN + ETH_FCS_LEN;
>> +
>> +	if (frame_len <= EMAC_DEFAULT_BUFSIZE)
>> +		priv->dma_buf_sz = EMAC_DEFAULT_BUFSIZE;
>> +	else if (frame_len <= EMAC_RX_BUF_2K)
>> +		priv->dma_buf_sz = EMAC_RX_BUF_2K;
>> +	else
>> +		priv->dma_buf_sz = EMAC_RX_BUF_4K;
>> +
>> +	ndev->mtu = mtu;
>> +
>> +	return 0;
>> +}
>> +
>> +static void emac_tx_timeout(struct net_device *ndev, unsigned int txqueue)
>> +{
>> +	struct emac_priv *priv = netdev_priv(ndev);
>> +
>> +	schedule_work(&priv->tx_timeout_task);
>> +}
>> +
>> +static int emac_mii_read(struct mii_bus *bus, int phy_addr, int regnum)
>> +{
>> +	struct emac_priv *priv = bus->priv;
>> +	u32 cmd = 0, val;
>> +	int ret;
>> +
>> +	cmd |= phy_addr & 0x1F;
>> +	cmd |= (regnum & 0x1F) << 5;
>> +	cmd |= MREGBIT_START_MDIO_TRANS | MREGBIT_MDIO_READ_WRITE;
>> +
>> +	emac_wr(priv, MAC_MDIO_DATA, 0x0);
>> +	emac_wr(priv, MAC_MDIO_CONTROL, cmd);
>> +
>> +	ret = readl_poll_timeout(priv->iobase + MAC_MDIO_CONTROL, val,
>> +				 !((val >> 15) & 0x1), 100, 10000);
>> +
>> +	if (ret)
>> +		return ret;
>> +
>> +	val = emac_rd(priv, MAC_MDIO_DATA);
>> +	return val;
>> +}
>> +
>> +static int emac_mii_write(struct mii_bus *bus, int phy_addr, int regnum,
>> +			  u16 value)
>> +{
>> +	struct emac_priv *priv = bus->priv;
>> +	u32 cmd = 0, val;
>> +	int ret;
>> +
>> +	emac_wr(priv, MAC_MDIO_DATA, value);
>> +
>> +	cmd |= phy_addr & 0x1F;
>> +	cmd |= (regnum & 0x1F) << 5;
>> +	cmd |= MREGBIT_START_MDIO_TRANS;
>> +
>> +	emac_wr(priv, MAC_MDIO_CONTROL, cmd);
>> +
>> +	ret = readl_poll_timeout(priv->iobase + MAC_MDIO_CONTROL, val,
>> +				 !((val >> 15) & 0x1), 100, 10000);
>> +
>> +	return ret;
>> +}
>> +
>> +static int emac_mdio_init(struct emac_priv *priv)
>> +{
>> +	struct device *dev = &priv->pdev->dev;
>> +	struct device_node *mii_np;
>> +	struct mii_bus *mii;
>> +	int ret;
>> +
>> +	mii = devm_mdiobus_alloc(dev);
>> +	if (!mii)
>> +		return -ENOMEM;
>> +
>> +	mii->priv = priv;
>> +	mii->name = "k1_emac_mii";
>> +	mii->read = emac_mii_read;
>> +	mii->write = emac_mii_write;
>> +	mii->parent = dev;
>> +	mii->phy_mask = 0xffffffff;
>> +	snprintf(mii->id, MII_BUS_ID_SIZE, "%s", priv->pdev->name);
>> +
>> +	mii_np = of_get_available_child_by_name(dev->of_node, "mdio-bus");
>> +
>> +	ret = devm_of_mdiobus_register(dev, mii, mii_np);
>> +	if (ret)
>> +		dev_err_probe(dev, ret, "Failed to register mdio bus\n");
>> +
>> +	of_node_put(mii_np);
>> +	return ret;
>> +}
>> +
>> +static void emac_get_strings(struct net_device *dev, u32 stringset, u8 *data)
>> +{
>> +	int i;
>> +
>> +	switch (stringset) {
>> +	case ETH_SS_STATS:
>> +		for (i = 0; i < ARRAY_SIZE(emac_ethtool_stats); i++) {
>> +			memcpy(data, emac_ethtool_stats[i].str,
>> +			       ETH_GSTRING_LEN);
>> +			data += ETH_GSTRING_LEN;
>> +		}
>> +		break;
>> +	}
>> +}
>> +
>> +static int emac_get_sset_count(struct net_device *dev, int sset)
>> +{
>> +	switch (sset) {
>> +	case ETH_SS_STATS:
>> +		return ARRAY_SIZE(emac_ethtool_stats);
>> +	default:
>> +		return -EOPNOTSUPP;
>> +	}
>> +}
>> +
>> +static void emac_stats_update(struct emac_priv *priv)
>> +{
>> +	struct emac_hw_stats *hwstats = priv->hw_stats;
>> +	u32 *stats = (u32 *)hwstats;
>> +	int i;
>> +
>> +	for (i = 0; i < EMAC_TX_STATS_NUM; i++)
>> +		stats[i] = emac_tx_read_stat_cnt(priv, i);
>> +
>> +	for (i = 0; i < EMAC_RX_STATS_NUM; i++)
>> +		stats[i + EMAC_TX_STATS_NUM] = emac_rx_read_stat_cnt(priv, i);
>> +}
>> +
>> +static void emac_get_ethtool_stats(struct net_device *dev,
>> +				   struct ethtool_stats *stats, u64 *data)
>> +{
>> +	struct emac_priv *priv = netdev_priv(dev);
>> +	struct emac_hw_stats *hwstats;
>> +	unsigned long flags;
>> +	u32 *data_src;
>> +	u64 *data_dst;
>> +	int i;
>> +
>> +	hwstats = priv->hw_stats;
>> +
>> +	if (netif_running(dev) && netif_device_present(dev)) {
>> +		if (spin_trylock_irqsave(&priv->stats_lock, flags)) {
>> +			emac_stats_update(priv);
>> +			spin_unlock_irqrestore(&priv->stats_lock, flags);
>> +		}
>> +	}
>> +
>> +	data_dst = data;
>> +
>> +	for (i = 0; i < ARRAY_SIZE(emac_ethtool_stats); i++) {
>> +		data_src = (u32 *)hwstats + emac_ethtool_stats[i].offset;
>> +		*data_dst++ = (u64)(*data_src);
>> +	}
>> +}
>> +
>> +static int emac_ethtool_get_regs_len(struct net_device *dev)
>> +{
>> +	return (EMAC_DMA_REG_CNT + EMAC_MAC_REG_CNT) * sizeof(u32);
>> +}
>> +
>> +static void emac_ethtool_get_regs(struct net_device *dev,
>> +				  struct ethtool_regs *regs, void *space)
>> +{
>> +	struct emac_priv *priv = netdev_priv(dev);
>> +	u32 *reg_space = space;
>> +	int i;
>> +
>> +	regs->version = 1;
>> +
>> +	for (i = 0; i < EMAC_DMA_REG_CNT; i++)
>> +		reg_space[i] = emac_rd(priv, DMA_CONFIGURATION + i * 4);
>> +
>> +	for (i = 0; i < EMAC_MAC_REG_CNT; i++)
>> +		reg_space[i + EMAC_DMA_REG_CNT] =
>> +			emac_rd(priv, MAC_GLOBAL_CONTROL + i * 4);
>> +}
>> +
>> +static void emac_get_drvinfo(struct net_device *dev,
>> +			     struct ethtool_drvinfo *info)
>> +{
>> +	strscpy(info->driver, DRIVER_NAME, sizeof(info->driver));
>> +	info->n_stats = ARRAY_SIZE(emac_ethtool_stats);
>> +}
>> +
>> +static void emac_tx_timeout_task(struct work_struct *work)
>> +{
>> +	struct net_device *ndev;
>> +	struct emac_priv *priv;
>> +
>> +	priv = container_of(work, struct emac_priv, tx_timeout_task);
>> +	ndev = priv->ndev;
>> +
>> +	rtnl_lock();
>> +
>> +	/* No need to reset if already down */
>> +	if (!netif_running(ndev)) {
>> +		rtnl_unlock();
>> +		return;
>> +	}
>> +
>> +	netdev_err(ndev, "MAC reset due to TX timeout\n");
>> +
>> +	netif_trans_update(ndev); /* prevent tx timeout */
>> +	dev_close(ndev);
>> +	dev_open(ndev, NULL);
>> +
>> +	rtnl_unlock();
>> +}
>> +
>> +static void emac_sw_init(struct emac_priv *priv)
>> +{
>> +	priv->dma_buf_sz = EMAC_DEFAULT_BUFSIZE;
>> +
>> +	priv->tx_ring.total_cnt = DEFAULT_TX_RING_NUM;
>> +	priv->rx_ring.total_cnt = DEFAULT_RX_RING_NUM;
>> +
>> +	spin_lock_init(&priv->stats_lock);
>> +
>> +	INIT_WORK(&priv->tx_timeout_task, emac_tx_timeout_task);
>> +
>> +	priv->tx_coal_frames = EMAC_TX_FRAMES;
>> +	priv->tx_coal_timeout = EMAC_TX_COAL_TIMEOUT;
>> +
>> +	timer_setup(&priv->txtimer, emac_tx_coal_timer, 0);
>> +}
>> +
> ...
>
>> +static irqreturn_t emac_interrupt_handler(int irq, void *dev_id)
>> +{
>> +	struct net_device *ndev = (struct net_device *)dev_id;
>> +	struct emac_priv *priv = netdev_priv(ndev);
>> +	bool should_schedule = false;
>> +	u32 status;
>> +	u32 clr = 0;
> nit: Reverse xmas tree - longest line to shortest - for
>      these local variable declarations please.
>
>      Edward Cree's tool can be helpful here:
>      https://github.com/ecree-solarflare/xmastree/commits/master/
>
> ...
>
Thanks for the script. I was indeed doing reverse xmas tree manually.

I will fix next version.

>> +static const struct net_device_ops emac_netdev_ops = {
>> +	.ndo_open               = emac_open,
>> +	.ndo_stop               = emac_close,
>> +	.ndo_start_xmit         = emac_start_xmit,
> I think that of emac_start_xmit should return netdev_tx_t rather than int
> to match the type of the .ndo_start_xmit member of this structure.
>
> Flagged by Clang 20.1.7 [-Wincompatible-function-pointer-types-strict]

I will fix the type next version.

Regards,
Vivian "dramforever" Wang

>> +	.ndo_set_mac_address    = emac_set_mac_address,
>> +	.ndo_eth_ioctl          = phy_do_ioctl_running,
>> +	.ndo_change_mtu         = emac_change_mtu,
>> +	.ndo_tx_timeout         = emac_tx_timeout,
>> +	.ndo_set_rx_mode        = emac_rx_mode_set,
>> +};
> ...


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ