linux/drivers/net/ethernet/stmicro/stmmac/dwmac-loongson.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/* Copyright (c) 2020, Loongson Corporation
   3 */
   4
   5#include <linux/clk-provider.h>
   6#include <linux/pci.h>
   7#include <linux/dmi.h>
   8#include <linux/device.h>
   9#include <linux/of_irq.h>
  10#include "stmmac.h"
  11
  12static int loongson_default_data(struct plat_stmmacenet_data *plat)
  13{
  14        plat->clk_csr = 2;      /* clk_csr_i = 20-35MHz & MDC = clk_csr_i/16 */
  15        plat->has_gmac = 1;
  16        plat->force_sf_dma_mode = 1;
  17
  18        /* Set default value for multicast hash bins */
  19        plat->multicast_filter_bins = HASH_TABLE_SIZE;
  20
  21        /* Set default value for unicast filter entries */
  22        plat->unicast_filter_entries = 1;
  23
  24        /* Set the maxmtu to a default of JUMBO_LEN */
  25        plat->maxmtu = JUMBO_LEN;
  26
  27        /* Set default number of RX and TX queues to use */
  28        plat->tx_queues_to_use = 1;
  29        plat->rx_queues_to_use = 1;
  30
  31        /* Disable Priority config by default */
  32        plat->tx_queues_cfg[0].use_prio = false;
  33        plat->rx_queues_cfg[0].use_prio = false;
  34
  35        /* Disable RX queues routing by default */
  36        plat->rx_queues_cfg[0].pkt_route = 0x0;
  37
  38        /* Default to phy auto-detection */
  39        plat->phy_addr = -1;
  40
  41        plat->dma_cfg->pbl = 32;
  42        plat->dma_cfg->pblx8 = true;
  43
  44        plat->multicast_filter_bins = 256;
  45        return 0;
  46}
  47
  48static int loongson_dwmac_probe(struct pci_dev *pdev, const struct pci_device_id *id)
  49{
  50        struct plat_stmmacenet_data *plat;
  51        struct stmmac_resources res;
  52        struct device_node *np;
  53        int ret, i, phy_mode;
  54        bool mdio = false;
  55
  56        np = dev_of_node(&pdev->dev);
  57
  58        if (!np) {
  59                pr_info("dwmac_loongson_pci: No OF node\n");
  60                return -ENODEV;
  61        }
  62
  63        if (!of_device_is_compatible(np, "loongson, pci-gmac")) {
  64                pr_info("dwmac_loongson_pci: Incompatible OF node\n");
  65                return -ENODEV;
  66        }
  67
  68        plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL);
  69        if (!plat)
  70                return -ENOMEM;
  71
  72        if (plat->mdio_node) {
  73                dev_err(&pdev->dev, "Found MDIO subnode\n");
  74                mdio = true;
  75        }
  76
  77        if (mdio) {
  78                plat->mdio_bus_data = devm_kzalloc(&pdev->dev,
  79                                                   sizeof(*plat->mdio_bus_data),
  80                                                   GFP_KERNEL);
  81                if (!plat->mdio_bus_data)
  82                        return -ENOMEM;
  83                plat->mdio_bus_data->needs_reset = true;
  84        }
  85
  86        plat->dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*plat->dma_cfg), GFP_KERNEL);
  87        if (!plat->dma_cfg)
  88                return -ENOMEM;
  89
  90        /* Enable pci device */
  91        ret = pci_enable_device(pdev);
  92        if (ret) {
  93                dev_err(&pdev->dev, "%s: ERROR: failed to enable device\n", __func__);
  94                return ret;
  95        }
  96
  97        /* Get the base address of device */
  98        for (i = 0; i < PCI_STD_NUM_BARS; i++) {
  99                if (pci_resource_len(pdev, i) == 0)
 100                        continue;
 101                ret = pcim_iomap_regions(pdev, BIT(0), pci_name(pdev));
 102                if (ret)
 103                        return ret;
 104                break;
 105        }
 106
 107        plat->bus_id = of_alias_get_id(np, "ethernet");
 108        if (plat->bus_id < 0)
 109                plat->bus_id = pci_dev_id(pdev);
 110
 111        phy_mode = device_get_phy_mode(&pdev->dev);
 112        if (phy_mode < 0) {
 113                dev_err(&pdev->dev, "phy_mode not found\n");
 114                return phy_mode;
 115        }
 116
 117        plat->phy_interface = phy_mode;
 118        plat->interface = PHY_INTERFACE_MODE_GMII;
 119
 120        pci_set_master(pdev);
 121
 122        loongson_default_data(plat);
 123        pci_enable_msi(pdev);
 124        memset(&res, 0, sizeof(res));
 125        res.addr = pcim_iomap_table(pdev)[0];
 126
 127        res.irq = of_irq_get_byname(np, "macirq");
 128        if (res.irq < 0) {
 129                dev_err(&pdev->dev, "IRQ macirq not found\n");
 130                ret = -ENODEV;
 131        }
 132
 133        res.wol_irq = of_irq_get_byname(np, "eth_wake_irq");
 134        if (res.wol_irq < 0) {
 135                dev_info(&pdev->dev, "IRQ eth_wake_irq not found, using macirq\n");
 136                res.wol_irq = res.irq;
 137        }
 138
 139        res.lpi_irq = of_irq_get_byname(np, "eth_lpi");
 140        if (res.lpi_irq < 0) {
 141                dev_err(&pdev->dev, "IRQ eth_lpi not found\n");
 142                ret = -ENODEV;
 143        }
 144
 145        return stmmac_dvr_probe(&pdev->dev, plat, &res);
 146}
 147
 148static void loongson_dwmac_remove(struct pci_dev *pdev)
 149{
 150        int i;
 151
 152        stmmac_dvr_remove(&pdev->dev);
 153
 154        for (i = 0; i < PCI_STD_NUM_BARS; i++) {
 155                if (pci_resource_len(pdev, i) == 0)
 156                        continue;
 157                pcim_iounmap_regions(pdev, BIT(i));
 158                break;
 159        }
 160
 161        pci_disable_device(pdev);
 162}
 163
 164static int __maybe_unused loongson_dwmac_suspend(struct device *dev)
 165{
 166        struct pci_dev *pdev = to_pci_dev(dev);
 167        int ret;
 168
 169        ret = stmmac_suspend(dev);
 170        if (ret)
 171                return ret;
 172
 173        ret = pci_save_state(pdev);
 174        if (ret)
 175                return ret;
 176
 177        pci_disable_device(pdev);
 178        pci_wake_from_d3(pdev, true);
 179        return 0;
 180}
 181
 182static int __maybe_unused loongson_dwmac_resume(struct device *dev)
 183{
 184        struct pci_dev *pdev = to_pci_dev(dev);
 185        int ret;
 186
 187        pci_restore_state(pdev);
 188        pci_set_power_state(pdev, PCI_D0);
 189
 190        ret = pci_enable_device(pdev);
 191        if (ret)
 192                return ret;
 193
 194        pci_set_master(pdev);
 195
 196        return stmmac_resume(dev);
 197}
 198
 199static SIMPLE_DEV_PM_OPS(loongson_dwmac_pm_ops, loongson_dwmac_suspend,
 200                         loongson_dwmac_resume);
 201
 202static const struct pci_device_id loongson_dwmac_id_table[] = {
 203        { PCI_VDEVICE(LOONGSON, 0x7a03) },
 204        {}
 205};
 206MODULE_DEVICE_TABLE(pci, loongson_dwmac_id_table);
 207
 208struct pci_driver loongson_dwmac_driver = {
 209        .name = "dwmac-loongson-pci",
 210        .id_table = loongson_dwmac_id_table,
 211        .probe = loongson_dwmac_probe,
 212        .remove = loongson_dwmac_remove,
 213        .driver = {
 214                .pm = &loongson_dwmac_pm_ops,
 215        },
 216};
 217
 218module_pci_driver(loongson_dwmac_driver);
 219
 220MODULE_DESCRIPTION("Loongson DWMAC PCI driver");
 221MODULE_AUTHOR("Qing Zhang <zhangqing@loongson.cn>");
 222MODULE_LICENSE("GPL v2");
 223