[PATCH 2/2] net: Add fsl_enetc network driver support
Ahmad Fatoum
a.fatoum at pengutronix.de
Wed Jan 3 07:16:01 PST 2024
Hello Sascha,
On 03.01.24 15:51, Sascha Hauer wrote:
> +static int enetc_ls1028a_write_hwaddr(struct eth_device *edev, const unsigned char *mac)
> +{
> + struct enetc_priv *priv = edev->priv;
> + struct pci_dev *pdev = to_pci_dev(priv->dev);
> + const int devfn_to_pf[] = {0, 1, 2, -1, -1, -1, 3};
> + int devfn = PCI_FUNC(pdev->devfn);
> + u32 lower, upper;
> + int pf;
> +
> + if (devfn >= ARRAY_SIZE(devfn_to_pf))
> + return 0;
> +
> + pf = devfn_to_pf[devfn];
> + if (pf < 0)
> + return 0;
> +
> + lower = *(const u16 *)(mac + 4);
> + upper = *(const u32 *)mac;
Please use get_unaligned accessors or similar to avoid alignment
issues.
> +
> + out_le32(LS1028A_IERB_PSIPMAR0(pf, 0), upper);
> + out_le32(LS1028A_IERB_PSIPMAR1(pf, 0), lower);
> +
> + return 0;
> +}
> +
> +static int enetc_write_hwaddr(struct eth_device *edev, const unsigned char *mac)
> +{
> + struct enetc_priv *priv = edev->priv;
> +
> + u16 lower = *(const u16 *)(mac + 4);
> + u32 upper = *(const u32 *)mac;
Same.
> + /* prepare Tx BD */
> + memset(&priv->enetc_txbd[pi], 0x0, sizeof(struct enetc_tx_bd));
> + priv->enetc_txbd[pi].addr = cpu_to_le64(dma);
> + priv->enetc_txbd[pi].buf_len = cpu_to_le16(length);
> + priv->enetc_txbd[pi].frm_len = cpu_to_le16(length);
> + priv->enetc_txbd[pi].flags = cpu_to_le16(ENETC_TXBD_FLAGS_F);
> +
> + dmb();
I am not fond of adding these memory barriers. I was under the impression that
the way we map memory means that accesses are already serialized and a compiler
barrier (or volatile accesses) would suffice here?
> +
> + /* send frame: increment producer index */
> + pi = (pi + 1) % txr->bd_count;
> + txr->next_prod_idx = pi;
> + enetc_write_reg(txr->prod_idx, pi);
> +
> + start = get_time_ns();
> +
> + while (1) {
> + if (is_timeout(start, 100 * USECOND)) {
> + ret = -ETIMEDOUT;
> + break;
> + }
> +
> + if (pi == (enetc_read_reg(txr->cons_idx) & ENETC_BDR_IDX_MASK)) {
> + ret = 0;
> + break;
> + }
> + }
> +
> + dma_unmap_single(priv->dev, dma, length, DMA_TO_DEVICE);
> +
> + return ret;
> +}
> +
> +/*
> + * Receive frame:
> + * - wait for the next BD to get ready bit set
> + * - clean up the descriptor
> + * - move on and indicate to HW that the cleaned BD is available for Rx
> + */
> +static int enetc_recv(struct eth_device *edev)
> +{
> + struct enetc_priv *priv = edev->priv;
> + struct bd_ring *rxr = &priv->rx_bdr;
> + int pi = rxr->next_prod_idx;
> + int ci = rxr->next_cons_idx;
> + u32 status;
> + void *pkg;
> + int len;
> +
> + status = le32_to_cpu(priv->enetc_rxbd[pi].r.lstatus);
> +
> + /* check if current BD is ready to be consumed */
> + if (!ENETC_RXBD_STATUS_R(status))
> + return 0;
> +
> + dmb();
> +
> + len = le16_to_cpu(priv->enetc_rxbd[pi].r.buf_len);
> +
> + dev_dbg(&edev->dev, "RxBD[%d]: len=%d err=%d pkt=0x%p\n", pi, len,
> + ENETC_RXBD_STATUS_ERRORS(status), pkg);
> +
> + dma_sync_single_for_cpu(priv->dev, priv->rx_pkg_phys[pi], PKTSIZE, DMA_FROM_DEVICE);
> + net_receive(edev, priv->rx_pkg[pi], len);
> + dma_sync_single_for_device(priv->dev, priv->rx_pkg_phys[pi], PKTSIZE, DMA_FROM_DEVICE);
> +
> + /* BD clean up and advance to next in ring */
> + memset(&priv->enetc_rxbd[pi], 0, sizeof(union enetc_rx_bd));
> + priv->enetc_rxbd[pi].w.addr = priv->rx_pkg_phys[pi];
> + rxr->next_prod_idx = (pi + 1) % rxr->bd_count;
> + ci = (ci + 1) % rxr->bd_count;
> + rxr->next_cons_idx = ci;
> + dmb();
> + /* free up the slot in the ring for HW */
> + enetc_write_reg(rxr->cons_idx, ci);
> +
> + return 0;
> +}
> +
> +static int enetc_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> +{
> + struct device *dev = &pdev->dev;
> + struct enetc_priv *priv;
> + struct eth_device *edev;
> + int ret;
> +
> + pci_enable_device(pdev);
> + pci_set_master(pdev);
> +
> + priv = xzalloc(sizeof(*priv));
> + priv->dev = dev;
> +
> + priv->enetc_txbd = dma_alloc_coherent(sizeof(struct enetc_tx_bd) * ENETC_BD_CNT, NULL);
> + priv->enetc_rxbd = dma_alloc_coherent(sizeof(union enetc_rx_bd) * ENETC_BD_CNT, NULL);
Can you store the DMA address and use that instead of assuming 1:1 mapping?
> +
> + if (!priv->enetc_txbd || !priv->enetc_rxbd) {
> + /* free should be able to handle NULL, just free all pointers */
> + free(priv->enetc_txbd);
> + free(priv->enetc_rxbd);
dma_alloc_coherent should be freed with dma_free_coherent.
> +
> + return -ENOMEM;
> + }
> +
> + /* initialize register */
> + priv->regs_base = pci_iomap(pdev, 0);
> + if (!priv->regs_base) {
> + dev_err(dev, "failed to map BAR0\n");
> + return -EINVAL;
> + }
> +
> + edev = &priv->edev;
> + dev->priv = priv;
> + edev->priv = priv;
> + edev->open = enetc_start;
> + edev->send = enetc_send;
> + edev->recv = enetc_recv;
> + edev->halt = enetc_stop;
> + edev->get_ethaddr = enetc_get_hwaddr;
> +
> + if (of_machine_is_compatible("fsl,ls1028a"))
> + edev->set_ethaddr = enetc_ls1028a_write_hwaddr;
> + else
> + edev->set_ethaddr = enetc_write_hwaddr;
> +
> + edev->parent = dev;
> +
> + priv->port_regs = priv->regs_base + ENETC_PORT_REGS_OFF;
> +
> + enetc_start_pcs(&priv->edev);
> +
> + ret = eth_register(edev);
> + if (ret)
> + return ret;
> +
> + return 0;
You could directly return eth_register(edev) here.
> +static struct pci_driver enetc_eth_driver = {
> + .name = "fsl_enetc",
> + .id_table = enetc_pci_tbl,
> + .probe = enetc_probe,
Don't we need a remove function here to quiesce the device's DMA?
> +};
> +device_pci_driver(enetc_eth_driver);
> diff --git a/drivers/net/fsl_enetc.h b/drivers/net/fsl_enetc.h
> new file mode 100644
> index 0000000000..d79b0a337c
> --- /dev/null
> +++ b/drivers/net/fsl_enetc.h
> @@ -0,0 +1,249 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * ENETC ethernet controller driver
> + * Copyright 2017-2021 NXP
> + */
> +
> +#ifndef _ENETC_H
> +#define _ENETC_H
> +
> +#include <linux/bitops.h>
Nitpick: <linux/bits.h> has narrower scope and should suffice.
> +struct enetc_mdio_priv {
> + void *regs_base;
__iomem
> + struct mii_bus bus;
> +};
> +static void enetc_mdio_wait_bsy(struct enetc_mdio_priv *priv)
> +{
> + int to = 10000;
> +
> + while ((enetc_read(priv, ENETC_MDIO_CFG) & ENETC_EMDIO_CFG_BSY) &&
> + --to)
> + cpu_relax();
> + if (!to)
> + printf("T");
This will run in pollers and should not print to stdout.
Cheers,
Ahmad
> +};
> +device_pci_driver(enetc_mdio_driver);
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
More information about the barebox
mailing list