dwmac-loongson.c (5081B)
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 208static struct 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");