1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright © 2021-2022 Dmitry Salychev 5 * Copyright © 2022 Bjoern A. Zeeb 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 /* 31 * The DPAA2 Management Complex (MC) Bus Driver (FDT-based). 32 * 33 * MC is a hardware resource manager which can be found in several NXP 34 * SoCs (LX2160A, for example) and provides an access to the specialized 35 * hardware objects used in network-oriented packet processing applications. 36 */ 37 38 #include <sys/param.h> 39 #include <sys/kernel.h> 40 #include <sys/bus.h> 41 #include <sys/rman.h> 42 #include <sys/module.h> 43 #include <sys/malloc.h> 44 #include <sys/mutex.h> 45 46 #include <machine/bus.h> 47 #include <machine/resource.h> 48 49 #include <dev/ofw/ofw_bus.h> 50 #include <dev/ofw/ofw_bus_subr.h> 51 #include <dev/fdt/simplebus.h> 52 53 #include "pcib_if.h" 54 #include "pci_if.h" 55 #include "ofw_bus_if.h" 56 57 #include "dpaa2_mcp.h" 58 #include "dpaa2_mc.h" 59 #include "dpaa2_mc_if.h" 60 61 struct dpaa2_mac_fdt_softc { 62 uint32_t reg; 63 phandle_t sfp; 64 phandle_t pcs_handle; 65 phandle_t phy_handle; 66 char managed[64]; 67 char phy_conn_type[64]; 68 }; 69 70 #if 0 71 ethernet@1 { 72 73 compatible = "fsl,qoriq-mc-dpmac"; 74 reg = <0x1>; 75 sfp = <0x14>; 76 pcs-handle = <0x15>; 77 phy-connection-type = "10gbase-r"; 78 managed = "in-band-status"; 79 }; 80 ethernet@3 { 81 82 compatible = "fsl,qoriq-mc-dpmac"; 83 reg = <0x3>; 84 phy-handle = <0x18>; 85 phy-connection-type = "qsgmii"; 86 managed = "in-band-status"; 87 pcs-handle = <0x19>; 88 }; 89 #endif 90 91 static int 92 dpaa2_mac_dev_probe(device_t dev) 93 { 94 phandle_t node; 95 uint64_t reg; 96 ssize_t s; 97 98 node = ofw_bus_get_node(dev); 99 if (!ofw_bus_node_is_compatible(node, "fsl,qoriq-mc-dpmac")) { 100 device_printf(dev, "'%s' not fsl,qoriq-mc-dpmac compatible\n", 101 ofw_bus_get_name(dev)); 102 return (ENXIO); 103 } 104 105 s = device_get_property(dev, "reg", ®, sizeof(reg), 106 DEVICE_PROP_UINT32); 107 if (s == -1) { 108 device_printf(dev, "%s: '%s' has no 'reg' property, s %zd\n", 109 __func__, ofw_bus_get_name(dev), s); 110 return (ENXIO); 111 } 112 113 device_set_desc(dev, "DPAA2 MAC DEV"); 114 return (BUS_PROBE_DEFAULT); 115 } 116 117 static int 118 dpaa2_mac_fdt_attach(device_t dev) 119 { 120 struct dpaa2_mac_fdt_softc *sc; 121 phandle_t node; 122 ssize_t s; 123 124 sc = device_get_softc(dev); 125 node = ofw_bus_get_node(dev); 126 127 s = device_get_property(dev, "reg", &sc->reg, sizeof(sc->reg), 128 DEVICE_PROP_UINT32); 129 if (s == -1) { 130 device_printf(dev, "Cannot find 'reg' property: %zd\n", s); 131 return (ENXIO); 132 } 133 134 s = device_get_property(dev, "managed", sc->managed, 135 sizeof(sc->managed), DEVICE_PROP_ANY); 136 s = device_get_property(dev, "phy-connection-type", sc->phy_conn_type, 137 sizeof(sc->phy_conn_type), DEVICE_PROP_ANY); 138 s = device_get_property(dev, "pcs-handle", &sc->pcs_handle, 139 sizeof(sc->pcs_handle), DEVICE_PROP_HANDLE); 140 141 /* 'sfp' and 'phy-handle' are optional but we need one or the other. */ 142 s = device_get_property(dev, "sfp", &sc->sfp, sizeof(sc->sfp), 143 DEVICE_PROP_HANDLE); 144 s = device_get_property(dev, "phy-handle", &sc->phy_handle, 145 sizeof(sc->phy_handle), DEVICE_PROP_HANDLE); 146 147 if (bootverbose) 148 device_printf(dev, "node %#x '%s': reg %#x sfp %#x pcs-handle " 149 "%#x phy-handle %#x managed '%s' phy-conn-type '%s'\n", 150 node, ofw_bus_get_name(dev), 151 sc->reg, sc->sfp, sc->pcs_handle, sc->phy_handle, 152 sc->managed, sc->phy_conn_type); 153 154 return (0); 155 } 156 157 static bool 158 dpaa2_mac_fdt_match_id(device_t dev, uint32_t id) 159 { 160 struct dpaa2_mac_fdt_softc *sc; 161 162 if (dev == NULL) 163 return (false); 164 165 sc = device_get_softc(dev); 166 if (sc->reg == id) 167 return (true); 168 169 return (false); 170 } 171 172 static device_t 173 dpaa2_mac_fdt_get_phy_dev(device_t dev) 174 { 175 struct dpaa2_mac_fdt_softc *sc; 176 177 if (dev == NULL) 178 return (NULL); 179 180 sc = device_get_softc(dev); 181 if (sc->phy_handle == 0 && sc->sfp == 0) 182 return (NULL); 183 184 #ifdef __not_yet__ /* No sff,sfp support yet. */ 185 if (sc->sfp != 0) { 186 device_t xdev; 187 188 xdev = OF_device_from_xref(OF_xref_from_node(sc->sfp)); 189 if (xdev != NULL) 190 return (xdev); 191 } 192 #endif 193 return (OF_device_from_xref(OF_xref_from_node(sc->phy_handle))); 194 } 195 196 static device_method_t dpaa2_mac_fdt_methods[] = { 197 /* Device interface */ 198 DEVMETHOD(device_probe, dpaa2_mac_dev_probe), 199 DEVMETHOD(device_attach, dpaa2_mac_fdt_attach), 200 DEVMETHOD(device_detach, bus_generic_detach), 201 202 DEVMETHOD_END 203 }; 204 205 DEFINE_CLASS_0(dpaa2_mac_fdt, dpaa2_mac_fdt_driver, dpaa2_mac_fdt_methods, 206 sizeof(struct dpaa2_mac_fdt_softc)); 207 DRIVER_MODULE(dpaa2_mac_fdt, dpaa2_mc, dpaa2_mac_fdt_driver, 0, 0); 208 MODULE_DEPEND(dpaa2_mac_fdt, memac_mdio_fdt, 1, 1, 1); 209 210 /* 211 * Device interface. 212 */ 213 214 static int 215 dpaa2_mc_fdt_probe(device_t dev) 216 { 217 if (!ofw_bus_status_okay(dev)) 218 return (ENXIO); 219 220 if (!ofw_bus_is_compatible(dev, "fsl,qoriq-mc")) 221 return (ENXIO); 222 223 device_set_desc(dev, "DPAA2 Management Complex"); 224 return (BUS_PROBE_DEFAULT); 225 } 226 227 static int 228 dpaa2_mc_fdt_probe_child(device_t bus, phandle_t child) 229 { 230 device_t childdev; 231 232 /* make sure we do not aliready have a device. */ 233 childdev = ofw_bus_find_child_device_by_phandle(bus, child); 234 if (childdev != NULL) 235 return (0); 236 237 childdev = simplebus_add_device(bus, child, 0, "dpaa2_mac_fdt", -1, 238 NULL); 239 if (childdev == NULL) 240 return (ENXIO); 241 242 return (device_probe_and_attach(childdev)); 243 } 244 245 static int 246 dpaa2_mc_fdt_attach(device_t dev) 247 { 248 struct dpaa2_mc_softc *sc; 249 phandle_t node; 250 phandle_t child; 251 252 sc = device_get_softc(dev); 253 sc->acpi_based = false; 254 sc->ofw_node = ofw_bus_get_node(dev); 255 256 bus_generic_probe(dev); 257 bus_enumerate_hinted_children(dev); 258 259 bus_generic_probe(dev); 260 bus_enumerate_hinted_children(dev); 261 /* 262 * Attach the children represented in the device tree. 263 */ 264 /* fsl-mc -> dpamcs */ 265 node = OF_child(sc->ofw_node); 266 simplebus_init(dev, node); 267 268 /* Attach the dpmac children represented in the device tree. */ 269 child = ofw_bus_find_compatible(node, "fsl,qoriq-mc-dpmac"); 270 for (; child > 0; child = OF_peer(child)) { 271 if (!ofw_bus_node_is_compatible(child, "fsl,qoriq-mc-dpmac")) 272 continue; 273 if (!OF_hasprop(child, "reg")) 274 continue; 275 if (dpaa2_mc_fdt_probe_child(dev, child) != 0) 276 continue; 277 } 278 279 return (dpaa2_mc_attach(dev)); 280 } 281 282 /* 283 * FDT compat layer. 284 */ 285 static device_t 286 dpaa2_mc_fdt_find_dpaa2_mac_dev(device_t dev, uint32_t id) 287 { 288 int devcount, error, i, len; 289 device_t *devlist, mdev; 290 const char *mdevname; 291 292 error = device_get_children(dev, &devlist, &devcount); 293 if (error != 0) 294 return (NULL); 295 296 for (i = 0; i < devcount; i++) { 297 mdev = devlist[i]; 298 mdevname = device_get_name(mdev); 299 if (mdevname == NULL) 300 continue; 301 len = strlen(mdevname); 302 if (strncmp("dpaa2_mac_fdt", mdevname, len) != 0) 303 continue; 304 if (!device_is_attached(mdev)) 305 continue; 306 307 if (dpaa2_mac_fdt_match_id(mdev, id)) 308 return (mdev); 309 } 310 311 return (NULL); 312 } 313 314 static int 315 dpaa2_mc_fdt_get_phy_dev(device_t dev, device_t *phy_dev, uint32_t id) 316 { 317 device_t mdev, pdev; 318 319 mdev = dpaa2_mc_fdt_find_dpaa2_mac_dev(dev, id); 320 if (mdev == NULL) { 321 device_printf(dev, "%s: error finding dpmac device with id=%u\n", 322 __func__, id); 323 return (ENXIO); 324 } 325 326 pdev = dpaa2_mac_fdt_get_phy_dev(mdev); 327 if (pdev == NULL) { 328 device_printf(dev, "%s: error getting MDIO device for dpamc %s " 329 "(id=%u)\n", __func__, device_get_nameunit(mdev), id); 330 return (ENXIO); 331 } 332 333 if (phy_dev != NULL) 334 *phy_dev = pdev; 335 336 if (bootverbose) 337 device_printf(dev, "dpmac_id %u mdev %p (%s) pdev %p (%s)\n", 338 id, mdev, device_get_nameunit(mdev), 339 pdev, device_get_nameunit(pdev)); 340 341 return (0); 342 } 343 344 static const struct ofw_bus_devinfo * 345 dpaa2_mc_simplebus_get_devinfo(device_t bus, device_t child) 346 { 347 348 return (OFW_BUS_GET_DEVINFO(device_get_parent(bus), child)); 349 } 350 351 static device_method_t dpaa2_mc_fdt_methods[] = { 352 /* Device interface */ 353 DEVMETHOD(device_probe, dpaa2_mc_fdt_probe), 354 DEVMETHOD(device_attach, dpaa2_mc_fdt_attach), 355 DEVMETHOD(device_detach, dpaa2_mc_detach), 356 357 /* Bus interface */ 358 DEVMETHOD(bus_get_rman, dpaa2_mc_rman), 359 DEVMETHOD(bus_alloc_resource, dpaa2_mc_alloc_resource), 360 DEVMETHOD(bus_adjust_resource, dpaa2_mc_adjust_resource), 361 DEVMETHOD(bus_release_resource, dpaa2_mc_release_resource), 362 DEVMETHOD(bus_activate_resource, dpaa2_mc_activate_resource), 363 DEVMETHOD(bus_deactivate_resource, dpaa2_mc_deactivate_resource), 364 DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 365 DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 366 367 /* Pseudo-PCIB interface */ 368 DEVMETHOD(pcib_alloc_msi, dpaa2_mc_alloc_msi), 369 DEVMETHOD(pcib_release_msi, dpaa2_mc_release_msi), 370 DEVMETHOD(pcib_map_msi, dpaa2_mc_map_msi), 371 DEVMETHOD(pcib_get_id, dpaa2_mc_get_id), 372 373 /* DPAA2 MC bus interface */ 374 DEVMETHOD(dpaa2_mc_manage_dev, dpaa2_mc_manage_dev), 375 DEVMETHOD(dpaa2_mc_get_free_dev,dpaa2_mc_get_free_dev), 376 DEVMETHOD(dpaa2_mc_get_dev, dpaa2_mc_get_dev), 377 DEVMETHOD(dpaa2_mc_get_shared_dev, dpaa2_mc_get_shared_dev), 378 DEVMETHOD(dpaa2_mc_reserve_dev, dpaa2_mc_reserve_dev), 379 DEVMETHOD(dpaa2_mc_release_dev, dpaa2_mc_release_dev), 380 DEVMETHOD(dpaa2_mc_get_phy_dev, dpaa2_mc_fdt_get_phy_dev), 381 382 /* OFW/simplebus */ 383 DEVMETHOD(ofw_bus_get_devinfo, dpaa2_mc_simplebus_get_devinfo), 384 DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), 385 DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), 386 DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), 387 DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), 388 DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), 389 390 DEVMETHOD_END 391 }; 392 393 DEFINE_CLASS_1(dpaa2_mc, dpaa2_mc_fdt_driver, dpaa2_mc_fdt_methods, 394 sizeof(struct dpaa2_mc_softc), dpaa2_mc_driver); 395 396 DRIVER_MODULE(dpaa2_mc, simplebus, dpaa2_mc_fdt_driver, 0, 0); 397