0001
0002
0003
0004
0005 #include <linux/clk-provider.h>
0006 #include <linux/pci.h>
0007 #include <linux/dmi.h>
0008 #include <linux/device.h>
0009 #include <linux/of_irq.h>
0010 #include "stmmac.h"
0011
0012 static int loongson_default_data(struct plat_stmmacenet_data *plat)
0013 {
0014 plat->clk_csr = 2;
0015 plat->has_gmac = 1;
0016 plat->force_sf_dma_mode = 1;
0017
0018
0019 plat->multicast_filter_bins = HASH_TABLE_SIZE;
0020
0021
0022 plat->unicast_filter_entries = 1;
0023
0024
0025 plat->maxmtu = JUMBO_LEN;
0026
0027
0028 plat->tx_queues_to_use = 1;
0029 plat->rx_queues_to_use = 1;
0030
0031
0032 plat->tx_queues_cfg[0].use_prio = false;
0033 plat->rx_queues_cfg[0].use_prio = false;
0034
0035
0036 plat->rx_queues_cfg[0].pkt_route = 0x0;
0037
0038
0039 plat->phy_addr = -1;
0040
0041 plat->dma_cfg->pbl = 32;
0042 plat->dma_cfg->pblx8 = true;
0043
0044 plat->multicast_filter_bins = 256;
0045 return 0;
0046 }
0047
0048 static int loongson_dwmac_probe(struct pci_dev *pdev, const struct pci_device_id *id)
0049 {
0050 struct plat_stmmacenet_data *plat;
0051 struct stmmac_resources res;
0052 struct device_node *np;
0053 int ret, i, phy_mode;
0054 bool mdio = false;
0055
0056 np = dev_of_node(&pdev->dev);
0057
0058 if (!np) {
0059 pr_info("dwmac_loongson_pci: No OF node\n");
0060 return -ENODEV;
0061 }
0062
0063 if (!of_device_is_compatible(np, "loongson, pci-gmac")) {
0064 pr_info("dwmac_loongson_pci: Incompatible OF node\n");
0065 return -ENODEV;
0066 }
0067
0068 plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL);
0069 if (!plat)
0070 return -ENOMEM;
0071
0072 if (plat->mdio_node) {
0073 dev_err(&pdev->dev, "Found MDIO subnode\n");
0074 mdio = true;
0075 }
0076
0077 if (mdio) {
0078 plat->mdio_bus_data = devm_kzalloc(&pdev->dev,
0079 sizeof(*plat->mdio_bus_data),
0080 GFP_KERNEL);
0081 if (!plat->mdio_bus_data)
0082 return -ENOMEM;
0083 plat->mdio_bus_data->needs_reset = true;
0084 }
0085
0086 plat->dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*plat->dma_cfg), GFP_KERNEL);
0087 if (!plat->dma_cfg)
0088 return -ENOMEM;
0089
0090
0091 ret = pci_enable_device(pdev);
0092 if (ret) {
0093 dev_err(&pdev->dev, "%s: ERROR: failed to enable device\n", __func__);
0094 return ret;
0095 }
0096
0097
0098 for (i = 0; i < PCI_STD_NUM_BARS; i++) {
0099 if (pci_resource_len(pdev, i) == 0)
0100 continue;
0101 ret = pcim_iomap_regions(pdev, BIT(0), pci_name(pdev));
0102 if (ret)
0103 return ret;
0104 break;
0105 }
0106
0107 plat->bus_id = of_alias_get_id(np, "ethernet");
0108 if (plat->bus_id < 0)
0109 plat->bus_id = pci_dev_id(pdev);
0110
0111 phy_mode = device_get_phy_mode(&pdev->dev);
0112 if (phy_mode < 0) {
0113 dev_err(&pdev->dev, "phy_mode not found\n");
0114 return phy_mode;
0115 }
0116
0117 plat->phy_interface = phy_mode;
0118 plat->interface = PHY_INTERFACE_MODE_GMII;
0119
0120 pci_set_master(pdev);
0121
0122 loongson_default_data(plat);
0123 pci_enable_msi(pdev);
0124 memset(&res, 0, sizeof(res));
0125 res.addr = pcim_iomap_table(pdev)[0];
0126
0127 res.irq = of_irq_get_byname(np, "macirq");
0128 if (res.irq < 0) {
0129 dev_err(&pdev->dev, "IRQ macirq not found\n");
0130 ret = -ENODEV;
0131 }
0132
0133 res.wol_irq = of_irq_get_byname(np, "eth_wake_irq");
0134 if (res.wol_irq < 0) {
0135 dev_info(&pdev->dev, "IRQ eth_wake_irq not found, using macirq\n");
0136 res.wol_irq = res.irq;
0137 }
0138
0139 res.lpi_irq = of_irq_get_byname(np, "eth_lpi");
0140 if (res.lpi_irq < 0) {
0141 dev_err(&pdev->dev, "IRQ eth_lpi not found\n");
0142 ret = -ENODEV;
0143 }
0144
0145 return stmmac_dvr_probe(&pdev->dev, plat, &res);
0146 }
0147
0148 static void loongson_dwmac_remove(struct pci_dev *pdev)
0149 {
0150 int i;
0151
0152 stmmac_dvr_remove(&pdev->dev);
0153
0154 for (i = 0; i < PCI_STD_NUM_BARS; i++) {
0155 if (pci_resource_len(pdev, i) == 0)
0156 continue;
0157 pcim_iounmap_regions(pdev, BIT(i));
0158 break;
0159 }
0160
0161 pci_disable_device(pdev);
0162 }
0163
0164 static int __maybe_unused loongson_dwmac_suspend(struct device *dev)
0165 {
0166 struct pci_dev *pdev = to_pci_dev(dev);
0167 int ret;
0168
0169 ret = stmmac_suspend(dev);
0170 if (ret)
0171 return ret;
0172
0173 ret = pci_save_state(pdev);
0174 if (ret)
0175 return ret;
0176
0177 pci_disable_device(pdev);
0178 pci_wake_from_d3(pdev, true);
0179 return 0;
0180 }
0181
0182 static int __maybe_unused loongson_dwmac_resume(struct device *dev)
0183 {
0184 struct pci_dev *pdev = to_pci_dev(dev);
0185 int ret;
0186
0187 pci_restore_state(pdev);
0188 pci_set_power_state(pdev, PCI_D0);
0189
0190 ret = pci_enable_device(pdev);
0191 if (ret)
0192 return ret;
0193
0194 pci_set_master(pdev);
0195
0196 return stmmac_resume(dev);
0197 }
0198
0199 static SIMPLE_DEV_PM_OPS(loongson_dwmac_pm_ops, loongson_dwmac_suspend,
0200 loongson_dwmac_resume);
0201
0202 static const struct pci_device_id loongson_dwmac_id_table[] = {
0203 { PCI_VDEVICE(LOONGSON, 0x7a03) },
0204 {}
0205 };
0206 MODULE_DEVICE_TABLE(pci, loongson_dwmac_id_table);
0207
0208 static struct pci_driver loongson_dwmac_driver = {
0209 .name = "dwmac-loongson-pci",
0210 .id_table = loongson_dwmac_id_table,
0211 .probe = loongson_dwmac_probe,
0212 .remove = loongson_dwmac_remove,
0213 .driver = {
0214 .pm = &loongson_dwmac_pm_ops,
0215 },
0216 };
0217
0218 module_pci_driver(loongson_dwmac_driver);
0219
0220 MODULE_DESCRIPTION("Loongson DWMAC PCI driver");
0221 MODULE_AUTHOR("Qing Zhang <zhangqing@loongson.cn>");
0222 MODULE_LICENSE("GPL v2");