1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2009-2016 Cavium, Inc. 4 */ 5 6 #include <linux/acpi.h> 7 #include <linux/gfp.h> 8 #include <linux/io.h> 9 #include <linux/module.h> 10 #include <linux/of_address.h> 11 #include <linux/of_mdio.h> 12 #include <linux/pci.h> 13 #include <linux/phy.h> 14 15 #include "mdio-cavium.h" 16 17 struct thunder_mdiobus_nexus { 18 void __iomem *bar0; 19 struct cavium_mdiobus *buses[4]; 20 }; 21 22 static int thunder_mdiobus_pci_probe(struct pci_dev *pdev, 23 const struct pci_device_id *ent) 24 { 25 struct device_node *node; 26 struct thunder_mdiobus_nexus *nexus; 27 int err; 28 int i; 29 30 nexus = devm_kzalloc(&pdev->dev, sizeof(*nexus), GFP_KERNEL); 31 if (!nexus) 32 return -ENOMEM; 33 34 pci_set_drvdata(pdev, nexus); 35 36 err = pcim_enable_device(pdev); 37 if (err) { 38 dev_err(&pdev->dev, "Failed to enable PCI device\n"); 39 pci_set_drvdata(pdev, NULL); 40 return err; 41 } 42 43 err = pci_request_regions(pdev, KBUILD_MODNAME); 44 if (err) { 45 dev_err(&pdev->dev, "pci_request_regions failed\n"); 46 goto err_disable_device; 47 } 48 49 nexus->bar0 = pcim_iomap(pdev, 0, pci_resource_len(pdev, 0)); 50 if (!nexus->bar0) { 51 err = -ENOMEM; 52 goto err_release_regions; 53 } 54 55 i = 0; 56 device_for_each_child_node_scoped(&pdev->dev, fwn) { 57 struct resource r; 58 struct mii_bus *mii_bus; 59 struct cavium_mdiobus *bus; 60 union cvmx_smix_en smi_en; 61 62 /* If it is not an OF node we cannot handle it yet, so 63 * exit the loop. 64 */ 65 node = to_of_node(fwn); 66 if (!node) 67 break; 68 69 err = of_address_to_resource(node, 0, &r); 70 if (err) { 71 dev_err(&pdev->dev, 72 "Couldn't translate address for \"%pOFn\"\n", 73 node); 74 break; 75 } 76 77 mii_bus = devm_mdiobus_alloc_size(&pdev->dev, sizeof(*bus)); 78 if (!mii_bus) 79 break; 80 bus = mii_bus->priv; 81 bus->mii_bus = mii_bus; 82 83 nexus->buses[i] = bus; 84 i++; 85 86 bus->register_base = nexus->bar0 + 87 r.start - pci_resource_start(pdev, 0); 88 89 smi_en.u64 = 0; 90 smi_en.s.en = 1; 91 oct_mdio_writeq(smi_en.u64, bus->register_base + SMI_EN); 92 bus->mii_bus->name = KBUILD_MODNAME; 93 snprintf(bus->mii_bus->id, MII_BUS_ID_SIZE, "%llx", r.start); 94 bus->mii_bus->parent = &pdev->dev; 95 bus->mii_bus->read = cavium_mdiobus_read_c22; 96 bus->mii_bus->write = cavium_mdiobus_write_c22; 97 bus->mii_bus->read_c45 = cavium_mdiobus_read_c45; 98 bus->mii_bus->write_c45 = cavium_mdiobus_write_c45; 99 100 err = of_mdiobus_register(bus->mii_bus, node); 101 if (err) 102 dev_err(&pdev->dev, "of_mdiobus_register failed\n"); 103 104 dev_info(&pdev->dev, "Added bus at %llx\n", r.start); 105 if (i >= ARRAY_SIZE(nexus->buses)) 106 break; 107 } 108 return 0; 109 110 err_release_regions: 111 pci_release_regions(pdev); 112 113 err_disable_device: 114 pci_set_drvdata(pdev, NULL); 115 return err; 116 } 117 118 static void thunder_mdiobus_pci_remove(struct pci_dev *pdev) 119 { 120 int i; 121 struct thunder_mdiobus_nexus *nexus = pci_get_drvdata(pdev); 122 123 for (i = 0; i < ARRAY_SIZE(nexus->buses); i++) { 124 struct cavium_mdiobus *bus = nexus->buses[i]; 125 126 if (!bus) 127 continue; 128 129 mdiobus_unregister(bus->mii_bus); 130 oct_mdio_writeq(0, bus->register_base + SMI_EN); 131 } 132 pci_release_regions(pdev); 133 pci_set_drvdata(pdev, NULL); 134 } 135 136 static const struct pci_device_id thunder_mdiobus_id_table[] = { 137 { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xa02b) }, 138 { 0, } /* End of table. */ 139 }; 140 MODULE_DEVICE_TABLE(pci, thunder_mdiobus_id_table); 141 142 static struct pci_driver thunder_mdiobus_driver = { 143 .name = KBUILD_MODNAME, 144 .id_table = thunder_mdiobus_id_table, 145 .probe = thunder_mdiobus_pci_probe, 146 .remove = thunder_mdiobus_pci_remove, 147 }; 148 149 module_pci_driver(thunder_mdiobus_driver); 150 151 MODULE_DESCRIPTION("Cavium ThunderX MDIO bus driver"); 152 MODULE_LICENSE("GPL v2"); 153