xref: /openbmc/linux/drivers/net/mdio/mdio-thunder.c (revision b1de5c78)
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