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