mdio-thunder.c (3483B)
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 17struct thunder_mdiobus_nexus { 18 void __iomem *bar0; 19 struct cavium_mdiobus *buses[4]; 20}; 21 22static 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; 97 bus->mii_bus->write = cavium_mdiobus_write; 98 99 err = of_mdiobus_register(bus->mii_bus, node); 100 if (err) 101 dev_err(&pdev->dev, "of_mdiobus_register failed\n"); 102 103 dev_info(&pdev->dev, "Added bus at %llx\n", r.start); 104 if (i >= ARRAY_SIZE(nexus->buses)) 105 break; 106 } 107 return 0; 108 109err_release_regions: 110 pci_release_regions(pdev); 111 112err_disable_device: 113 pci_set_drvdata(pdev, NULL); 114 return err; 115} 116 117static void thunder_mdiobus_pci_remove(struct pci_dev *pdev) 118{ 119 int i; 120 struct thunder_mdiobus_nexus *nexus = pci_get_drvdata(pdev); 121 122 for (i = 0; i < ARRAY_SIZE(nexus->buses); i++) { 123 struct cavium_mdiobus *bus = nexus->buses[i]; 124 125 if (!bus) 126 continue; 127 128 mdiobus_unregister(bus->mii_bus); 129 oct_mdio_writeq(0, bus->register_base + SMI_EN); 130 } 131 pci_release_regions(pdev); 132 pci_set_drvdata(pdev, NULL); 133} 134 135static const struct pci_device_id thunder_mdiobus_id_table[] = { 136 { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xa02b) }, 137 { 0, } /* End of table. */ 138}; 139MODULE_DEVICE_TABLE(pci, thunder_mdiobus_id_table); 140 141static struct pci_driver thunder_mdiobus_driver = { 142 .name = KBUILD_MODNAME, 143 .id_table = thunder_mdiobus_id_table, 144 .probe = thunder_mdiobus_pci_probe, 145 .remove = thunder_mdiobus_pci_remove, 146}; 147 148module_pci_driver(thunder_mdiobus_driver); 149 150MODULE_DESCRIPTION("Cavium ThunderX MDIO bus driver"); 151MODULE_LICENSE("GPL v2");