mv_pci.c revision 240493
1185089Sraj/*- 2209131Sraj * Copyright (c) 2008 MARVELL INTERNATIONAL LTD. 3209131Sraj * Copyright (c) 2010 The FreeBSD Foundation 4240489Sgber * Copyright (c) 2010-2012 Semihalf 5185089Sraj * All rights reserved. 6185089Sraj * 7185089Sraj * Developed by Semihalf. 8185089Sraj * 9209131Sraj * Portions of this software were developed by Semihalf 10209131Sraj * under sponsorship from the FreeBSD Foundation. 11209131Sraj * 12185089Sraj * Redistribution and use in source and binary forms, with or without 13185089Sraj * modification, are permitted provided that the following conditions 14185089Sraj * are met: 15185089Sraj * 1. Redistributions of source code must retain the above copyright 16185089Sraj * notice, this list of conditions and the following disclaimer. 17185089Sraj * 2. Redistributions in binary form must reproduce the above copyright 18185089Sraj * notice, this list of conditions and the following disclaimer in the 19185089Sraj * documentation and/or other materials provided with the distribution. 20185089Sraj * 3. Neither the name of MARVELL nor the names of contributors 21185089Sraj * may be used to endorse or promote products derived from this software 22185089Sraj * without specific prior written permission. 23185089Sraj * 24185089Sraj * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 25185089Sraj * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26185089Sraj * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27185089Sraj * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 28185089Sraj * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29185089Sraj * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30185089Sraj * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31185089Sraj * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32185089Sraj * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33185089Sraj * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34185089Sraj * SUCH DAMAGE. 35185089Sraj */ 36185089Sraj 37185089Sraj/* 38185089Sraj * Marvell integrated PCI/PCI-Express controller driver. 39185089Sraj */ 40185089Sraj 41185089Sraj#include <sys/cdefs.h> 42185089Sraj__FBSDID("$FreeBSD: head/sys/arm/mv/mv_pci.c 240493 2012-09-14 10:06:56Z gber $"); 43185089Sraj 44185089Sraj#include <sys/param.h> 45185089Sraj#include <sys/systm.h> 46185089Sraj#include <sys/kernel.h> 47185089Sraj#include <sys/lock.h> 48185089Sraj#include <sys/malloc.h> 49185089Sraj#include <sys/module.h> 50185089Sraj#include <sys/mutex.h> 51185089Sraj#include <sys/queue.h> 52185089Sraj#include <sys/bus.h> 53185089Sraj#include <sys/rman.h> 54185089Sraj#include <sys/endian.h> 55185089Sraj 56240493Sgber#include <machine/intr.h> 57240493Sgber 58185089Sraj#include <vm/vm.h> 59185089Sraj#include <vm/pmap.h> 60185089Sraj 61209131Sraj#include <dev/fdt/fdt_common.h> 62209131Sraj#include <dev/ofw/ofw_bus.h> 63209131Sraj#include <dev/ofw/ofw_bus_subr.h> 64185089Sraj#include <dev/pci/pcivar.h> 65185089Sraj#include <dev/pci/pcireg.h> 66185089Sraj#include <dev/pci/pcib_private.h> 67185089Sraj 68209131Sraj#include "ofw_bus_if.h" 69185089Sraj#include "pcib_if.h" 70185089Sraj 71185089Sraj#include <machine/resource.h> 72185089Sraj#include <machine/bus.h> 73185089Sraj 74185089Sraj#include <arm/mv/mvreg.h> 75185089Sraj#include <arm/mv/mvvar.h> 76209131Sraj#include <arm/mv/mvwin.h> 77185089Sraj 78240493Sgber#ifdef DEBUG 79240493Sgber#define debugf(fmt, args...) do { printf(fmt,##args); } while (0) 80240493Sgber#else 81240493Sgber#define debugf(fmt, args...) 82240493Sgber#endif 83240493Sgber 84185089Sraj#define PCI_CFG_ENA (1 << 31) 85185089Sraj#define PCI_CFG_BUS(bus) (((bus) & 0xff) << 16) 86185089Sraj#define PCI_CFG_DEV(dev) (((dev) & 0x1f) << 11) 87185089Sraj#define PCI_CFG_FUN(fun) (((fun) & 0x7) << 8) 88185089Sraj#define PCI_CFG_PCIE_REG(reg) ((reg) & 0xfc) 89185089Sraj 90185089Sraj#define PCI_REG_CFG_ADDR 0x0C78 91185089Sraj#define PCI_REG_CFG_DATA 0x0C7C 92185089Sraj 93185089Sraj#define PCIE_REG_CFG_ADDR 0x18F8 94185089Sraj#define PCIE_REG_CFG_DATA 0x18FC 95185089Sraj#define PCIE_REG_CONTROL 0x1A00 96185089Sraj#define PCIE_CTRL_LINK1X 0x00000001 97185089Sraj#define PCIE_REG_STATUS 0x1A04 98185089Sraj#define PCIE_REG_IRQ_MASK 0x1910 99185089Sraj 100240489Sgber#define PCIE_CONTROL_ROOT_CMPLX (1 << 1) 101240489Sgber#define PCIE_CONTROL_HOT_RESET (1 << 24) 102185089Sraj 103240489Sgber#define PCIE_LINK_TIMEOUT 1000000 104185089Sraj 105240489Sgber#define PCIE_STATUS_LINK_DOWN 1 106240489Sgber#define PCIE_STATUS_DEV_OFFS 16 107185089Sraj 108240489Sgber/* Minimum PCI Memory and I/O allocations taken from PCI spec (in bytes) */ 109240489Sgber#define PCI_MIN_IO_ALLOC 4 110240489Sgber#define PCI_MIN_MEM_ALLOC 16 111240489Sgber 112240489Sgber#define BITS_PER_UINT32 (NBBY * sizeof(uint32_t)) 113240489Sgber 114209131Srajstruct mv_pcib_softc { 115185089Sraj device_t sc_dev; 116185089Sraj 117209131Sraj struct rman sc_mem_rman; 118209131Sraj bus_addr_t sc_mem_base; 119209131Sraj bus_addr_t sc_mem_size; 120240489Sgber uint32_t sc_mem_map[MV_PCI_MEM_SLICE_SIZE / 121240489Sgber (PCI_MIN_MEM_ALLOC * BITS_PER_UINT32)]; 122240489Sgber int sc_win_target; 123209131Sraj int sc_mem_win_attr; 124185089Sraj 125209131Sraj struct rman sc_io_rman; 126209131Sraj bus_addr_t sc_io_base; 127209131Sraj bus_addr_t sc_io_size; 128240489Sgber uint32_t sc_io_map[MV_PCI_IO_SLICE_SIZE / 129240489Sgber (PCI_MIN_IO_ALLOC * BITS_PER_UINT32)]; 130209131Sraj int sc_io_win_attr; 131185089Sraj 132185089Sraj struct resource *sc_res; 133185089Sraj bus_space_handle_t sc_bsh; 134185089Sraj bus_space_tag_t sc_bst; 135185089Sraj int sc_rid; 136185089Sraj 137240493Sgber struct mtx sc_msi_mtx; 138240493Sgber uint32_t sc_msi_bitmap; 139240493Sgber 140185089Sraj int sc_busnr; /* Host bridge bus number */ 141185089Sraj int sc_devnr; /* Host bridge device number */ 142209131Sraj int sc_type; 143240489Sgber int sc_mode; /* Endpoint / Root Complex */ 144185089Sraj 145209131Sraj struct fdt_pci_intr sc_intr_info; 146185089Sraj}; 147185089Sraj 148209131Sraj/* Local forward prototypes */ 149209131Srajstatic int mv_pcib_decode_win(phandle_t, struct mv_pcib_softc *); 150209131Srajstatic void mv_pcib_hw_cfginit(void); 151209131Srajstatic uint32_t mv_pcib_hw_cfgread(struct mv_pcib_softc *, u_int, u_int, 152209131Sraj u_int, u_int, int); 153209131Srajstatic void mv_pcib_hw_cfgwrite(struct mv_pcib_softc *, u_int, u_int, 154209131Sraj u_int, u_int, uint32_t, int); 155209131Srajstatic int mv_pcib_init(struct mv_pcib_softc *, int, int); 156209131Srajstatic int mv_pcib_init_all_bars(struct mv_pcib_softc *, int, int, int, int); 157209131Srajstatic void mv_pcib_init_bridge(struct mv_pcib_softc *, int, int, int); 158209131Srajstatic int mv_pcib_intr_info(phandle_t, struct mv_pcib_softc *); 159209131Srajstatic inline void pcib_write_irq_mask(struct mv_pcib_softc *, uint32_t); 160240489Sgberstatic void mv_pcib_enable(struct mv_pcib_softc *, uint32_t); 161240489Sgberstatic int mv_pcib_mem_init(struct mv_pcib_softc *); 162185089Sraj 163209131Sraj/* Forward prototypes */ 164209131Srajstatic int mv_pcib_probe(device_t); 165209131Srajstatic int mv_pcib_attach(device_t); 166209131Sraj 167209131Srajstatic struct resource *mv_pcib_alloc_resource(device_t, device_t, int, int *, 168185089Sraj u_long, u_long, u_long, u_int); 169209131Srajstatic int mv_pcib_release_resource(device_t, device_t, int, int, 170185089Sraj struct resource *); 171209131Srajstatic int mv_pcib_read_ivar(device_t, device_t, int, uintptr_t *); 172209131Srajstatic int mv_pcib_write_ivar(device_t, device_t, int, uintptr_t); 173185089Sraj 174209131Srajstatic int mv_pcib_maxslots(device_t); 175209131Srajstatic uint32_t mv_pcib_read_config(device_t, u_int, u_int, u_int, u_int, int); 176209131Srajstatic void mv_pcib_write_config(device_t, u_int, u_int, u_int, u_int, 177185089Sraj uint32_t, int); 178209131Srajstatic int mv_pcib_route_interrupt(device_t, device_t, int); 179240493Sgber#if defined(SOC_MV_ARMADAXP) 180240493Sgberstatic int mv_pcib_alloc_msi(device_t, device_t, int, int, int *); 181240493Sgberstatic int mv_pcib_map_msi(device_t, device_t, int, uint64_t *, uint32_t *); 182240493Sgberstatic int mv_pcib_release_msi(device_t, device_t, int, int *); 183240493Sgber#endif 184185089Sraj 185185089Sraj/* 186185089Sraj * Bus interface definitions. 187185089Sraj */ 188209131Srajstatic device_method_t mv_pcib_methods[] = { 189185089Sraj /* Device interface */ 190209131Sraj DEVMETHOD(device_probe, mv_pcib_probe), 191209131Sraj DEVMETHOD(device_attach, mv_pcib_attach), 192185089Sraj 193185089Sraj /* Bus interface */ 194209131Sraj DEVMETHOD(bus_read_ivar, mv_pcib_read_ivar), 195209131Sraj DEVMETHOD(bus_write_ivar, mv_pcib_write_ivar), 196209131Sraj DEVMETHOD(bus_alloc_resource, mv_pcib_alloc_resource), 197209131Sraj DEVMETHOD(bus_release_resource, mv_pcib_release_resource), 198185089Sraj DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 199185089Sraj DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 200185089Sraj DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 201185089Sraj DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 202185089Sraj 203185089Sraj /* pcib interface */ 204209131Sraj DEVMETHOD(pcib_maxslots, mv_pcib_maxslots), 205209131Sraj DEVMETHOD(pcib_read_config, mv_pcib_read_config), 206209131Sraj DEVMETHOD(pcib_write_config, mv_pcib_write_config), 207209131Sraj DEVMETHOD(pcib_route_interrupt, mv_pcib_route_interrupt), 208240493Sgber 209240493Sgber#if defined(SOC_MV_ARMADAXP) 210240493Sgber DEVMETHOD(pcib_alloc_msi, mv_pcib_alloc_msi), 211240493Sgber DEVMETHOD(pcib_release_msi, mv_pcib_release_msi), 212240493Sgber DEVMETHOD(pcib_map_msi, mv_pcib_map_msi), 213240493Sgber#endif 214240493Sgber 215209131Sraj /* OFW bus interface */ 216209131Sraj DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), 217209131Sraj DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), 218209131Sraj DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), 219209131Sraj DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), 220209131Sraj DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), 221209131Sraj 222227843Smarius DEVMETHOD_END 223185089Sraj}; 224185089Sraj 225209131Srajstatic driver_t mv_pcib_driver = { 226185089Sraj "pcib", 227209131Sraj mv_pcib_methods, 228209131Sraj sizeof(struct mv_pcib_softc), 229185089Sraj}; 230185089Sraj 231185089Srajdevclass_t pcib_devclass; 232185089Sraj 233209131SrajDRIVER_MODULE(pcib, fdtbus, mv_pcib_driver, pcib_devclass, 0, 0); 234185089Sraj 235185089Srajstatic struct mtx pcicfg_mtx; 236185089Sraj 237185089Srajstatic int 238209131Srajmv_pcib_probe(device_t self) 239185089Sraj{ 240218228Smarcel phandle_t node; 241185089Sraj 242218228Smarcel node = ofw_bus_get_node(self); 243218228Smarcel if (!fdt_is_type(node, "pci")) 244209131Sraj return (ENXIO); 245218228Smarcel 246218228Smarcel if (!(fdt_is_compatible(node, "mrvl,pcie") || 247218228Smarcel fdt_is_compatible(node, "mrvl,pci"))) 248209131Sraj return (ENXIO); 249185089Sraj 250209131Sraj device_set_desc(self, "Marvell Integrated PCI/PCI-E Controller"); 251209131Sraj return (BUS_PROBE_DEFAULT); 252185089Sraj} 253185089Sraj 254185089Srajstatic int 255209131Srajmv_pcib_attach(device_t self) 256185089Sraj{ 257209131Sraj struct mv_pcib_softc *sc; 258209131Sraj phandle_t node, parnode; 259240489Sgber uint32_t val, unit; 260209131Sraj int err; 261185089Sraj 262185089Sraj sc = device_get_softc(self); 263209131Sraj sc->sc_dev = self; 264240489Sgber unit = fdt_get_unit(self); 265185089Sraj 266240489Sgber 267218228Smarcel node = ofw_bus_get_node(self); 268218228Smarcel parnode = OF_parent(node); 269218228Smarcel if (fdt_is_compatible(node, "mrvl,pcie")) { 270209131Sraj sc->sc_type = MV_TYPE_PCIE; 271240489Sgber sc->sc_win_target = MV_WIN_PCIE_TARGET(unit); 272240489Sgber sc->sc_mem_win_attr = MV_WIN_PCIE_MEM_ATTR(unit); 273240489Sgber sc->sc_io_win_attr = MV_WIN_PCIE_IO_ATTR(unit); 274218228Smarcel } else if (fdt_is_compatible(node, "mrvl,pci")) { 275209131Sraj sc->sc_type = MV_TYPE_PCI; 276240489Sgber sc->sc_win_target = MV_WIN_PCI_TARGET; 277209131Sraj sc->sc_mem_win_attr = MV_WIN_PCI_MEM_ATTR; 278209131Sraj sc->sc_io_win_attr = MV_WIN_PCI_IO_ATTR; 279209131Sraj } else 280185089Sraj return (ENXIO); 281185089Sraj 282209131Sraj /* 283209131Sraj * Retrieve our mem-mapped registers range. 284209131Sraj */ 285185089Sraj sc->sc_rid = 0; 286185089Sraj sc->sc_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &sc->sc_rid, 287185089Sraj RF_ACTIVE); 288185089Sraj if (sc->sc_res == NULL) { 289209131Sraj device_printf(self, "could not map memory\n"); 290185089Sraj return (ENXIO); 291185089Sraj } 292185089Sraj sc->sc_bst = rman_get_bustag(sc->sc_res); 293185089Sraj sc->sc_bsh = rman_get_bushandle(sc->sc_res); 294185089Sraj 295240489Sgber val = bus_space_read_4(sc->sc_bst, sc->sc_bsh, PCIE_REG_CONTROL); 296240489Sgber sc->sc_mode = (val & PCIE_CONTROL_ROOT_CMPLX ? MV_MODE_ROOT : 297240489Sgber MV_MODE_ENDPOINT); 298240489Sgber 299209131Sraj /* 300240489Sgber * Get PCI interrupt info. 301240489Sgber */ 302240489Sgber if ((sc->sc_mode == MV_MODE_ROOT) && 303240489Sgber (mv_pcib_intr_info(node, sc) != 0)) { 304240489Sgber device_printf(self, "could not retrieve interrupt info\n"); 305240489Sgber return (ENXIO); 306240489Sgber } 307240489Sgber 308240489Sgber /* 309209131Sraj * Configure decode windows for PCI(E) access. 310209131Sraj */ 311209131Sraj if (mv_pcib_decode_win(node, sc) != 0) 312209131Sraj return (ENXIO); 313209131Sraj 314209131Sraj mv_pcib_hw_cfginit(); 315209131Sraj 316209131Sraj /* 317240489Sgber * Enable PCIE device. 318209131Sraj */ 319240489Sgber mv_pcib_enable(sc, unit); 320185089Sraj 321240489Sgber /* 322240489Sgber * Memory management. 323240489Sgber */ 324240489Sgber err = mv_pcib_mem_init(sc); 325240489Sgber if (err) 326240489Sgber return (err); 327185089Sraj 328240489Sgber if (sc->sc_mode == MV_MODE_ROOT) { 329240489Sgber err = mv_pcib_init(sc, sc->sc_busnr, 330240489Sgber mv_pcib_maxslots(sc->sc_dev)); 331240489Sgber if (err) 332240489Sgber goto error; 333240489Sgber 334240489Sgber device_add_child(self, "pci", -1); 335240489Sgber } else { 336240489Sgber sc->sc_devnr = 1; 337240489Sgber bus_space_write_4(sc->sc_bst, sc->sc_bsh, 338240489Sgber PCIE_REG_STATUS, 1 << PCIE_STATUS_DEV_OFFS); 339240489Sgber device_add_child(self, "pci_ep", -1); 340240489Sgber } 341240489Sgber 342240493Sgber mtx_init(&sc->sc_msi_mtx, "msi_mtx", NULL, MTX_DEF); 343240489Sgber return (bus_generic_attach(self)); 344240489Sgber 345240489Sgbererror: 346240489Sgber /* XXX SYS_RES_ should be released here */ 347240489Sgber rman_fini(&sc->sc_mem_rman); 348240489Sgber rman_fini(&sc->sc_io_rman); 349240489Sgber 350240489Sgber return (err); 351240489Sgber} 352240489Sgber 353240489Sgberstatic void 354240489Sgbermv_pcib_enable(struct mv_pcib_softc *sc, uint32_t unit) 355240489Sgber{ 356240489Sgber uint32_t val; 357240489Sgber#if !defined(SOC_MV_ARMADAXP) 358240489Sgber int timeout; 359240489Sgber 360240489Sgber /* 361240489Sgber * Check if PCIE device is enabled. 362240489Sgber */ 363240489Sgber if (read_cpu_ctrl(CPU_CONTROL) & CPU_CONTROL_PCIE_DISABLE(unit)) { 364240489Sgber write_cpu_ctrl(CPU_CONTROL, read_cpu_ctrl(CPU_CONTROL) & 365240489Sgber ~(CPU_CONTROL_PCIE_DISABLE(unit))); 366240489Sgber 367240489Sgber timeout = PCIE_LINK_TIMEOUT; 368240489Sgber val = bus_space_read_4(sc->sc_bst, sc->sc_bsh, 369240489Sgber PCIE_REG_STATUS); 370240489Sgber while (((val & PCIE_STATUS_LINK_DOWN) == 1) && (timeout > 0)) { 371240489Sgber DELAY(1000); 372240489Sgber timeout -= 1000; 373240489Sgber val = bus_space_read_4(sc->sc_bst, sc->sc_bsh, 374240489Sgber PCIE_REG_STATUS); 375240489Sgber } 376240489Sgber } 377240489Sgber#endif 378240489Sgber 379240489Sgber 380240489Sgber if (sc->sc_mode == MV_MODE_ROOT) { 381240489Sgber /* 382240489Sgber * Enable PCI bridge. 383240489Sgber */ 384240489Sgber val = bus_space_read_4(sc->sc_bst, sc->sc_bsh, PCIR_COMMAND); 385240489Sgber val |= PCIM_CMD_SERRESPEN | PCIM_CMD_BUSMASTEREN | 386240489Sgber PCIM_CMD_MEMEN | PCIM_CMD_PORTEN; 387240489Sgber bus_space_write_4(sc->sc_bst, sc->sc_bsh, PCIR_COMMAND, val); 388240489Sgber } 389240489Sgber} 390240489Sgber 391240489Sgberstatic int 392240489Sgbermv_pcib_mem_init(struct mv_pcib_softc *sc) 393240489Sgber{ 394240489Sgber int err; 395240489Sgber 396240489Sgber /* 397240489Sgber * Memory management. 398240489Sgber */ 399209131Sraj sc->sc_mem_rman.rm_type = RMAN_ARRAY; 400209131Sraj err = rman_init(&sc->sc_mem_rman); 401186932Sraj if (err) 402186932Sraj return (err); 403186932Sraj 404209131Sraj sc->sc_io_rman.rm_type = RMAN_ARRAY; 405209131Sraj err = rman_init(&sc->sc_io_rman); 406186932Sraj if (err) { 407209131Sraj rman_fini(&sc->sc_mem_rman); 408186932Sraj return (err); 409186932Sraj } 410186932Sraj 411209131Sraj err = rman_manage_region(&sc->sc_mem_rman, sc->sc_mem_base, 412209131Sraj sc->sc_mem_base + sc->sc_mem_size - 1); 413186932Sraj if (err) 414186932Sraj goto error; 415186932Sraj 416209131Sraj err = rman_manage_region(&sc->sc_io_rman, sc->sc_io_base, 417209131Sraj sc->sc_io_base + sc->sc_io_size - 1); 418186932Sraj if (err) 419186932Sraj goto error; 420186932Sraj 421240489Sgber return (0); 422185089Sraj 423186932Srajerror: 424209131Sraj rman_fini(&sc->sc_mem_rman); 425209131Sraj rman_fini(&sc->sc_io_rman); 426240489Sgber 427186932Sraj return (err); 428185089Sraj} 429185089Sraj 430240489Sgberstatic inline uint32_t 431240489Sgberpcib_bit_get(uint32_t *map, uint32_t bit) 432240489Sgber{ 433240489Sgber uint32_t n = bit / BITS_PER_UINT32; 434240489Sgber 435240489Sgber bit = bit % BITS_PER_UINT32; 436240489Sgber return (map[n] & (1 << bit)); 437240489Sgber} 438240489Sgber 439240489Sgberstatic inline void 440240489Sgberpcib_bit_set(uint32_t *map, uint32_t bit) 441240489Sgber{ 442240489Sgber uint32_t n = bit / BITS_PER_UINT32; 443240489Sgber 444240489Sgber bit = bit % BITS_PER_UINT32; 445240489Sgber map[n] |= (1 << bit); 446240489Sgber} 447240489Sgber 448240489Sgberstatic inline uint32_t 449240489Sgberpcib_map_check(uint32_t *map, uint32_t start, uint32_t bits) 450240489Sgber{ 451240489Sgber uint32_t i; 452240489Sgber 453240489Sgber for (i = start; i < start + bits; i++) 454240489Sgber if (pcib_bit_get(map, i)) 455240489Sgber return (0); 456240489Sgber 457240489Sgber return (1); 458240489Sgber} 459240489Sgber 460240489Sgberstatic inline void 461240489Sgberpcib_map_set(uint32_t *map, uint32_t start, uint32_t bits) 462240489Sgber{ 463240489Sgber uint32_t i; 464240489Sgber 465240489Sgber for (i = start; i < start + bits; i++) 466240489Sgber pcib_bit_set(map, i); 467240489Sgber} 468240489Sgber 469240489Sgber/* 470240489Sgber * The idea of this allocator is taken from ARM No-Cache memory 471240489Sgber * management code (sys/arm/arm/vm_machdep.c). 472240489Sgber */ 473240489Sgberstatic bus_addr_t 474240489Sgberpcib_alloc(struct mv_pcib_softc *sc, uint32_t smask) 475240489Sgber{ 476240489Sgber uint32_t bits, bits_limit, i, *map, min_alloc, size; 477240489Sgber bus_addr_t addr = 0; 478240489Sgber bus_addr_t base; 479240489Sgber 480240489Sgber if (smask & 1) { 481240489Sgber base = sc->sc_io_base; 482240489Sgber min_alloc = PCI_MIN_IO_ALLOC; 483240489Sgber bits_limit = sc->sc_io_size / min_alloc; 484240489Sgber map = sc->sc_io_map; 485240489Sgber smask &= ~0x3; 486240489Sgber } else { 487240489Sgber base = sc->sc_mem_base; 488240489Sgber min_alloc = PCI_MIN_MEM_ALLOC; 489240489Sgber bits_limit = sc->sc_mem_size / min_alloc; 490240489Sgber map = sc->sc_mem_map; 491240489Sgber smask &= ~0xF; 492240489Sgber } 493240489Sgber 494240489Sgber size = ~smask + 1; 495240489Sgber bits = size / min_alloc; 496240489Sgber 497240489Sgber for (i = 0; i + bits <= bits_limit; i += bits) 498240489Sgber if (pcib_map_check(map, i, bits)) { 499240489Sgber pcib_map_set(map, i, bits); 500240489Sgber addr = base + (i * min_alloc); 501240489Sgber return (addr); 502240489Sgber } 503240489Sgber 504240489Sgber return (addr); 505240489Sgber} 506240489Sgber 507185089Srajstatic int 508209131Srajmv_pcib_init_bar(struct mv_pcib_softc *sc, int bus, int slot, int func, 509185089Sraj int barno) 510185089Sraj{ 511240489Sgber uint32_t addr, bar; 512185089Sraj int reg, width; 513185089Sraj 514185089Sraj reg = PCIR_BAR(barno); 515240489Sgber 516240489Sgber /* 517240489Sgber * Need to init the BAR register with 0xffffffff before correct 518240489Sgber * value can be read. 519240489Sgber */ 520240489Sgber mv_pcib_write_config(sc->sc_dev, bus, slot, func, reg, ~0, 4); 521209131Sraj bar = mv_pcib_read_config(sc->sc_dev, bus, slot, func, reg, 4); 522185089Sraj if (bar == 0) 523185089Sraj return (1); 524185089Sraj 525185089Sraj /* Calculate BAR size: 64 or 32 bit (in 32-bit units) */ 526185089Sraj width = ((bar & 7) == 4) ? 2 : 1; 527185089Sraj 528240489Sgber addr = pcib_alloc(sc, bar); 529240489Sgber if (!addr) 530185089Sraj return (-1); 531185089Sraj 532185089Sraj if (bootverbose) 533240489Sgber printf("PCI %u:%u:%u: reg %x: smask=%08x: addr=%08x\n", 534240489Sgber bus, slot, func, reg, bar, addr); 535185089Sraj 536209131Sraj mv_pcib_write_config(sc->sc_dev, bus, slot, func, reg, addr, 4); 537185089Sraj if (width == 2) 538209131Sraj mv_pcib_write_config(sc->sc_dev, bus, slot, func, reg + 4, 539185089Sraj 0, 4); 540185089Sraj 541185089Sraj return (width); 542185089Sraj} 543185089Sraj 544185089Srajstatic void 545209131Srajmv_pcib_init_bridge(struct mv_pcib_softc *sc, int bus, int slot, int func) 546185089Sraj{ 547185089Sraj bus_addr_t io_base, mem_base; 548185089Sraj uint32_t io_limit, mem_limit; 549185089Sraj int secbus; 550185089Sraj 551209131Sraj io_base = sc->sc_io_base; 552209131Sraj io_limit = io_base + sc->sc_io_size - 1; 553209131Sraj mem_base = sc->sc_mem_base; 554209131Sraj mem_limit = mem_base + sc->sc_mem_size - 1; 555185089Sraj 556185089Sraj /* Configure I/O decode registers */ 557209131Sraj mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_IOBASEL_1, 558185639Sraj io_base >> 8, 1); 559209131Sraj mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_IOBASEH_1, 560185639Sraj io_base >> 16, 2); 561209131Sraj mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_IOLIMITL_1, 562185089Sraj io_limit >> 8, 1); 563209131Sraj mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_IOLIMITH_1, 564185089Sraj io_limit >> 16, 2); 565185089Sraj 566185089Sraj /* Configure memory decode registers */ 567209131Sraj mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_MEMBASE_1, 568185089Sraj mem_base >> 16, 2); 569209131Sraj mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_MEMLIMIT_1, 570185089Sraj mem_limit >> 16, 2); 571185089Sraj 572185089Sraj /* Disable memory prefetch decode */ 573209131Sraj mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_PMBASEL_1, 574185089Sraj 0x10, 2); 575209131Sraj mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_PMBASEH_1, 576185089Sraj 0x0, 4); 577209131Sraj mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_PMLIMITL_1, 578185089Sraj 0xF, 2); 579209131Sraj mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_PMLIMITH_1, 580185089Sraj 0x0, 4); 581185089Sraj 582209131Sraj secbus = mv_pcib_read_config(sc->sc_dev, bus, slot, func, 583185089Sraj PCIR_SECBUS_1, 1); 584185089Sraj 585185089Sraj /* Configure buses behind the bridge */ 586209131Sraj mv_pcib_init(sc, secbus, PCI_SLOTMAX); 587185089Sraj} 588185089Sraj 589185089Srajstatic int 590209131Srajmv_pcib_init(struct mv_pcib_softc *sc, int bus, int maxslot) 591185089Sraj{ 592185089Sraj int slot, func, maxfunc, error; 593185089Sraj uint8_t hdrtype, command, class, subclass; 594185089Sraj 595185089Sraj for (slot = 0; slot <= maxslot; slot++) { 596185089Sraj maxfunc = 0; 597185089Sraj for (func = 0; func <= maxfunc; func++) { 598209131Sraj hdrtype = mv_pcib_read_config(sc->sc_dev, bus, slot, 599185089Sraj func, PCIR_HDRTYPE, 1); 600185089Sraj 601185089Sraj if ((hdrtype & PCIM_HDRTYPE) > PCI_MAXHDRTYPE) 602185089Sraj continue; 603185089Sraj 604185089Sraj if (func == 0 && (hdrtype & PCIM_MFDEV)) 605185089Sraj maxfunc = PCI_FUNCMAX; 606185089Sraj 607209131Sraj command = mv_pcib_read_config(sc->sc_dev, bus, slot, 608185089Sraj func, PCIR_COMMAND, 1); 609185089Sraj command &= ~(PCIM_CMD_MEMEN | PCIM_CMD_PORTEN); 610209131Sraj mv_pcib_write_config(sc->sc_dev, bus, slot, func, 611185089Sraj PCIR_COMMAND, command, 1); 612185089Sraj 613209131Sraj error = mv_pcib_init_all_bars(sc, bus, slot, func, 614185089Sraj hdrtype); 615185089Sraj 616185089Sraj if (error) 617185089Sraj return (error); 618185089Sraj 619185089Sraj command |= PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN | 620185089Sraj PCIM_CMD_PORTEN; 621209131Sraj mv_pcib_write_config(sc->sc_dev, bus, slot, func, 622185089Sraj PCIR_COMMAND, command, 1); 623185089Sraj 624185089Sraj /* Handle PCI-PCI bridges */ 625209131Sraj class = mv_pcib_read_config(sc->sc_dev, bus, slot, 626185089Sraj func, PCIR_CLASS, 1); 627209131Sraj subclass = mv_pcib_read_config(sc->sc_dev, bus, slot, 628185089Sraj func, PCIR_SUBCLASS, 1); 629185089Sraj 630185089Sraj if (class != PCIC_BRIDGE || 631185089Sraj subclass != PCIS_BRIDGE_PCI) 632185089Sraj continue; 633185089Sraj 634209131Sraj mv_pcib_init_bridge(sc, bus, slot, func); 635185089Sraj } 636185089Sraj } 637185089Sraj 638185089Sraj /* Enable all ABCD interrupts */ 639185089Sraj pcib_write_irq_mask(sc, (0xF << 24)); 640185089Sraj 641185089Sraj return (0); 642185089Sraj} 643185089Sraj 644209131Srajstatic int 645209131Srajmv_pcib_init_all_bars(struct mv_pcib_softc *sc, int bus, int slot, 646209131Sraj int func, int hdrtype) 647209131Sraj{ 648209131Sraj int maxbar, bar, i; 649209131Sraj 650209131Sraj maxbar = (hdrtype & PCIM_HDRTYPE) ? 0 : 6; 651209131Sraj bar = 0; 652209131Sraj 653209131Sraj /* Program the base address registers */ 654209131Sraj while (bar < maxbar) { 655209131Sraj i = mv_pcib_init_bar(sc, bus, slot, func, bar); 656209131Sraj bar += i; 657209131Sraj if (i < 0) { 658209131Sraj device_printf(sc->sc_dev, 659209131Sraj "PCI IO/Memory space exhausted\n"); 660209131Sraj return (ENOMEM); 661209131Sraj } 662209131Sraj } 663209131Sraj 664209131Sraj return (0); 665209131Sraj} 666209131Sraj 667185089Srajstatic struct resource * 668209131Srajmv_pcib_alloc_resource(device_t dev, device_t child, int type, int *rid, 669185089Sraj u_long start, u_long end, u_long count, u_int flags) 670185089Sraj{ 671209131Sraj struct mv_pcib_softc *sc = device_get_softc(dev); 672186932Sraj struct rman *rm = NULL; 673186932Sraj struct resource *res; 674185089Sraj 675186932Sraj switch (type) { 676186932Sraj case SYS_RES_IOPORT: 677209131Sraj rm = &sc->sc_io_rman; 678186932Sraj break; 679186932Sraj case SYS_RES_MEMORY: 680209131Sraj rm = &sc->sc_mem_rman; 681186932Sraj break; 682186932Sraj default: 683240489Sgber return (BUS_ALLOC_RESOURCE(device_get_parent(dev), dev, 684186932Sraj type, rid, start, end, count, flags)); 685186932Sraj }; 686186932Sraj 687240489Sgber if ((start == 0UL) && (end == ~0UL)) { 688240489Sgber start = sc->sc_mem_base; 689240489Sgber end = sc->sc_mem_base + sc->sc_mem_size - 1; 690240489Sgber count = sc->sc_mem_size; 691240489Sgber } 692240489Sgber 693240489Sgber if ((start < sc->sc_mem_base) || (start + count - 1 != end) || 694240489Sgber (end > sc->sc_mem_base + sc->sc_mem_size - 1)) 695240489Sgber return (NULL); 696240489Sgber 697186932Sraj res = rman_reserve_resource(rm, start, end, count, flags, child); 698186932Sraj if (res == NULL) 699186932Sraj return (NULL); 700186932Sraj 701186932Sraj rman_set_rid(res, *rid); 702209131Sraj rman_set_bustag(res, fdtbus_bs_tag); 703186932Sraj rman_set_bushandle(res, start); 704186932Sraj 705186932Sraj if (flags & RF_ACTIVE) 706186932Sraj if (bus_activate_resource(child, type, *rid, res)) { 707186932Sraj rman_release_resource(res); 708186932Sraj return (NULL); 709186932Sraj } 710186932Sraj 711186932Sraj return (res); 712185089Sraj} 713185089Sraj 714185089Srajstatic int 715209131Srajmv_pcib_release_resource(device_t dev, device_t child, int type, int rid, 716185089Sraj struct resource *res) 717185089Sraj{ 718185089Sraj 719186932Sraj if (type != SYS_RES_IOPORT && type != SYS_RES_MEMORY) 720186932Sraj return (BUS_RELEASE_RESOURCE(device_get_parent(dev), child, 721186932Sraj type, rid, res)); 722186932Sraj 723186932Sraj return (rman_release_resource(res)); 724185089Sraj} 725185089Sraj 726185089Srajstatic int 727209131Srajmv_pcib_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) 728185089Sraj{ 729209131Sraj struct mv_pcib_softc *sc = device_get_softc(dev); 730185089Sraj 731185089Sraj switch (which) { 732185089Sraj case PCIB_IVAR_BUS: 733185089Sraj *result = sc->sc_busnr; 734185089Sraj return (0); 735185089Sraj case PCIB_IVAR_DOMAIN: 736185089Sraj *result = device_get_unit(dev); 737185089Sraj return (0); 738185089Sraj } 739185089Sraj 740185089Sraj return (ENOENT); 741185089Sraj} 742185089Sraj 743185089Srajstatic int 744209131Srajmv_pcib_write_ivar(device_t dev, device_t child, int which, uintptr_t value) 745185089Sraj{ 746209131Sraj struct mv_pcib_softc *sc = device_get_softc(dev); 747185089Sraj 748185089Sraj switch (which) { 749185089Sraj case PCIB_IVAR_BUS: 750185089Sraj sc->sc_busnr = value; 751185089Sraj return (0); 752185089Sraj } 753185089Sraj 754185089Sraj return (ENOENT); 755185089Sraj} 756209131Sraj 757209131Srajstatic inline void 758209131Srajpcib_write_irq_mask(struct mv_pcib_softc *sc, uint32_t mask) 759209131Sraj{ 760209131Sraj 761209131Sraj if (!sc->sc_type != MV_TYPE_PCI) 762209131Sraj return; 763209131Sraj 764209131Sraj bus_space_write_4(sc->sc_bst, sc->sc_bsh, PCIE_REG_IRQ_MASK, mask); 765209131Sraj} 766209131Sraj 767209131Srajstatic void 768209131Srajmv_pcib_hw_cfginit(void) 769209131Sraj{ 770209131Sraj static int opened = 0; 771209131Sraj 772209131Sraj if (opened) 773209131Sraj return; 774209131Sraj 775209131Sraj mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN); 776209131Sraj opened = 1; 777209131Sraj} 778209131Sraj 779209131Srajstatic uint32_t 780209131Srajmv_pcib_hw_cfgread(struct mv_pcib_softc *sc, u_int bus, u_int slot, 781209131Sraj u_int func, u_int reg, int bytes) 782209131Sraj{ 783209131Sraj uint32_t addr, data, ca, cd; 784209131Sraj 785209131Sraj ca = (sc->sc_type != MV_TYPE_PCI) ? 786209131Sraj PCIE_REG_CFG_ADDR : PCI_REG_CFG_ADDR; 787209131Sraj cd = (sc->sc_type != MV_TYPE_PCI) ? 788209131Sraj PCIE_REG_CFG_DATA : PCI_REG_CFG_DATA; 789209131Sraj addr = PCI_CFG_ENA | PCI_CFG_BUS(bus) | PCI_CFG_DEV(slot) | 790209131Sraj PCI_CFG_FUN(func) | PCI_CFG_PCIE_REG(reg); 791209131Sraj 792209131Sraj mtx_lock_spin(&pcicfg_mtx); 793209131Sraj bus_space_write_4(sc->sc_bst, sc->sc_bsh, ca, addr); 794209131Sraj 795209131Sraj data = ~0; 796209131Sraj switch (bytes) { 797209131Sraj case 1: 798209131Sraj data = bus_space_read_1(sc->sc_bst, sc->sc_bsh, 799209131Sraj cd + (reg & 3)); 800209131Sraj break; 801209131Sraj case 2: 802209131Sraj data = le16toh(bus_space_read_2(sc->sc_bst, sc->sc_bsh, 803209131Sraj cd + (reg & 2))); 804209131Sraj break; 805209131Sraj case 4: 806209131Sraj data = le32toh(bus_space_read_4(sc->sc_bst, sc->sc_bsh, 807209131Sraj cd)); 808209131Sraj break; 809209131Sraj } 810209131Sraj mtx_unlock_spin(&pcicfg_mtx); 811209131Sraj return (data); 812209131Sraj} 813209131Sraj 814209131Srajstatic void 815209131Srajmv_pcib_hw_cfgwrite(struct mv_pcib_softc *sc, u_int bus, u_int slot, 816209131Sraj u_int func, u_int reg, uint32_t data, int bytes) 817209131Sraj{ 818209131Sraj uint32_t addr, ca, cd; 819209131Sraj 820209131Sraj ca = (sc->sc_type != MV_TYPE_PCI) ? 821209131Sraj PCIE_REG_CFG_ADDR : PCI_REG_CFG_ADDR; 822209131Sraj cd = (sc->sc_type != MV_TYPE_PCI) ? 823209131Sraj PCIE_REG_CFG_DATA : PCI_REG_CFG_DATA; 824209131Sraj addr = PCI_CFG_ENA | PCI_CFG_BUS(bus) | PCI_CFG_DEV(slot) | 825209131Sraj PCI_CFG_FUN(func) | PCI_CFG_PCIE_REG(reg); 826209131Sraj 827209131Sraj mtx_lock_spin(&pcicfg_mtx); 828209131Sraj bus_space_write_4(sc->sc_bst, sc->sc_bsh, ca, addr); 829209131Sraj 830209131Sraj switch (bytes) { 831209131Sraj case 1: 832209131Sraj bus_space_write_1(sc->sc_bst, sc->sc_bsh, 833209131Sraj cd + (reg & 3), data); 834209131Sraj break; 835209131Sraj case 2: 836209131Sraj bus_space_write_2(sc->sc_bst, sc->sc_bsh, 837209131Sraj cd + (reg & 2), htole16(data)); 838209131Sraj break; 839209131Sraj case 4: 840209131Sraj bus_space_write_4(sc->sc_bst, sc->sc_bsh, 841209131Sraj cd, htole32(data)); 842209131Sraj break; 843209131Sraj } 844209131Sraj mtx_unlock_spin(&pcicfg_mtx); 845209131Sraj} 846209131Sraj 847209131Srajstatic int 848209131Srajmv_pcib_maxslots(device_t dev) 849209131Sraj{ 850209131Sraj struct mv_pcib_softc *sc = device_get_softc(dev); 851209131Sraj 852209131Sraj return ((sc->sc_type != MV_TYPE_PCI) ? 1 : PCI_SLOTMAX); 853209131Sraj} 854209131Sraj 855209131Srajstatic uint32_t 856209131Srajmv_pcib_read_config(device_t dev, u_int bus, u_int slot, u_int func, 857209131Sraj u_int reg, int bytes) 858209131Sraj{ 859209131Sraj struct mv_pcib_softc *sc = device_get_softc(dev); 860209131Sraj 861240489Sgber /* Return ~0 if link is inactive or trying to read from Root */ 862240489Sgber if ((bus_space_read_4(sc->sc_bst, sc->sc_bsh, PCIE_REG_STATUS) & 863240489Sgber PCIE_STATUS_LINK_DOWN) || (slot == 0)) 864209131Sraj return (~0U); 865209131Sraj 866209131Sraj return (mv_pcib_hw_cfgread(sc, bus, slot, func, reg, bytes)); 867209131Sraj} 868209131Sraj 869209131Srajstatic void 870209131Srajmv_pcib_write_config(device_t dev, u_int bus, u_int slot, u_int func, 871209131Sraj u_int reg, uint32_t val, int bytes) 872209131Sraj{ 873209131Sraj struct mv_pcib_softc *sc = device_get_softc(dev); 874209131Sraj 875240489Sgber /* Return if link is inactive or trying to write to Root */ 876240489Sgber if ((bus_space_read_4(sc->sc_bst, sc->sc_bsh, PCIE_REG_STATUS) & 877240489Sgber PCIE_STATUS_LINK_DOWN) || (slot == 0)) 878209131Sraj return; 879209131Sraj 880209131Sraj mv_pcib_hw_cfgwrite(sc, bus, slot, func, reg, val, bytes); 881209131Sraj} 882209131Sraj 883209131Srajstatic int 884209131Srajmv_pcib_route_interrupt(device_t pcib, device_t dev, int pin) 885209131Sraj{ 886209131Sraj struct mv_pcib_softc *sc; 887209131Sraj int err, interrupt; 888209131Sraj 889209131Sraj sc = device_get_softc(pcib); 890209131Sraj 891209131Sraj err = fdt_pci_route_intr(pci_get_bus(dev), pci_get_slot(dev), 892209131Sraj pci_get_function(dev), pin, &sc->sc_intr_info, &interrupt); 893209131Sraj if (err == 0) 894209131Sraj return (interrupt); 895209131Sraj 896209131Sraj device_printf(pcib, "could not route pin %d for device %d.%d\n", 897209131Sraj pin, pci_get_slot(dev), pci_get_function(dev)); 898209131Sraj return (PCI_INVALID_IRQ); 899209131Sraj} 900209131Sraj 901209131Srajstatic int 902209131Srajmv_pcib_decode_win(phandle_t node, struct mv_pcib_softc *sc) 903209131Sraj{ 904209131Sraj struct fdt_pci_range io_space, mem_space; 905209131Sraj device_t dev; 906209131Sraj int error; 907209131Sraj 908209131Sraj dev = sc->sc_dev; 909209131Sraj 910209131Sraj if ((error = fdt_pci_ranges(node, &io_space, &mem_space)) != 0) { 911209131Sraj device_printf(dev, "could not retrieve 'ranges' data\n"); 912209131Sraj return (error); 913209131Sraj } 914209131Sraj 915209131Sraj /* Configure CPU decoding windows */ 916240489Sgber error = decode_win_cpu_set(sc->sc_win_target, 917240489Sgber sc->sc_io_win_attr, io_space.base_parent, io_space.len, ~0); 918209131Sraj if (error < 0) { 919209131Sraj device_printf(dev, "could not set up CPU decode " 920209131Sraj "window for PCI IO\n"); 921209131Sraj return (ENXIO); 922209131Sraj } 923240489Sgber error = decode_win_cpu_set(sc->sc_win_target, 924240489Sgber sc->sc_mem_win_attr, mem_space.base_parent, mem_space.len, 925240489Sgber mem_space.base_parent); 926209131Sraj if (error < 0) { 927209131Sraj device_printf(dev, "could not set up CPU decode " 928209131Sraj "windows for PCI MEM\n"); 929209131Sraj return (ENXIO); 930209131Sraj } 931209131Sraj 932209131Sraj sc->sc_io_base = io_space.base_parent; 933209131Sraj sc->sc_io_size = io_space.len; 934209131Sraj 935209131Sraj sc->sc_mem_base = mem_space.base_parent; 936209131Sraj sc->sc_mem_size = mem_space.len; 937209131Sraj 938209131Sraj return (0); 939209131Sraj} 940209131Sraj 941209131Srajstatic int 942209131Srajmv_pcib_intr_info(phandle_t node, struct mv_pcib_softc *sc) 943209131Sraj{ 944209131Sraj int error; 945209131Sraj 946209131Sraj if ((error = fdt_pci_intr_info(node, &sc->sc_intr_info)) != 0) 947209131Sraj return (error); 948209131Sraj 949209131Sraj return (0); 950209131Sraj} 951209131Sraj 952240493Sgber#if defined(SOC_MV_ARMADAXP) 953240493Sgberstatic int 954240493Sgbermv_pcib_map_msi(device_t dev, device_t child, int irq, uint64_t *addr, 955240493Sgber uint32_t *data) 956240493Sgber{ 957240493Sgber struct mv_pcib_softc *sc; 958240493Sgber 959240493Sgber sc = device_get_softc(dev); 960240493Sgber irq = irq - MSI_IRQ; 961240493Sgber 962240493Sgber /* validate parameters */ 963240493Sgber if (isclr(&sc->sc_msi_bitmap, irq)) { 964240493Sgber device_printf(dev, "invalid MSI 0x%x\n", irq); 965240493Sgber return (EINVAL); 966240493Sgber } 967240493Sgber 968240493Sgber mv_msi_data(irq, addr, data); 969240493Sgber 970240493Sgber debugf("%s: irq: %d addr: %jx data: %x\n", 971240493Sgber __func__, irq, *addr, *data); 972240493Sgber 973240493Sgber return (0); 974240493Sgber} 975240493Sgber 976240493Sgberstatic int 977240493Sgbermv_pcib_alloc_msi(device_t dev, device_t child, int count, 978240493Sgber int maxcount __unused, int *irqs) 979240493Sgber{ 980240493Sgber struct mv_pcib_softc *sc; 981240493Sgber u_int start = 0, i; 982240493Sgber 983240493Sgber if (powerof2(count) == 0 || count > MSI_IRQ_NUM) 984240493Sgber return (EINVAL); 985240493Sgber 986240493Sgber sc = device_get_softc(dev); 987240493Sgber mtx_lock(&sc->sc_msi_mtx); 988240493Sgber 989240493Sgber for (start = 0; (start + count) < MSI_IRQ_NUM; start++) { 990240493Sgber for (i = start; i < start + count; i++) { 991240493Sgber if (isset(&sc->sc_msi_bitmap, i)) 992240493Sgber break; 993240493Sgber } 994240493Sgber if (i == start + count) 995240493Sgber break; 996240493Sgber } 997240493Sgber 998240493Sgber if ((start + count) == MSI_IRQ_NUM) { 999240493Sgber mtx_unlock(&sc->sc_msi_mtx); 1000240493Sgber return (ENXIO); 1001240493Sgber } 1002240493Sgber 1003240493Sgber for (i = start; i < start + count; i++) { 1004240493Sgber setbit(&sc->sc_msi_bitmap, i); 1005240493Sgber irqs[i] = MSI_IRQ + i; 1006240493Sgber } 1007240493Sgber debugf("%s: start: %x count: %x\n", __func__, start, count); 1008240493Sgber 1009240493Sgber mtx_unlock(&sc->sc_msi_mtx); 1010240493Sgber return (0); 1011240493Sgber} 1012240493Sgber 1013240493Sgberstatic int 1014240493Sgbermv_pcib_release_msi(device_t dev, device_t child, int count, int *irqs) 1015240493Sgber{ 1016240493Sgber struct mv_pcib_softc *sc; 1017240493Sgber u_int i; 1018240493Sgber 1019240493Sgber sc = device_get_softc(dev); 1020240493Sgber mtx_lock(&sc->sc_msi_mtx); 1021240493Sgber 1022240493Sgber for (i = 0; i < count; i++) 1023240493Sgber clrbit(&sc->sc_msi_bitmap, irqs[i] - MSI_IRQ); 1024240493Sgber 1025240493Sgber mtx_unlock(&sc->sc_msi_mtx); 1026240493Sgber return (0); 1027240493Sgber} 1028240493Sgber#endif 1029