185958649SZbigniew Bodek /*- 285958649SZbigniew Bodek * Copyright (c) 2016 Stormshield 385958649SZbigniew Bodek * Copyright (c) 2016 Semihalf 485958649SZbigniew Bodek * All rights reserved. 585958649SZbigniew Bodek * 685958649SZbigniew Bodek * Developed by Semihalf. 785958649SZbigniew Bodek * 885958649SZbigniew Bodek * Portions of this software were developed by Semihalf 985958649SZbigniew Bodek * under sponsorship from the FreeBSD Foundation. 1085958649SZbigniew Bodek * 1185958649SZbigniew Bodek * Redistribution and use in source and binary forms, with or without 1285958649SZbigniew Bodek * modification, are permitted provided that the following conditions 1385958649SZbigniew Bodek * are met: 1485958649SZbigniew Bodek * 1. Redistributions of source code must retain the above copyright 1585958649SZbigniew Bodek * notice, this list of conditions and the following disclaimer. 1685958649SZbigniew Bodek * 2. Redistributions in binary form must reproduce the above copyright 1785958649SZbigniew Bodek * notice, this list of conditions and the following disclaimer in the 1885958649SZbigniew Bodek * documentation and/or other materials provided with the distribution. 1985958649SZbigniew Bodek * 3. Neither the name of MARVELL nor the names of contributors 2085958649SZbigniew Bodek * may be used to endorse or promote products derived from this software 2185958649SZbigniew Bodek * without specific prior written permission. 2285958649SZbigniew Bodek * 2385958649SZbigniew Bodek * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 2485958649SZbigniew Bodek * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2585958649SZbigniew Bodek * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2685958649SZbigniew Bodek * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 2785958649SZbigniew Bodek * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2885958649SZbigniew Bodek * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2985958649SZbigniew Bodek * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3085958649SZbigniew Bodek * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3185958649SZbigniew Bodek * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3285958649SZbigniew Bodek * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3385958649SZbigniew Bodek * SUCH DAMAGE. 3485958649SZbigniew Bodek */ 3585958649SZbigniew Bodek 3685958649SZbigniew Bodek /* 3785958649SZbigniew Bodek * Marvell integrated PCI/PCI-Express Bus Controller Driver. 3885958649SZbigniew Bodek */ 3985958649SZbigniew Bodek 4085958649SZbigniew Bodek #include <sys/cdefs.h> 4185958649SZbigniew Bodek __FBSDID("$FreeBSD$"); 4285958649SZbigniew Bodek 4385958649SZbigniew Bodek #include <sys/param.h> 4485958649SZbigniew Bodek #include <sys/systm.h> 4585958649SZbigniew Bodek #include <sys/kernel.h> 4685958649SZbigniew Bodek #include <sys/lock.h> 4785958649SZbigniew Bodek #include <sys/malloc.h> 4885958649SZbigniew Bodek #include <sys/module.h> 4985958649SZbigniew Bodek #include <sys/bus.h> 5085958649SZbigniew Bodek #include <sys/rman.h> 5185958649SZbigniew Bodek 5285958649SZbigniew Bodek #include <dev/ofw/ofw_bus.h> 5385958649SZbigniew Bodek #include <dev/ofw/ofw_bus_subr.h> 5485958649SZbigniew Bodek 5585958649SZbigniew Bodek static int mv_pcib_ctrl_probe(device_t); 5685958649SZbigniew Bodek static int mv_pcib_ctrl_attach(device_t); 5785958649SZbigniew Bodek static device_t mv_pcib_ctrl_add_child(device_t, u_int, const char *, int); 5885958649SZbigniew Bodek static const struct ofw_bus_devinfo * mv_pcib_ctrl_get_devinfo(device_t, device_t); 5985958649SZbigniew Bodek static struct resource * mv_pcib_ctrl_alloc_resource(device_t, device_t, int, 6085958649SZbigniew Bodek int *, rman_res_t, rman_res_t, rman_res_t, u_int); 6185958649SZbigniew Bodek void mv_pcib_ctrl_init(device_t, phandle_t); 6285958649SZbigniew Bodek static int mv_pcib_ofw_bus_attach(device_t); 6385958649SZbigniew Bodek 6485958649SZbigniew Bodek struct mv_pcib_ctrl_range { 6585958649SZbigniew Bodek uint64_t bus; 6685958649SZbigniew Bodek uint64_t host; 6785958649SZbigniew Bodek uint64_t size; 6885958649SZbigniew Bodek }; 6985958649SZbigniew Bodek 7013d464bfSMarcin Wojtas typedef int (*get_rl_t)(device_t dev, phandle_t node, pcell_t acells, 7113d464bfSMarcin Wojtas pcell_t scells, struct resource_list *rl); 7213d464bfSMarcin Wojtas 7385958649SZbigniew Bodek struct mv_pcib_ctrl_softc { 7485958649SZbigniew Bodek pcell_t addr_cells; 7585958649SZbigniew Bodek pcell_t size_cells; 7685958649SZbigniew Bodek int nranges; 7785958649SZbigniew Bodek struct mv_pcib_ctrl_range *ranges; 7885958649SZbigniew Bodek }; 7985958649SZbigniew Bodek 8085958649SZbigniew Bodek struct mv_pcib_ctrl_devinfo { 8185958649SZbigniew Bodek struct ofw_bus_devinfo di_dinfo; 8285958649SZbigniew Bodek struct resource_list di_rl; 8385958649SZbigniew Bodek }; 8485958649SZbigniew Bodek 8585958649SZbigniew Bodek static int mv_pcib_ctrl_fill_ranges(phandle_t, struct mv_pcib_ctrl_softc *); 8685958649SZbigniew Bodek 8785958649SZbigniew Bodek /* 8885958649SZbigniew Bodek * Bus interface definitions 8985958649SZbigniew Bodek */ 9085958649SZbigniew Bodek static device_method_t mv_pcib_ctrl_methods[] = { 9185958649SZbigniew Bodek /* Device interface */ 9285958649SZbigniew Bodek DEVMETHOD(device_probe, mv_pcib_ctrl_probe), 9385958649SZbigniew Bodek DEVMETHOD(device_attach, mv_pcib_ctrl_attach), 9485958649SZbigniew Bodek 9585958649SZbigniew Bodek /* Bus interface */ 9685958649SZbigniew Bodek DEVMETHOD(bus_add_child, mv_pcib_ctrl_add_child), 9785958649SZbigniew Bodek DEVMETHOD(bus_alloc_resource, mv_pcib_ctrl_alloc_resource), 9885958649SZbigniew Bodek DEVMETHOD(bus_release_resource, bus_generic_release_resource), 9985958649SZbigniew Bodek DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 10085958649SZbigniew Bodek DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 10185958649SZbigniew Bodek 10285958649SZbigniew Bodek /* ofw_bus interface */ 10385958649SZbigniew Bodek DEVMETHOD(ofw_bus_get_devinfo, mv_pcib_ctrl_get_devinfo), 10485958649SZbigniew Bodek DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), 10585958649SZbigniew Bodek DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), 10685958649SZbigniew Bodek DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), 10785958649SZbigniew Bodek DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), 10885958649SZbigniew Bodek DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), 10985958649SZbigniew Bodek 11085958649SZbigniew Bodek DEVMETHOD_END 11185958649SZbigniew Bodek }; 11285958649SZbigniew Bodek 11313d464bfSMarcin Wojtas static struct ofw_compat_data mv_pcib_ctrl_compat[] = { 11413d464bfSMarcin Wojtas {"mrvl,pcie-ctrl", (uintptr_t)&ofw_bus_reg_to_rl}, 11513d464bfSMarcin Wojtas {"marvell,armada-370-pcie", 11613d464bfSMarcin Wojtas (uintptr_t)&ofw_bus_assigned_addresses_to_rl}, 11713d464bfSMarcin Wojtas {NULL, (uintptr_t)NULL}, 11813d464bfSMarcin Wojtas }; 11913d464bfSMarcin Wojtas 12085958649SZbigniew Bodek static driver_t mv_pcib_ctrl_driver = { 12185958649SZbigniew Bodek "pcib_ctrl", 12285958649SZbigniew Bodek mv_pcib_ctrl_methods, 12385958649SZbigniew Bodek sizeof(struct mv_pcib_ctrl_softc), 12485958649SZbigniew Bodek }; 12585958649SZbigniew Bodek 12685958649SZbigniew Bodek devclass_t pcib_ctrl_devclass; 12785958649SZbigniew Bodek 12885958649SZbigniew Bodek DRIVER_MODULE(pcib_ctrl, simplebus, mv_pcib_ctrl_driver, pcib_ctrl_devclass, 0, 0); 12985958649SZbigniew Bodek 13085958649SZbigniew Bodek MALLOC_DEFINE(M_PCIB_CTRL, "PCIe Bus Controller", 13185958649SZbigniew Bodek "Marvell Integrated PCIe Bus Controller"); 13285958649SZbigniew Bodek 13385958649SZbigniew Bodek static int 13485958649SZbigniew Bodek mv_pcib_ctrl_probe(device_t dev) 13585958649SZbigniew Bodek { 13685958649SZbigniew Bodek 13713d464bfSMarcin Wojtas if (!ofw_bus_status_okay(dev)) 13813d464bfSMarcin Wojtas return (ENXIO); 13913d464bfSMarcin Wojtas 14013d464bfSMarcin Wojtas if (!ofw_bus_search_compatible(dev, mv_pcib_ctrl_compat)->ocd_data) 14185958649SZbigniew Bodek return (ENXIO); 14285958649SZbigniew Bodek 14385958649SZbigniew Bodek device_set_desc(dev, "Marvell Integrated PCIe Bus Controller"); 14485958649SZbigniew Bodek return (BUS_PROBE_DEFAULT); 14585958649SZbigniew Bodek } 14685958649SZbigniew Bodek 14785958649SZbigniew Bodek static int 14885958649SZbigniew Bodek mv_pcib_ctrl_attach(device_t dev) 14985958649SZbigniew Bodek { 15085958649SZbigniew Bodek int err; 15185958649SZbigniew Bodek 15285958649SZbigniew Bodek err = mv_pcib_ofw_bus_attach(dev); 15385958649SZbigniew Bodek if (err != 0) 15485958649SZbigniew Bodek return (err); 15585958649SZbigniew Bodek 15685958649SZbigniew Bodek return (bus_generic_attach(dev)); 15785958649SZbigniew Bodek } 15885958649SZbigniew Bodek 15985958649SZbigniew Bodek static int 16085958649SZbigniew Bodek mv_pcib_ofw_bus_attach(device_t dev) 16185958649SZbigniew Bodek { 16285958649SZbigniew Bodek struct mv_pcib_ctrl_devinfo *di; 16385958649SZbigniew Bodek struct mv_pcib_ctrl_softc *sc; 16485958649SZbigniew Bodek device_t child; 16585958649SZbigniew Bodek phandle_t parent, node; 16613d464bfSMarcin Wojtas get_rl_t get_rl; 16785958649SZbigniew Bodek 16885958649SZbigniew Bodek parent = ofw_bus_get_node(dev); 16985958649SZbigniew Bodek sc = device_get_softc(dev); 17085958649SZbigniew Bodek if (parent > 0) { 17185958649SZbigniew Bodek sc->addr_cells = 1; 17285958649SZbigniew Bodek if (OF_getencprop(parent, "#address-cells", &(sc->addr_cells), 17385958649SZbigniew Bodek sizeof(sc->addr_cells)) <= 0) 17485958649SZbigniew Bodek return(ENXIO); 17585958649SZbigniew Bodek 17685958649SZbigniew Bodek sc->size_cells = 1; 17785958649SZbigniew Bodek if (OF_getencprop(parent, "#size-cells", &(sc->size_cells), 17885958649SZbigniew Bodek sizeof(sc->size_cells)) <= 0) 17985958649SZbigniew Bodek return(ENXIO); 18085958649SZbigniew Bodek 18185958649SZbigniew Bodek for (node = OF_child(parent); node > 0; node = OF_peer(node)) { 18285958649SZbigniew Bodek di = malloc(sizeof(*di), M_PCIB_CTRL, M_WAITOK | M_ZERO); 18385958649SZbigniew Bodek if (ofw_bus_gen_setup_devinfo(&di->di_dinfo, node)) { 18485958649SZbigniew Bodek if (bootverbose) { 18585958649SZbigniew Bodek device_printf(dev, 18685958649SZbigniew Bodek "Could not set up devinfo for PCI\n"); 18785958649SZbigniew Bodek } 18885958649SZbigniew Bodek free(di, M_PCIB_CTRL); 18985958649SZbigniew Bodek continue; 19085958649SZbigniew Bodek } 19185958649SZbigniew Bodek 19285958649SZbigniew Bodek child = device_add_child(dev, NULL, -1); 19385958649SZbigniew Bodek if (child == NULL) { 19485958649SZbigniew Bodek if (bootverbose) { 19585958649SZbigniew Bodek device_printf(dev, 19685958649SZbigniew Bodek "Could not add child: %s\n", 19785958649SZbigniew Bodek di->di_dinfo.obd_name); 19885958649SZbigniew Bodek } 19985958649SZbigniew Bodek ofw_bus_gen_destroy_devinfo(&di->di_dinfo); 20085958649SZbigniew Bodek free(di, M_PCIB_CTRL); 20185958649SZbigniew Bodek continue; 20285958649SZbigniew Bodek } 20385958649SZbigniew Bodek 20485958649SZbigniew Bodek resource_list_init(&di->di_rl); 20513d464bfSMarcin Wojtas get_rl = (get_rl_t) ofw_bus_search_compatible(dev, 20613d464bfSMarcin Wojtas mv_pcib_ctrl_compat)->ocd_data; 20713d464bfSMarcin Wojtas if (get_rl != NULL) 20813d464bfSMarcin Wojtas get_rl(child, node, sc->addr_cells, 20985958649SZbigniew Bodek sc->size_cells, &di->di_rl); 21085958649SZbigniew Bodek 21185958649SZbigniew Bodek device_set_ivars(child, di); 21285958649SZbigniew Bodek } 21385958649SZbigniew Bodek } 21485958649SZbigniew Bodek 21585958649SZbigniew Bodek if (mv_pcib_ctrl_fill_ranges(parent, sc) < 0) { 21685958649SZbigniew Bodek device_printf(dev, "could not get ranges\n"); 21785958649SZbigniew Bodek return (ENXIO); 21885958649SZbigniew Bodek } 21985958649SZbigniew Bodek 22085958649SZbigniew Bodek return (0); 22185958649SZbigniew Bodek } 22285958649SZbigniew Bodek 22385958649SZbigniew Bodek static device_t 22485958649SZbigniew Bodek mv_pcib_ctrl_add_child(device_t dev, u_int order, const char *name, int unit) 22585958649SZbigniew Bodek { 22685958649SZbigniew Bodek device_t cdev; 22785958649SZbigniew Bodek struct mv_pcib_ctrl_devinfo *di; 22885958649SZbigniew Bodek 22985958649SZbigniew Bodek cdev = device_add_child_ordered(dev, order, name, unit); 23085958649SZbigniew Bodek if (cdev == NULL) 23185958649SZbigniew Bodek return (NULL); 23285958649SZbigniew Bodek 23385958649SZbigniew Bodek di = malloc(sizeof(*di), M_DEVBUF, M_WAITOK | M_ZERO); 23485958649SZbigniew Bodek di->di_dinfo.obd_node = -1; 23585958649SZbigniew Bodek resource_list_init(&di->di_rl); 23685958649SZbigniew Bodek device_set_ivars(cdev, di); 23785958649SZbigniew Bodek 23885958649SZbigniew Bodek return (cdev); 23985958649SZbigniew Bodek } 24085958649SZbigniew Bodek 24185958649SZbigniew Bodek static struct resource * 24285958649SZbigniew Bodek mv_pcib_ctrl_alloc_resource(device_t bus, device_t child, int type, int *rid, 24385958649SZbigniew Bodek rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) 24485958649SZbigniew Bodek { 24585958649SZbigniew Bodek struct mv_pcib_ctrl_devinfo *di; 24685958649SZbigniew Bodek struct resource_list_entry *rle; 24785958649SZbigniew Bodek struct mv_pcib_ctrl_softc *sc; 24885958649SZbigniew Bodek int i; 24985958649SZbigniew Bodek 25085958649SZbigniew Bodek if (RMAN_IS_DEFAULT_RANGE(start, end)) { 25185958649SZbigniew Bodek 25285958649SZbigniew Bodek if ((di = device_get_ivars(child)) == NULL) 25385958649SZbigniew Bodek return (NULL); 25485958649SZbigniew Bodek if (type != SYS_RES_MEMORY) 25585958649SZbigniew Bodek return (NULL); 25685958649SZbigniew Bodek 25785958649SZbigniew Bodek /* Find defaults for this rid */ 25885958649SZbigniew Bodek rle = resource_list_find(&di->di_rl, type, *rid); 25985958649SZbigniew Bodek 26085958649SZbigniew Bodek if (rle == NULL) 26185958649SZbigniew Bodek return (NULL); 26285958649SZbigniew Bodek 26385958649SZbigniew Bodek start = rle->start; 26485958649SZbigniew Bodek end = rle->end; 26585958649SZbigniew Bodek count = rle->count; 26685958649SZbigniew Bodek } 26785958649SZbigniew Bodek 26885958649SZbigniew Bodek sc = device_get_softc(bus); 26985958649SZbigniew Bodek if (type == SYS_RES_MEMORY) { 27085958649SZbigniew Bodek /* Remap through ranges property */ 27185958649SZbigniew Bodek for (i = 0; i < sc->nranges; i++) { 27285958649SZbigniew Bodek if (start >= sc->ranges[i].bus && end < 27385958649SZbigniew Bodek sc->ranges[i].bus + sc->ranges[i].size) { 27485958649SZbigniew Bodek start -= sc->ranges[i].bus; 27585958649SZbigniew Bodek start += sc->ranges[i].host; 27685958649SZbigniew Bodek end -= sc->ranges[i].bus; 27785958649SZbigniew Bodek end += sc->ranges[i].host; 27885958649SZbigniew Bodek break; 27985958649SZbigniew Bodek } 28085958649SZbigniew Bodek } 28185958649SZbigniew Bodek 28285958649SZbigniew Bodek if (i == sc->nranges && sc->nranges != 0) { 28385958649SZbigniew Bodek device_printf(bus, "Could not map resource " 28485958649SZbigniew Bodek "%#llx-%#llx\n", start, end); 28585958649SZbigniew Bodek return (NULL); 28685958649SZbigniew Bodek } 28785958649SZbigniew Bodek } 28885958649SZbigniew Bodek 28985958649SZbigniew Bodek return (bus_generic_alloc_resource(bus, child, type, rid, start, end, 29085958649SZbigniew Bodek count, flags)); 29185958649SZbigniew Bodek } 29285958649SZbigniew Bodek 29385958649SZbigniew Bodek static int 29485958649SZbigniew Bodek mv_pcib_ctrl_fill_ranges(phandle_t node, struct mv_pcib_ctrl_softc *sc) 29585958649SZbigniew Bodek { 29685958649SZbigniew Bodek int host_address_cells; 29785958649SZbigniew Bodek cell_t *base_ranges; 29885958649SZbigniew Bodek ssize_t nbase_ranges; 29985958649SZbigniew Bodek int err; 30085958649SZbigniew Bodek int i, j, k; 30185958649SZbigniew Bodek 30285958649SZbigniew Bodek err = OF_searchencprop(OF_parent(node), "#address-cells", 30385958649SZbigniew Bodek &host_address_cells, sizeof(host_address_cells)); 30485958649SZbigniew Bodek if (err <= 0) 30585958649SZbigniew Bodek return (-1); 30685958649SZbigniew Bodek 30785958649SZbigniew Bodek nbase_ranges = OF_getproplen(node, "ranges"); 30885958649SZbigniew Bodek if (nbase_ranges < 0) 30985958649SZbigniew Bodek return (-1); 31085958649SZbigniew Bodek sc->nranges = nbase_ranges / sizeof(cell_t) / 31185958649SZbigniew Bodek (sc->addr_cells + host_address_cells + sc->size_cells); 31285958649SZbigniew Bodek if (sc->nranges == 0) 31385958649SZbigniew Bodek return (0); 31485958649SZbigniew Bodek 31585958649SZbigniew Bodek sc->ranges = malloc(sc->nranges * sizeof(sc->ranges[0]), 31685958649SZbigniew Bodek M_DEVBUF, M_WAITOK); 31785958649SZbigniew Bodek base_ranges = malloc(nbase_ranges, M_DEVBUF, M_WAITOK); 31885958649SZbigniew Bodek OF_getencprop(node, "ranges", base_ranges, nbase_ranges); 31985958649SZbigniew Bodek 32085958649SZbigniew Bodek for (i = 0, j = 0; i < sc->nranges; i++) { 32185958649SZbigniew Bodek sc->ranges[i].bus = 0; 32285958649SZbigniew Bodek for (k = 0; k < sc->addr_cells; k++) { 32385958649SZbigniew Bodek sc->ranges[i].bus <<= 32; 32485958649SZbigniew Bodek sc->ranges[i].bus |= base_ranges[j++]; 32585958649SZbigniew Bodek } 32685958649SZbigniew Bodek sc->ranges[i].host = 0; 32785958649SZbigniew Bodek for (k = 0; k < host_address_cells; k++) { 32885958649SZbigniew Bodek sc->ranges[i].host <<= 32; 32985958649SZbigniew Bodek sc->ranges[i].host |= base_ranges[j++]; 33085958649SZbigniew Bodek } 33185958649SZbigniew Bodek sc->ranges[i].size = 0; 33285958649SZbigniew Bodek for (k = 0; k < sc->size_cells; k++) { 33385958649SZbigniew Bodek sc->ranges[i].size <<= 32; 33485958649SZbigniew Bodek sc->ranges[i].size |= base_ranges[j++]; 33585958649SZbigniew Bodek } 33685958649SZbigniew Bodek } 33785958649SZbigniew Bodek 33885958649SZbigniew Bodek free(base_ranges, M_DEVBUF); 33985958649SZbigniew Bodek return (sc->nranges); 34085958649SZbigniew Bodek } 34185958649SZbigniew Bodek 34285958649SZbigniew Bodek static const struct ofw_bus_devinfo * 34385958649SZbigniew Bodek mv_pcib_ctrl_get_devinfo(device_t bus __unused, device_t child) 34485958649SZbigniew Bodek { 34585958649SZbigniew Bodek struct mv_pcib_ctrl_devinfo *di; 34685958649SZbigniew Bodek 34785958649SZbigniew Bodek di = device_get_ivars(child); 34885958649SZbigniew Bodek return (&di->di_dinfo); 34985958649SZbigniew Bodek } 350