0001
0002
0003
0004
0005
0006 #include <linux/acpi.h>
0007 #include <linux/gfp.h>
0008 #include <linux/io.h>
0009 #include <linux/module.h>
0010 #include <linux/of_address.h>
0011 #include <linux/of_mdio.h>
0012 #include <linux/pci.h>
0013 #include <linux/phy.h>
0014
0015 #include "mdio-cavium.h"
0016
0017 struct thunder_mdiobus_nexus {
0018 void __iomem *bar0;
0019 struct cavium_mdiobus *buses[4];
0020 };
0021
0022 static int thunder_mdiobus_pci_probe(struct pci_dev *pdev,
0023 const struct pci_device_id *ent)
0024 {
0025 struct device_node *node;
0026 struct fwnode_handle *fwn;
0027 struct thunder_mdiobus_nexus *nexus;
0028 int err;
0029 int i;
0030
0031 nexus = devm_kzalloc(&pdev->dev, sizeof(*nexus), GFP_KERNEL);
0032 if (!nexus)
0033 return -ENOMEM;
0034
0035 pci_set_drvdata(pdev, nexus);
0036
0037 err = pcim_enable_device(pdev);
0038 if (err) {
0039 dev_err(&pdev->dev, "Failed to enable PCI device\n");
0040 pci_set_drvdata(pdev, NULL);
0041 return err;
0042 }
0043
0044 err = pci_request_regions(pdev, KBUILD_MODNAME);
0045 if (err) {
0046 dev_err(&pdev->dev, "pci_request_regions failed\n");
0047 goto err_disable_device;
0048 }
0049
0050 nexus->bar0 = pcim_iomap(pdev, 0, pci_resource_len(pdev, 0));
0051 if (!nexus->bar0) {
0052 err = -ENOMEM;
0053 goto err_release_regions;
0054 }
0055
0056 i = 0;
0057 device_for_each_child_node(&pdev->dev, fwn) {
0058 struct resource r;
0059 struct mii_bus *mii_bus;
0060 struct cavium_mdiobus *bus;
0061 union cvmx_smix_en smi_en;
0062
0063
0064
0065
0066 node = to_of_node(fwn);
0067 if (!node)
0068 break;
0069
0070 err = of_address_to_resource(node, 0, &r);
0071 if (err) {
0072 dev_err(&pdev->dev,
0073 "Couldn't translate address for \"%pOFn\"\n",
0074 node);
0075 break;
0076 }
0077
0078 mii_bus = devm_mdiobus_alloc_size(&pdev->dev, sizeof(*bus));
0079 if (!mii_bus)
0080 break;
0081 bus = mii_bus->priv;
0082 bus->mii_bus = mii_bus;
0083
0084 nexus->buses[i] = bus;
0085 i++;
0086
0087 bus->register_base = nexus->bar0 +
0088 r.start - pci_resource_start(pdev, 0);
0089
0090 smi_en.u64 = 0;
0091 smi_en.s.en = 1;
0092 oct_mdio_writeq(smi_en.u64, bus->register_base + SMI_EN);
0093 bus->mii_bus->name = KBUILD_MODNAME;
0094 snprintf(bus->mii_bus->id, MII_BUS_ID_SIZE, "%llx", r.start);
0095 bus->mii_bus->parent = &pdev->dev;
0096 bus->mii_bus->read = cavium_mdiobus_read;
0097 bus->mii_bus->write = cavium_mdiobus_write;
0098
0099 err = of_mdiobus_register(bus->mii_bus, node);
0100 if (err)
0101 dev_err(&pdev->dev, "of_mdiobus_register failed\n");
0102
0103 dev_info(&pdev->dev, "Added bus at %llx\n", r.start);
0104 if (i >= ARRAY_SIZE(nexus->buses))
0105 break;
0106 }
0107 return 0;
0108
0109 err_release_regions:
0110 pci_release_regions(pdev);
0111
0112 err_disable_device:
0113 pci_set_drvdata(pdev, NULL);
0114 return err;
0115 }
0116
0117 static void thunder_mdiobus_pci_remove(struct pci_dev *pdev)
0118 {
0119 int i;
0120 struct thunder_mdiobus_nexus *nexus = pci_get_drvdata(pdev);
0121
0122 for (i = 0; i < ARRAY_SIZE(nexus->buses); i++) {
0123 struct cavium_mdiobus *bus = nexus->buses[i];
0124
0125 if (!bus)
0126 continue;
0127
0128 mdiobus_unregister(bus->mii_bus);
0129 oct_mdio_writeq(0, bus->register_base + SMI_EN);
0130 }
0131 pci_release_regions(pdev);
0132 pci_set_drvdata(pdev, NULL);
0133 }
0134
0135 static const struct pci_device_id thunder_mdiobus_id_table[] = {
0136 { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xa02b) },
0137 { 0, }
0138 };
0139 MODULE_DEVICE_TABLE(pci, thunder_mdiobus_id_table);
0140
0141 static struct pci_driver thunder_mdiobus_driver = {
0142 .name = KBUILD_MODNAME,
0143 .id_table = thunder_mdiobus_id_table,
0144 .probe = thunder_mdiobus_pci_probe,
0145 .remove = thunder_mdiobus_pci_remove,
0146 };
0147
0148 module_pci_driver(thunder_mdiobus_driver);
0149
0150 MODULE_DESCRIPTION("Cavium ThunderX MDIO bus driver");
0151 MODULE_LICENSE("GPL v2");