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