diff options
Diffstat (limited to 'drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c')
| -rw-r--r-- | drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c | 320 |
1 files changed, 320 insertions, 0 deletions
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c new file mode 100644 index 000000000000..316f941629d4 --- /dev/null +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c @@ -0,0 +1,320 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2020 - 2025 Mucse Corporation. */ + +#include <linux/pci.h> +#include <net/rtnetlink.h> +#include <linux/etherdevice.h> + +#include "rnpgbe.h" +#include "rnpgbe_hw.h" +#include "rnpgbe_mbx_fw.h" + +static const char rnpgbe_driver_name[] = "rnpgbe"; + +/* rnpgbe_pci_tbl - PCI Device ID Table + * + * { PCI_VDEVICE(Vendor ID, Device ID), + * private_data (used for different hw chip) } + */ +static struct pci_device_id rnpgbe_pci_tbl[] = { + { PCI_VDEVICE(MUCSE, RNPGBE_DEVICE_ID_N210), board_n210 }, + { PCI_VDEVICE(MUCSE, RNPGBE_DEVICE_ID_N210L), board_n210 }, + { PCI_VDEVICE(MUCSE, RNPGBE_DEVICE_ID_N500_DUAL_PORT), board_n500 }, + { PCI_VDEVICE(MUCSE, RNPGBE_DEVICE_ID_N500_QUAD_PORT), board_n500 }, + /* required last entry */ + {0, }, +}; + +/** + * rnpgbe_open - Called when a network interface is made active + * @netdev: network interface device structure + * + * The open entry point is called when a network interface is made + * active by the system (IFF_UP). + * + * Return: 0 + **/ +static int rnpgbe_open(struct net_device *netdev) +{ + return 0; +} + +/** + * rnpgbe_close - Disables a network interface + * @netdev: network interface device structure + * + * The close entry point is called when an interface is de-activated + * by the OS. + * + * Return: 0, this is not allowed to fail + **/ +static int rnpgbe_close(struct net_device *netdev) +{ + return 0; +} + +/** + * rnpgbe_xmit_frame - Send a skb to driver + * @skb: skb structure to be sent + * @netdev: network interface device structure + * + * Return: NETDEV_TX_OK + **/ +static netdev_tx_t rnpgbe_xmit_frame(struct sk_buff *skb, + struct net_device *netdev) +{ + struct mucse *mucse = netdev_priv(netdev); + + dev_kfree_skb_any(skb); + mucse->stats.tx_dropped++; + + return NETDEV_TX_OK; +} + +static const struct net_device_ops rnpgbe_netdev_ops = { + .ndo_open = rnpgbe_open, + .ndo_stop = rnpgbe_close, + .ndo_start_xmit = rnpgbe_xmit_frame, +}; + +/** + * rnpgbe_add_adapter - Add netdev for this pci_dev + * @pdev: PCI device information structure + * @board_type: board type + * + * rnpgbe_add_adapter initializes a netdev for this pci_dev + * structure. Initializes Bar map, private structure, and a + * hardware reset occur. + * + * Return: 0 on success, negative errno on failure + **/ +static int rnpgbe_add_adapter(struct pci_dev *pdev, + int board_type) +{ + struct net_device *netdev; + u8 perm_addr[ETH_ALEN]; + void __iomem *hw_addr; + struct mucse *mucse; + struct mucse_hw *hw; + int err, err_notify; + + netdev = alloc_etherdev_mq(sizeof(struct mucse), RNPGBE_MAX_QUEUES); + if (!netdev) + return -ENOMEM; + + SET_NETDEV_DEV(netdev, &pdev->dev); + mucse = netdev_priv(netdev); + mucse->netdev = netdev; + mucse->pdev = pdev; + pci_set_drvdata(pdev, mucse); + + hw = &mucse->hw; + hw_addr = devm_ioremap(&pdev->dev, + pci_resource_start(pdev, 2), + pci_resource_len(pdev, 2)); + if (!hw_addr) { + err = -EIO; + goto err_free_net; + } + + hw->hw_addr = hw_addr; + hw->pdev = pdev; + + err = rnpgbe_init_hw(hw, board_type); + if (err) { + dev_err(&pdev->dev, "Init hw err %d\n", err); + goto err_free_net; + } + /* Step 1: Send power-up notification to firmware (no response expected) + * This informs firmware to initialize hardware power state, but + * firmware only acknowledges receipt without returning data. Must be + * done before synchronization as firmware may be in low-power idle + * state initially. + */ + err_notify = rnpgbe_send_notify(hw, true, mucse_fw_powerup); + if (err_notify) { + dev_warn(&pdev->dev, "Send powerup to hw failed %d\n", + err_notify); + dev_warn(&pdev->dev, "Maybe low performance\n"); + } + /* Step 2: Synchronize mailbox communication with firmware (requires + * response) After power-up, confirm firmware is ready to process + * requests with responses. This ensures subsequent request/response + * interactions work reliably. + */ + err = mucse_mbx_sync_fw(hw); + if (err) { + dev_err(&pdev->dev, "Sync fw failed! %d\n", err); + goto err_powerdown; + } + + netdev->netdev_ops = &rnpgbe_netdev_ops; + err = rnpgbe_reset_hw(hw); + if (err) { + dev_err(&pdev->dev, "Hw reset failed %d\n", err); + goto err_powerdown; + } + + err = rnpgbe_get_permanent_mac(hw, perm_addr); + if (!err) { + eth_hw_addr_set(netdev, perm_addr); + } else if (err == -EINVAL) { + dev_warn(&pdev->dev, "Using random MAC\n"); + eth_hw_addr_random(netdev); + } else if (err) { + dev_err(&pdev->dev, "get perm_addr failed %d\n", err); + goto err_powerdown; + } + + err = register_netdev(netdev); + if (err) + goto err_powerdown; + + return 0; +err_powerdown: + /* notify powerdown only powerup ok */ + if (!err_notify) { + err_notify = rnpgbe_send_notify(hw, false, mucse_fw_powerup); + if (err_notify) + dev_warn(&pdev->dev, "Send powerdown to hw failed %d\n", + err_notify); + } +err_free_net: + free_netdev(netdev); + return err; +} + +/** + * rnpgbe_probe - Device initialization routine + * @pdev: PCI device information struct + * @id: entry in rnpgbe_pci_tbl + * + * rnpgbe_probe initializes a PF adapter identified by a pci_dev + * structure. + * + * Return: 0 on success, negative errno on failure + **/ +static int rnpgbe_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + int board_type = id->driver_data; + int err; + + err = pci_enable_device_mem(pdev); + if (err) + return err; + + err = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(56)); + if (err) { + dev_err(&pdev->dev, + "No usable DMA configuration, aborting %d\n", err); + goto err_disable_dev; + } + + err = pci_request_mem_regions(pdev, rnpgbe_driver_name); + if (err) { + dev_err(&pdev->dev, + "pci_request_selected_regions failed %d\n", err); + goto err_disable_dev; + } + + pci_set_master(pdev); + err = pci_save_state(pdev); + if (err) { + dev_err(&pdev->dev, "pci_save_state failed %d\n", err); + goto err_free_regions; + } + + err = rnpgbe_add_adapter(pdev, board_type); + if (err) + goto err_free_regions; + + return 0; +err_free_regions: + pci_release_mem_regions(pdev); +err_disable_dev: + pci_disable_device(pdev); + return err; +} + +/** + * rnpgbe_rm_adapter - Remove netdev for this mucse structure + * @pdev: PCI device information struct + * + * rnpgbe_rm_adapter remove a netdev for this mucse structure + **/ +static void rnpgbe_rm_adapter(struct pci_dev *pdev) +{ + struct mucse *mucse = pci_get_drvdata(pdev); + struct mucse_hw *hw = &mucse->hw; + struct net_device *netdev; + int err; + + if (!mucse) + return; + netdev = mucse->netdev; + unregister_netdev(netdev); + err = rnpgbe_send_notify(hw, false, mucse_fw_powerup); + if (err) + dev_warn(&pdev->dev, "Send powerdown to hw failed %d\n", err); + free_netdev(netdev); +} + +/** + * rnpgbe_remove - Device removal routine + * @pdev: PCI device information struct + * + * rnpgbe_remove is called by the PCI subsystem to alert the driver + * that it should release a PCI device. This could be caused by a + * Hot-Plug event, or because the driver is going to be removed from + * memory. + **/ +static void rnpgbe_remove(struct pci_dev *pdev) +{ + rnpgbe_rm_adapter(pdev); + pci_release_mem_regions(pdev); + pci_disable_device(pdev); +} + +/** + * rnpgbe_dev_shutdown - Device shutdown routine + * @pdev: PCI device information struct + **/ +static void rnpgbe_dev_shutdown(struct pci_dev *pdev) +{ + struct mucse *mucse = pci_get_drvdata(pdev); + struct net_device *netdev = mucse->netdev; + + rtnl_lock(); + netif_device_detach(netdev); + if (netif_running(netdev)) + rnpgbe_close(netdev); + rtnl_unlock(); + pci_disable_device(pdev); +} + +/** + * rnpgbe_shutdown - Device shutdown routine + * @pdev: PCI device information struct + * + * rnpgbe_shutdown is called by the PCI subsystem to alert the driver + * that os shutdown. Device should setup wakeup state here. + **/ +static void rnpgbe_shutdown(struct pci_dev *pdev) +{ + rnpgbe_dev_shutdown(pdev); +} + +static struct pci_driver rnpgbe_driver = { + .name = rnpgbe_driver_name, + .id_table = rnpgbe_pci_tbl, + .probe = rnpgbe_probe, + .remove = rnpgbe_remove, + .shutdown = rnpgbe_shutdown, +}; + +module_pci_driver(rnpgbe_driver); + +MODULE_DEVICE_TABLE(pci, rnpgbe_pci_tbl); +MODULE_AUTHOR("Yibo Dong, <dong100@mucse.com>"); +MODULE_DESCRIPTION("Mucse(R) 1 Gigabit PCI Express Network Driver"); +MODULE_LICENSE("GPL"); |
