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 12struct stmmac_pci_info { 13 int (*setup)(struct pci_dev *pdev, struct plat_stmmacenet_data *plat); 14}; 15 16static void common_default_data(struct pci_dev *pdev, 17 struct plat_stmmacenet_data *plat) 18{ 19 plat->bus_id = (pci_domain_nr(pdev->bus) << 16) | PCI_DEVID(pdev->bus->number, pdev->devfn); 20 plat->interface = PHY_INTERFACE_MODE_GMII; 21 22 plat->clk_csr = 2; /* clk_csr_i = 20-35MHz & MDC = clk_csr_i/16 */ 23 plat->has_gmac = 1; 24 plat->force_sf_dma_mode = 1; 25 26 /* Set default value for multicast hash bins */ 27 plat->multicast_filter_bins = 256; 28 29 /* Set default value for unicast filter entries */ 30 plat->unicast_filter_entries = 1; 31 32 /* Set the maxmtu to a default of JUMBO_LEN */ 33 plat->maxmtu = JUMBO_LEN; 34 35 /* Set default number of RX and TX queues to use */ 36 plat->tx_queues_to_use = 1; 37 plat->rx_queues_to_use = 1; 38 39 /* Disable Priority config by default */ 40 plat->tx_queues_cfg[0].use_prio = false; 41 plat->rx_queues_cfg[0].use_prio = false; 42 43 /* Disable RX queues routing by default */ 44 plat->rx_queues_cfg[0].pkt_route = 0x0; 45 46 plat->dma_cfg->pbl = 32; 47 plat->dma_cfg->pblx8 = true; 48 49 plat->clk_ref_rate = 125000000; 50 plat->clk_ptp_rate = 125000000; 51} 52 53static int loongson_gmac_data(struct pci_dev *pdev, 54 struct plat_stmmacenet_data *plat) 55{ 56 common_default_data(pdev, plat); 57 58 plat->mdio_bus_data->phy_mask = 0; 59 60 plat->phy_addr = -1; 61 plat->phy_interface = PHY_INTERFACE_MODE_RGMII_ID; 62 63 return 0; 64} 65 66static struct stmmac_pci_info loongson_gmac_pci_info = { 67 .setup = loongson_gmac_data, 68}; 69 70static void loongson_gnet_fix_speed(void *priv, unsigned int speed) 71{ 72 struct net_device *ndev = (struct net_device *)(*(unsigned long *)priv); 73 struct stmmac_priv *ptr = netdev_priv(ndev); 74 75 if (speed == SPEED_1000) { 76 if (readl(ptr->ioaddr + MAC_CTRL_REG) & (1 << 15) /* PS */) { 77 /* reset phy */ 78 phy_set_bits(ndev->phydev, 0 /*MII_BMCR*/, 79 0x200 /*BMCR_ANRESTART*/); 80 } 81 } 82} 83 84static int loongson_gnet_data(struct pci_dev *pdev, 85 struct plat_stmmacenet_data *plat) 86{ 87 common_default_data(pdev, plat); 88 89 plat->mdio_bus_data->phy_mask = 0xfffffffb; 90 91 plat->phy_addr = 2; 92 plat->phy_interface = PHY_INTERFACE_MODE_GMII; 93 94 /* GNET 1000M speed need workaround */ 95 plat->fix_mac_speed = loongson_gnet_fix_speed; 96 97 /* Get netdev pointer address */ 98 plat->bsp_priv = &(pdev->dev.driver_data); 99 100 return 0; 101} 102 103static struct stmmac_pci_info loongson_gnet_pci_info = { 104 .setup = loongson_gnet_data, 105}; 106 107static int loongson_dwmac_probe(struct pci_dev *pdev, 108 const struct pci_device_id *id) 109{ 110 struct plat_stmmacenet_data *plat; 111 struct stmmac_pci_info *info; 112 struct stmmac_resources res; 113 struct device_node *np; 114 int ret, i, bus_id, phy_mode; 115 bool mdio = false; 116 117 np = dev_of_node(&pdev->dev); 118 if (np && !of_device_is_compatible(np, "loongson, pci-gmac")) { 119 pr_info("dwmac_loongson_pci: Incompatible OF node\n"); 120 return -ENODEV; 121 } 122 123 plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL); 124 if (!plat) 125 return -ENOMEM; 126 127 if (plat->mdio_node) { 128 dev_err(&pdev->dev, "Found MDIO subnode\n"); 129 mdio = true; 130 } 131 132 plat->mdio_bus_data = devm_kzalloc(&pdev->dev, 133 sizeof(*plat->mdio_bus_data), 134 GFP_KERNEL); 135 if (!plat->mdio_bus_data) 136 return -ENOMEM; 137 138 if (mdio) 139 plat->mdio_bus_data->needs_reset = true; 140 141 plat->dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*plat->dma_cfg), GFP_KERNEL); 142 if (!plat->dma_cfg) 143 return -ENOMEM; 144 145 /* Enable pci device */ 146 ret = pci_enable_device(pdev); 147 if (ret) { 148 dev_err(&pdev->dev, "%s: ERROR: failed to enable device\n", __func__); 149 return ret; 150 } 151 152 /* Get the base address of device */ 153 for (i = 0; i < PCI_STD_NUM_BARS; i++) { 154 if (pci_resource_len(pdev, i) == 0) 155 continue; 156 ret = pcim_iomap_regions(pdev, BIT(0), pci_name(pdev)); 157 if (ret) 158 return ret; 159 break; 160 } 161 162 pci_set_master(pdev); 163 164 info = (struct stmmac_pci_info *)id->driver_data; 165 ret = info->setup(pdev, plat); 166 if (ret) 167 return ret; 168 169 if (np) { 170 bus_id = of_alias_get_id(np, "ethernet"); 171 if (bus_id >= 0) 172 plat->bus_id = bus_id; 173 174 phy_mode = device_get_phy_mode(&pdev->dev); 175 if (phy_mode < 0) { 176 dev_err(&pdev->dev, "phy_mode not found\n"); 177 return phy_mode; 178 } 179 plat->phy_interface = phy_mode; 180 } 181 182 pci_enable_msi(pdev); 183 184 memset(&res, 0, sizeof(res)); 185 res.addr = pcim_iomap_table(pdev)[0]; 186 if (np) { 187 res.irq = of_irq_get_byname(np, "macirq"); 188 if (res.irq < 0) { 189 dev_err(&pdev->dev, "IRQ macirq not found\n"); 190 ret = -ENODEV; 191 } 192 193 res.wol_irq = of_irq_get_byname(np, "eth_wake_irq"); 194 if (res.wol_irq < 0) { 195 dev_info(&pdev->dev, 196 "IRQ eth_wake_irq not found, using macirq\n"); 197 res.wol_irq = res.irq; 198 } 199 200 res.lpi_irq = of_irq_get_byname(np, "eth_lpi"); 201 if (res.lpi_irq < 0) { 202 dev_err(&pdev->dev, "IRQ eth_lpi not found\n"); 203 ret = -ENODEV; 204 } 205 } else { 206 res.irq = pdev->irq; 207 res.wol_irq = pdev->irq; 208 } 209 210 return stmmac_dvr_probe(&pdev->dev, plat, &res); 211} 212 213static void loongson_dwmac_remove(struct pci_dev *pdev) 214{ 215 int i; 216 217 stmmac_dvr_remove(&pdev->dev); 218 219 for (i = 0; i < PCI_STD_NUM_BARS; i++) { 220 if (pci_resource_len(pdev, i) == 0) 221 continue; 222 pcim_iounmap_regions(pdev, BIT(i)); 223 break; 224 } 225 226 pci_disable_device(pdev); 227} 228 229static int __maybe_unused loongson_dwmac_suspend(struct device *dev) 230{ 231 struct pci_dev *pdev = to_pci_dev(dev); 232 int ret; 233 234 ret = stmmac_suspend(dev); 235 if (ret) 236 return ret; 237 238 ret = pci_save_state(pdev); 239 if (ret) 240 return ret; 241 242 pci_disable_device(pdev); 243 pci_wake_from_d3(pdev, true); 244 return 0; 245} 246 247static int __maybe_unused loongson_dwmac_resume(struct device *dev) 248{ 249 struct pci_dev *pdev = to_pci_dev(dev); 250 int ret; 251 252 pci_restore_state(pdev); 253 pci_set_power_state(pdev, PCI_D0); 254 255 ret = pci_enable_device(pdev); 256 if (ret) 257 return ret; 258 259 pci_set_master(pdev); 260 261 return stmmac_resume(dev); 262} 263 264static SIMPLE_DEV_PM_OPS(loongson_dwmac_pm_ops, loongson_dwmac_suspend, 265 loongson_dwmac_resume); 266 267#define PCI_DEVICE_ID_LOONGSON_GMAC 0x7a03 268#define PCI_DEVICE_ID_LOONGSON_GNET 0x7a13 269 270static const struct pci_device_id loongson_dwmac_id_table[] = { 271 { PCI_DEVICE_DATA(LOONGSON, GMAC, &loongson_gmac_pci_info) }, 272 { PCI_DEVICE_DATA(LOONGSON, GNET, &loongson_gnet_pci_info) }, 273 {} 274}; 275MODULE_DEVICE_TABLE(pci, loongson_dwmac_id_table); 276 277struct pci_driver loongson_dwmac_driver = { 278 .name = "dwmac-loongson-pci", 279 .id_table = loongson_dwmac_id_table, 280 .probe = loongson_dwmac_probe, 281 .remove = loongson_dwmac_remove, 282 .driver = { 283 .pm = &loongson_dwmac_pm_ops, 284 }, 285}; 286 287module_pci_driver(loongson_dwmac_driver); 288 289MODULE_DESCRIPTION("Loongson DWMAC PCI driver"); 290MODULE_AUTHOR("Qing Zhang <zhangqing@loongson.cn>"); 291MODULE_LICENSE("GPL v2"); 292