mv_pci.c revision 240489
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 240489 2012-09-14 09:57:41Z 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 56185089Sraj#include <vm/vm.h> 57185089Sraj#include <vm/pmap.h> 58185089Sraj 59209131Sraj#include <dev/fdt/fdt_common.h> 60209131Sraj#include <dev/ofw/ofw_bus.h> 61209131Sraj#include <dev/ofw/ofw_bus_subr.h> 62185089Sraj#include <dev/pci/pcivar.h> 63185089Sraj#include <dev/pci/pcireg.h> 64185089Sraj#include <dev/pci/pcib_private.h> 65185089Sraj 66209131Sraj#include "ofw_bus_if.h" 67185089Sraj#include "pcib_if.h" 68185089Sraj 69185089Sraj#include <machine/resource.h> 70185089Sraj#include <machine/bus.h> 71185089Sraj 72185089Sraj#include <arm/mv/mvreg.h> 73185089Sraj#include <arm/mv/mvvar.h> 74209131Sraj#include <arm/mv/mvwin.h> 75185089Sraj 76185089Sraj#define PCI_CFG_ENA (1 << 31) 77185089Sraj#define PCI_CFG_BUS(bus) (((bus) & 0xff) << 16) 78185089Sraj#define PCI_CFG_DEV(dev) (((dev) & 0x1f) << 11) 79185089Sraj#define PCI_CFG_FUN(fun) (((fun) & 0x7) << 8) 80185089Sraj#define PCI_CFG_PCIE_REG(reg) ((reg) & 0xfc) 81185089Sraj 82185089Sraj#define PCI_REG_CFG_ADDR 0x0C78 83185089Sraj#define PCI_REG_CFG_DATA 0x0C7C 84185089Sraj#define PCI_REG_P2P_CONF 0x1D14 85185089Sraj 86185089Sraj#define PCIE_REG_CFG_ADDR 0x18F8 87185089Sraj#define PCIE_REG_CFG_DATA 0x18FC 88185089Sraj#define PCIE_REG_CONTROL 0x1A00 89185089Sraj#define PCIE_CTRL_LINK1X 0x00000001 90185089Sraj#define PCIE_REG_STATUS 0x1A04 91185089Sraj#define PCIE_REG_IRQ_MASK 0x1910 92185089Sraj 93240489Sgber#define PCIE_CONTROL_ROOT_CMPLX (1 << 1) 94240489Sgber#define PCIE_CONTROL_HOT_RESET (1 << 24) 95185089Sraj 96240489Sgber#define PCIE_LINK_TIMEOUT 1000000 97185089Sraj 98240489Sgber#define PCIE_STATUS_LINK_DOWN 1 99240489Sgber#define PCIE_STATUS_DEV_OFFS 16 100185089Sraj 101240489Sgber/* Minimum PCI Memory and I/O allocations taken from PCI spec (in bytes) */ 102240489Sgber#define PCI_MIN_IO_ALLOC 4 103240489Sgber#define PCI_MIN_MEM_ALLOC 16 104240489Sgber 105240489Sgber#define BITS_PER_UINT32 (NBBY * sizeof(uint32_t)) 106240489Sgber 107209131Srajstruct mv_pcib_softc { 108185089Sraj device_t sc_dev; 109185089Sraj 110209131Sraj struct rman sc_mem_rman; 111209131Sraj bus_addr_t sc_mem_base; 112209131Sraj bus_addr_t sc_mem_size; 113240489Sgber uint32_t sc_mem_map[MV_PCI_MEM_SLICE_SIZE / 114240489Sgber (PCI_MIN_MEM_ALLOC * BITS_PER_UINT32)]; 115240489Sgber int sc_win_target; 116209131Sraj int sc_mem_win_attr; 117185089Sraj 118209131Sraj struct rman sc_io_rman; 119209131Sraj bus_addr_t sc_io_base; 120209131Sraj bus_addr_t sc_io_size; 121240489Sgber uint32_t sc_io_map[MV_PCI_IO_SLICE_SIZE / 122240489Sgber (PCI_MIN_IO_ALLOC * BITS_PER_UINT32)]; 123209131Sraj int sc_io_win_attr; 124185089Sraj 125185089Sraj struct resource *sc_res; 126185089Sraj bus_space_handle_t sc_bsh; 127185089Sraj bus_space_tag_t sc_bst; 128185089Sraj int sc_rid; 129185089Sraj 130185089Sraj int sc_busnr; /* Host bridge bus number */ 131185089Sraj int sc_devnr; /* Host bridge device number */ 132209131Sraj int sc_type; 133240489Sgber int sc_mode; /* Endpoint / Root Complex */ 134185089Sraj 135209131Sraj struct fdt_pci_intr sc_intr_info; 136185089Sraj}; 137185089Sraj 138209131Sraj/* Local forward prototypes */ 139209131Srajstatic int mv_pcib_decode_win(phandle_t, struct mv_pcib_softc *); 140209131Srajstatic void mv_pcib_hw_cfginit(void); 141209131Srajstatic uint32_t mv_pcib_hw_cfgread(struct mv_pcib_softc *, u_int, u_int, 142209131Sraj u_int, u_int, int); 143209131Srajstatic void mv_pcib_hw_cfgwrite(struct mv_pcib_softc *, u_int, u_int, 144209131Sraj u_int, u_int, uint32_t, int); 145209131Srajstatic int mv_pcib_init(struct mv_pcib_softc *, int, int); 146209131Srajstatic int mv_pcib_init_all_bars(struct mv_pcib_softc *, int, int, int, int); 147209131Srajstatic void mv_pcib_init_bridge(struct mv_pcib_softc *, int, int, int); 148209131Srajstatic int mv_pcib_intr_info(phandle_t, struct mv_pcib_softc *); 149209131Srajstatic inline void pcib_write_irq_mask(struct mv_pcib_softc *, uint32_t); 150240489Sgberstatic void mv_pcib_enable(struct mv_pcib_softc *, uint32_t); 151240489Sgberstatic int mv_pcib_mem_init(struct mv_pcib_softc *); 152185089Sraj 153209131Sraj/* Forward prototypes */ 154209131Srajstatic int mv_pcib_probe(device_t); 155209131Srajstatic int mv_pcib_attach(device_t); 156209131Sraj 157209131Srajstatic struct resource *mv_pcib_alloc_resource(device_t, device_t, int, int *, 158185089Sraj u_long, u_long, u_long, u_int); 159209131Srajstatic int mv_pcib_release_resource(device_t, device_t, int, int, 160185089Sraj struct resource *); 161209131Srajstatic int mv_pcib_read_ivar(device_t, device_t, int, uintptr_t *); 162209131Srajstatic int mv_pcib_write_ivar(device_t, device_t, int, uintptr_t); 163185089Sraj 164209131Srajstatic int mv_pcib_maxslots(device_t); 165209131Srajstatic uint32_t mv_pcib_read_config(device_t, u_int, u_int, u_int, u_int, int); 166209131Srajstatic void mv_pcib_write_config(device_t, u_int, u_int, u_int, u_int, 167185089Sraj uint32_t, int); 168209131Srajstatic int mv_pcib_route_interrupt(device_t, device_t, int); 169185089Sraj 170185089Sraj/* 171185089Sraj * Bus interface definitions. 172185089Sraj */ 173209131Srajstatic device_method_t mv_pcib_methods[] = { 174185089Sraj /* Device interface */ 175209131Sraj DEVMETHOD(device_probe, mv_pcib_probe), 176209131Sraj DEVMETHOD(device_attach, mv_pcib_attach), 177185089Sraj 178185089Sraj /* Bus interface */ 179209131Sraj DEVMETHOD(bus_read_ivar, mv_pcib_read_ivar), 180209131Sraj DEVMETHOD(bus_write_ivar, mv_pcib_write_ivar), 181209131Sraj DEVMETHOD(bus_alloc_resource, mv_pcib_alloc_resource), 182209131Sraj DEVMETHOD(bus_release_resource, mv_pcib_release_resource), 183185089Sraj DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 184185089Sraj DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 185185089Sraj DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 186185089Sraj DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 187185089Sraj 188185089Sraj /* pcib interface */ 189209131Sraj DEVMETHOD(pcib_maxslots, mv_pcib_maxslots), 190209131Sraj DEVMETHOD(pcib_read_config, mv_pcib_read_config), 191209131Sraj DEVMETHOD(pcib_write_config, mv_pcib_write_config), 192209131Sraj DEVMETHOD(pcib_route_interrupt, mv_pcib_route_interrupt), 193240489Sgber 194209131Sraj /* OFW bus interface */ 195209131Sraj DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), 196209131Sraj DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), 197209131Sraj DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), 198209131Sraj DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), 199209131Sraj DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), 200209131Sraj 201227843Smarius DEVMETHOD_END 202185089Sraj}; 203185089Sraj 204209131Srajstatic driver_t mv_pcib_driver = { 205185089Sraj "pcib", 206209131Sraj mv_pcib_methods, 207209131Sraj sizeof(struct mv_pcib_softc), 208185089Sraj}; 209185089Sraj 210185089Srajdevclass_t pcib_devclass; 211185089Sraj 212209131SrajDRIVER_MODULE(pcib, fdtbus, mv_pcib_driver, pcib_devclass, 0, 0); 213185089Sraj 214185089Srajstatic struct mtx pcicfg_mtx; 215185089Sraj 216185089Srajstatic int 217209131Srajmv_pcib_probe(device_t self) 218185089Sraj{ 219218228Smarcel phandle_t node; 220185089Sraj 221218228Smarcel node = ofw_bus_get_node(self); 222218228Smarcel if (!fdt_is_type(node, "pci")) 223209131Sraj return (ENXIO); 224218228Smarcel 225218228Smarcel if (!(fdt_is_compatible(node, "mrvl,pcie") || 226218228Smarcel fdt_is_compatible(node, "mrvl,pci"))) 227209131Sraj return (ENXIO); 228185089Sraj 229209131Sraj device_set_desc(self, "Marvell Integrated PCI/PCI-E Controller"); 230209131Sraj return (BUS_PROBE_DEFAULT); 231185089Sraj} 232185089Sraj 233185089Srajstatic int 234209131Srajmv_pcib_attach(device_t self) 235185089Sraj{ 236209131Sraj struct mv_pcib_softc *sc; 237209131Sraj phandle_t node, parnode; 238240489Sgber uint32_t val, unit; 239209131Sraj int err; 240185089Sraj 241185089Sraj sc = device_get_softc(self); 242209131Sraj sc->sc_dev = self; 243240489Sgber unit = fdt_get_unit(self); 244185089Sraj 245240489Sgber 246218228Smarcel node = ofw_bus_get_node(self); 247218228Smarcel parnode = OF_parent(node); 248218228Smarcel if (fdt_is_compatible(node, "mrvl,pcie")) { 249209131Sraj sc->sc_type = MV_TYPE_PCIE; 250240489Sgber sc->sc_win_target = MV_WIN_PCIE_TARGET(unit); 251240489Sgber sc->sc_mem_win_attr = MV_WIN_PCIE_MEM_ATTR(unit); 252240489Sgber sc->sc_io_win_attr = MV_WIN_PCIE_IO_ATTR(unit); 253218228Smarcel } else if (fdt_is_compatible(node, "mrvl,pci")) { 254209131Sraj sc->sc_type = MV_TYPE_PCI; 255240489Sgber sc->sc_win_target = MV_WIN_PCI_TARGET; 256209131Sraj sc->sc_mem_win_attr = MV_WIN_PCI_MEM_ATTR; 257209131Sraj sc->sc_io_win_attr = MV_WIN_PCI_IO_ATTR; 258209131Sraj } else 259185089Sraj return (ENXIO); 260185089Sraj 261209131Sraj /* 262209131Sraj * Retrieve our mem-mapped registers range. 263209131Sraj */ 264185089Sraj sc->sc_rid = 0; 265185089Sraj sc->sc_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &sc->sc_rid, 266185089Sraj RF_ACTIVE); 267185089Sraj if (sc->sc_res == NULL) { 268209131Sraj device_printf(self, "could not map memory\n"); 269185089Sraj return (ENXIO); 270185089Sraj } 271185089Sraj sc->sc_bst = rman_get_bustag(sc->sc_res); 272185089Sraj sc->sc_bsh = rman_get_bushandle(sc->sc_res); 273185089Sraj 274240489Sgber val = bus_space_read_4(sc->sc_bst, sc->sc_bsh, PCIE_REG_CONTROL); 275240489Sgber sc->sc_mode = (val & PCIE_CONTROL_ROOT_CMPLX ? MV_MODE_ROOT : 276240489Sgber MV_MODE_ENDPOINT); 277240489Sgber 278209131Sraj /* 279240489Sgber * Get PCI interrupt info. 280240489Sgber */ 281240489Sgber if ((sc->sc_mode == MV_MODE_ROOT) && 282240489Sgber (mv_pcib_intr_info(node, sc) != 0)) { 283240489Sgber device_printf(self, "could not retrieve interrupt info\n"); 284240489Sgber return (ENXIO); 285240489Sgber } 286240489Sgber 287240489Sgber /* 288209131Sraj * Configure decode windows for PCI(E) access. 289209131Sraj */ 290209131Sraj if (mv_pcib_decode_win(node, sc) != 0) 291209131Sraj return (ENXIO); 292209131Sraj 293209131Sraj mv_pcib_hw_cfginit(); 294209131Sraj 295209131Sraj /* 296240489Sgber * Enable PCIE device. 297209131Sraj */ 298240489Sgber mv_pcib_enable(sc, unit); 299185089Sraj 300240489Sgber /* 301240489Sgber * Memory management. 302240489Sgber */ 303240489Sgber err = mv_pcib_mem_init(sc); 304240489Sgber if (err) 305240489Sgber return (err); 306185089Sraj 307240489Sgber if (sc->sc_mode == MV_MODE_ROOT) { 308240489Sgber err = mv_pcib_init(sc, sc->sc_busnr, 309240489Sgber mv_pcib_maxslots(sc->sc_dev)); 310240489Sgber if (err) 311240489Sgber goto error; 312240489Sgber 313240489Sgber device_add_child(self, "pci", -1); 314240489Sgber } else { 315240489Sgber sc->sc_devnr = 1; 316240489Sgber bus_space_write_4(sc->sc_bst, sc->sc_bsh, 317240489Sgber PCIE_REG_STATUS, 1 << PCIE_STATUS_DEV_OFFS); 318240489Sgber device_add_child(self, "pci_ep", -1); 319240489Sgber } 320240489Sgber 321240489Sgber return (bus_generic_attach(self)); 322240489Sgber 323240489Sgbererror: 324240489Sgber /* XXX SYS_RES_ should be released here */ 325240489Sgber rman_fini(&sc->sc_mem_rman); 326240489Sgber rman_fini(&sc->sc_io_rman); 327240489Sgber 328240489Sgber return (err); 329240489Sgber} 330240489Sgber 331240489Sgberstatic void 332240489Sgbermv_pcib_enable(struct mv_pcib_softc *sc, uint32_t unit) 333240489Sgber{ 334240489Sgber uint32_t val; 335240489Sgber#if !defined(SOC_MV_ARMADAXP) 336240489Sgber int timeout; 337240489Sgber 338240489Sgber /* 339240489Sgber * Check if PCIE device is enabled. 340240489Sgber */ 341240489Sgber if (read_cpu_ctrl(CPU_CONTROL) & CPU_CONTROL_PCIE_DISABLE(unit)) { 342240489Sgber write_cpu_ctrl(CPU_CONTROL, read_cpu_ctrl(CPU_CONTROL) & 343240489Sgber ~(CPU_CONTROL_PCIE_DISABLE(unit))); 344240489Sgber 345240489Sgber timeout = PCIE_LINK_TIMEOUT; 346240489Sgber val = bus_space_read_4(sc->sc_bst, sc->sc_bsh, 347240489Sgber PCIE_REG_STATUS); 348240489Sgber while (((val & PCIE_STATUS_LINK_DOWN) == 1) && (timeout > 0)) { 349240489Sgber DELAY(1000); 350240489Sgber timeout -= 1000; 351240489Sgber val = bus_space_read_4(sc->sc_bst, sc->sc_bsh, 352240489Sgber PCIE_REG_STATUS); 353240489Sgber } 354240489Sgber } 355240489Sgber#endif 356240489Sgber 357240489Sgber 358240489Sgber if (sc->sc_mode == MV_MODE_ROOT) { 359240489Sgber /* 360240489Sgber * Enable PCI bridge. 361240489Sgber */ 362240489Sgber val = bus_space_read_4(sc->sc_bst, sc->sc_bsh, PCIR_COMMAND); 363240489Sgber val |= PCIM_CMD_SERRESPEN | PCIM_CMD_BUSMASTEREN | 364240489Sgber PCIM_CMD_MEMEN | PCIM_CMD_PORTEN; 365240489Sgber bus_space_write_4(sc->sc_bst, sc->sc_bsh, PCIR_COMMAND, val); 366240489Sgber } 367240489Sgber} 368240489Sgber 369240489Sgberstatic int 370240489Sgbermv_pcib_mem_init(struct mv_pcib_softc *sc) 371240489Sgber{ 372240489Sgber int err; 373240489Sgber 374240489Sgber /* 375240489Sgber * Memory management. 376240489Sgber */ 377209131Sraj sc->sc_mem_rman.rm_type = RMAN_ARRAY; 378209131Sraj err = rman_init(&sc->sc_mem_rman); 379186932Sraj if (err) 380186932Sraj return (err); 381186932Sraj 382209131Sraj sc->sc_io_rman.rm_type = RMAN_ARRAY; 383209131Sraj err = rman_init(&sc->sc_io_rman); 384186932Sraj if (err) { 385209131Sraj rman_fini(&sc->sc_mem_rman); 386186932Sraj return (err); 387186932Sraj } 388186932Sraj 389209131Sraj err = rman_manage_region(&sc->sc_mem_rman, sc->sc_mem_base, 390209131Sraj sc->sc_mem_base + sc->sc_mem_size - 1); 391186932Sraj if (err) 392186932Sraj goto error; 393186932Sraj 394209131Sraj err = rman_manage_region(&sc->sc_io_rman, sc->sc_io_base, 395209131Sraj sc->sc_io_base + sc->sc_io_size - 1); 396186932Sraj if (err) 397186932Sraj goto error; 398186932Sraj 399240489Sgber return (0); 400185089Sraj 401186932Srajerror: 402209131Sraj rman_fini(&sc->sc_mem_rman); 403209131Sraj rman_fini(&sc->sc_io_rman); 404240489Sgber 405186932Sraj return (err); 406185089Sraj} 407185089Sraj 408240489Sgberstatic inline uint32_t 409240489Sgberpcib_bit_get(uint32_t *map, uint32_t bit) 410240489Sgber{ 411240489Sgber uint32_t n = bit / BITS_PER_UINT32; 412240489Sgber 413240489Sgber bit = bit % BITS_PER_UINT32; 414240489Sgber return (map[n] & (1 << bit)); 415240489Sgber} 416240489Sgber 417240489Sgberstatic inline void 418240489Sgberpcib_bit_set(uint32_t *map, uint32_t bit) 419240489Sgber{ 420240489Sgber uint32_t n = bit / BITS_PER_UINT32; 421240489Sgber 422240489Sgber bit = bit % BITS_PER_UINT32; 423240489Sgber map[n] |= (1 << bit); 424240489Sgber} 425240489Sgber 426240489Sgberstatic inline uint32_t 427240489Sgberpcib_map_check(uint32_t *map, uint32_t start, uint32_t bits) 428240489Sgber{ 429240489Sgber uint32_t i; 430240489Sgber 431240489Sgber for (i = start; i < start + bits; i++) 432240489Sgber if (pcib_bit_get(map, i)) 433240489Sgber return (0); 434240489Sgber 435240489Sgber return (1); 436240489Sgber} 437240489Sgber 438240489Sgberstatic inline void 439240489Sgberpcib_map_set(uint32_t *map, uint32_t start, uint32_t bits) 440240489Sgber{ 441240489Sgber uint32_t i; 442240489Sgber 443240489Sgber for (i = start; i < start + bits; i++) 444240489Sgber pcib_bit_set(map, i); 445240489Sgber} 446240489Sgber 447240489Sgber/* 448240489Sgber * The idea of this allocator is taken from ARM No-Cache memory 449240489Sgber * management code (sys/arm/arm/vm_machdep.c). 450240489Sgber */ 451240489Sgberstatic bus_addr_t 452240489Sgberpcib_alloc(struct mv_pcib_softc *sc, uint32_t smask) 453240489Sgber{ 454240489Sgber uint32_t bits, bits_limit, i, *map, min_alloc, size; 455240489Sgber bus_addr_t addr = 0; 456240489Sgber bus_addr_t base; 457240489Sgber 458240489Sgber if (smask & 1) { 459240489Sgber base = sc->sc_io_base; 460240489Sgber min_alloc = PCI_MIN_IO_ALLOC; 461240489Sgber bits_limit = sc->sc_io_size / min_alloc; 462240489Sgber map = sc->sc_io_map; 463240489Sgber smask &= ~0x3; 464240489Sgber } else { 465240489Sgber base = sc->sc_mem_base; 466240489Sgber min_alloc = PCI_MIN_MEM_ALLOC; 467240489Sgber bits_limit = sc->sc_mem_size / min_alloc; 468240489Sgber map = sc->sc_mem_map; 469240489Sgber smask &= ~0xF; 470240489Sgber } 471240489Sgber 472240489Sgber size = ~smask + 1; 473240489Sgber bits = size / min_alloc; 474240489Sgber 475240489Sgber for (i = 0; i + bits <= bits_limit; i += bits) 476240489Sgber if (pcib_map_check(map, i, bits)) { 477240489Sgber pcib_map_set(map, i, bits); 478240489Sgber addr = base + (i * min_alloc); 479240489Sgber return (addr); 480240489Sgber } 481240489Sgber 482240489Sgber return (addr); 483240489Sgber} 484240489Sgber 485185089Srajstatic int 486209131Srajmv_pcib_init_bar(struct mv_pcib_softc *sc, int bus, int slot, int func, 487185089Sraj int barno) 488185089Sraj{ 489240489Sgber uint32_t addr, bar; 490185089Sraj int reg, width; 491185089Sraj 492185089Sraj reg = PCIR_BAR(barno); 493240489Sgber 494240489Sgber /* 495240489Sgber * Need to init the BAR register with 0xffffffff before correct 496240489Sgber * value can be read. 497240489Sgber */ 498240489Sgber mv_pcib_write_config(sc->sc_dev, bus, slot, func, reg, ~0, 4); 499209131Sraj bar = mv_pcib_read_config(sc->sc_dev, bus, slot, func, reg, 4); 500185089Sraj if (bar == 0) 501185089Sraj return (1); 502185089Sraj 503185089Sraj /* Calculate BAR size: 64 or 32 bit (in 32-bit units) */ 504185089Sraj width = ((bar & 7) == 4) ? 2 : 1; 505185089Sraj 506240489Sgber addr = pcib_alloc(sc, bar); 507240489Sgber if (!addr) 508185089Sraj return (-1); 509185089Sraj 510185089Sraj if (bootverbose) 511240489Sgber printf("PCI %u:%u:%u: reg %x: smask=%08x: addr=%08x\n", 512240489Sgber bus, slot, func, reg, bar, addr); 513185089Sraj 514209131Sraj mv_pcib_write_config(sc->sc_dev, bus, slot, func, reg, addr, 4); 515185089Sraj if (width == 2) 516209131Sraj mv_pcib_write_config(sc->sc_dev, bus, slot, func, reg + 4, 517185089Sraj 0, 4); 518185089Sraj 519185089Sraj return (width); 520185089Sraj} 521185089Sraj 522185089Srajstatic void 523209131Srajmv_pcib_init_bridge(struct mv_pcib_softc *sc, int bus, int slot, int func) 524185089Sraj{ 525185089Sraj bus_addr_t io_base, mem_base; 526185089Sraj uint32_t io_limit, mem_limit; 527185089Sraj int secbus; 528185089Sraj 529209131Sraj io_base = sc->sc_io_base; 530209131Sraj io_limit = io_base + sc->sc_io_size - 1; 531209131Sraj mem_base = sc->sc_mem_base; 532209131Sraj mem_limit = mem_base + sc->sc_mem_size - 1; 533185089Sraj 534185089Sraj /* Configure I/O decode registers */ 535209131Sraj mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_IOBASEL_1, 536185639Sraj io_base >> 8, 1); 537209131Sraj mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_IOBASEH_1, 538185639Sraj io_base >> 16, 2); 539209131Sraj mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_IOLIMITL_1, 540185089Sraj io_limit >> 8, 1); 541209131Sraj mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_IOLIMITH_1, 542185089Sraj io_limit >> 16, 2); 543185089Sraj 544185089Sraj /* Configure memory decode registers */ 545209131Sraj mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_MEMBASE_1, 546185089Sraj mem_base >> 16, 2); 547209131Sraj mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_MEMLIMIT_1, 548185089Sraj mem_limit >> 16, 2); 549185089Sraj 550185089Sraj /* Disable memory prefetch decode */ 551209131Sraj mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_PMBASEL_1, 552185089Sraj 0x10, 2); 553209131Sraj mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_PMBASEH_1, 554185089Sraj 0x0, 4); 555209131Sraj mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_PMLIMITL_1, 556185089Sraj 0xF, 2); 557209131Sraj mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_PMLIMITH_1, 558185089Sraj 0x0, 4); 559185089Sraj 560209131Sraj secbus = mv_pcib_read_config(sc->sc_dev, bus, slot, func, 561185089Sraj PCIR_SECBUS_1, 1); 562185089Sraj 563185089Sraj /* Configure buses behind the bridge */ 564209131Sraj mv_pcib_init(sc, secbus, PCI_SLOTMAX); 565185089Sraj} 566185089Sraj 567185089Srajstatic int 568209131Srajmv_pcib_init(struct mv_pcib_softc *sc, int bus, int maxslot) 569185089Sraj{ 570185089Sraj int slot, func, maxfunc, error; 571185089Sraj uint8_t hdrtype, command, class, subclass; 572185089Sraj 573185089Sraj for (slot = 0; slot <= maxslot; slot++) { 574185089Sraj maxfunc = 0; 575185089Sraj for (func = 0; func <= maxfunc; func++) { 576209131Sraj hdrtype = mv_pcib_read_config(sc->sc_dev, bus, slot, 577185089Sraj func, PCIR_HDRTYPE, 1); 578185089Sraj 579185089Sraj if ((hdrtype & PCIM_HDRTYPE) > PCI_MAXHDRTYPE) 580185089Sraj continue; 581185089Sraj 582185089Sraj if (func == 0 && (hdrtype & PCIM_MFDEV)) 583185089Sraj maxfunc = PCI_FUNCMAX; 584185089Sraj 585209131Sraj command = mv_pcib_read_config(sc->sc_dev, bus, slot, 586185089Sraj func, PCIR_COMMAND, 1); 587185089Sraj command &= ~(PCIM_CMD_MEMEN | PCIM_CMD_PORTEN); 588209131Sraj mv_pcib_write_config(sc->sc_dev, bus, slot, func, 589185089Sraj PCIR_COMMAND, command, 1); 590185089Sraj 591209131Sraj error = mv_pcib_init_all_bars(sc, bus, slot, func, 592185089Sraj hdrtype); 593185089Sraj 594185089Sraj if (error) 595185089Sraj return (error); 596185089Sraj 597185089Sraj command |= PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN | 598185089Sraj PCIM_CMD_PORTEN; 599209131Sraj mv_pcib_write_config(sc->sc_dev, bus, slot, func, 600185089Sraj PCIR_COMMAND, command, 1); 601185089Sraj 602185089Sraj /* Handle PCI-PCI bridges */ 603209131Sraj class = mv_pcib_read_config(sc->sc_dev, bus, slot, 604185089Sraj func, PCIR_CLASS, 1); 605209131Sraj subclass = mv_pcib_read_config(sc->sc_dev, bus, slot, 606185089Sraj func, PCIR_SUBCLASS, 1); 607185089Sraj 608185089Sraj if (class != PCIC_BRIDGE || 609185089Sraj subclass != PCIS_BRIDGE_PCI) 610185089Sraj continue; 611185089Sraj 612209131Sraj mv_pcib_init_bridge(sc, bus, slot, func); 613185089Sraj } 614185089Sraj } 615185089Sraj 616185089Sraj /* Enable all ABCD interrupts */ 617185089Sraj pcib_write_irq_mask(sc, (0xF << 24)); 618185089Sraj 619185089Sraj return (0); 620185089Sraj} 621185089Sraj 622209131Srajstatic int 623209131Srajmv_pcib_init_all_bars(struct mv_pcib_softc *sc, int bus, int slot, 624209131Sraj int func, int hdrtype) 625209131Sraj{ 626209131Sraj int maxbar, bar, i; 627209131Sraj 628209131Sraj maxbar = (hdrtype & PCIM_HDRTYPE) ? 0 : 6; 629209131Sraj bar = 0; 630209131Sraj 631209131Sraj /* Program the base address registers */ 632209131Sraj while (bar < maxbar) { 633209131Sraj i = mv_pcib_init_bar(sc, bus, slot, func, bar); 634209131Sraj bar += i; 635209131Sraj if (i < 0) { 636209131Sraj device_printf(sc->sc_dev, 637209131Sraj "PCI IO/Memory space exhausted\n"); 638209131Sraj return (ENOMEM); 639209131Sraj } 640209131Sraj } 641209131Sraj 642209131Sraj return (0); 643209131Sraj} 644209131Sraj 645185089Srajstatic struct resource * 646209131Srajmv_pcib_alloc_resource(device_t dev, device_t child, int type, int *rid, 647185089Sraj u_long start, u_long end, u_long count, u_int flags) 648185089Sraj{ 649209131Sraj struct mv_pcib_softc *sc = device_get_softc(dev); 650186932Sraj struct rman *rm = NULL; 651186932Sraj struct resource *res; 652185089Sraj 653186932Sraj switch (type) { 654186932Sraj case SYS_RES_IOPORT: 655209131Sraj rm = &sc->sc_io_rman; 656186932Sraj break; 657186932Sraj case SYS_RES_MEMORY: 658209131Sraj rm = &sc->sc_mem_rman; 659186932Sraj break; 660186932Sraj default: 661240489Sgber return (BUS_ALLOC_RESOURCE(device_get_parent(dev), dev, 662186932Sraj type, rid, start, end, count, flags)); 663186932Sraj }; 664186932Sraj 665240489Sgber if ((start == 0UL) && (end == ~0UL)) { 666240489Sgber start = sc->sc_mem_base; 667240489Sgber end = sc->sc_mem_base + sc->sc_mem_size - 1; 668240489Sgber count = sc->sc_mem_size; 669240489Sgber } 670240489Sgber 671240489Sgber if ((start < sc->sc_mem_base) || (start + count - 1 != end) || 672240489Sgber (end > sc->sc_mem_base + sc->sc_mem_size - 1)) 673240489Sgber return (NULL); 674240489Sgber 675186932Sraj res = rman_reserve_resource(rm, start, end, count, flags, child); 676186932Sraj if (res == NULL) 677186932Sraj return (NULL); 678186932Sraj 679186932Sraj rman_set_rid(res, *rid); 680209131Sraj rman_set_bustag(res, fdtbus_bs_tag); 681186932Sraj rman_set_bushandle(res, start); 682186932Sraj 683186932Sraj if (flags & RF_ACTIVE) 684186932Sraj if (bus_activate_resource(child, type, *rid, res)) { 685186932Sraj rman_release_resource(res); 686186932Sraj return (NULL); 687186932Sraj } 688186932Sraj 689186932Sraj return (res); 690185089Sraj} 691185089Sraj 692185089Srajstatic int 693209131Srajmv_pcib_release_resource(device_t dev, device_t child, int type, int rid, 694185089Sraj struct resource *res) 695185089Sraj{ 696185089Sraj 697186932Sraj if (type != SYS_RES_IOPORT && type != SYS_RES_MEMORY) 698186932Sraj return (BUS_RELEASE_RESOURCE(device_get_parent(dev), child, 699186932Sraj type, rid, res)); 700186932Sraj 701186932Sraj return (rman_release_resource(res)); 702185089Sraj} 703185089Sraj 704185089Srajstatic int 705209131Srajmv_pcib_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) 706185089Sraj{ 707209131Sraj struct mv_pcib_softc *sc = device_get_softc(dev); 708185089Sraj 709185089Sraj switch (which) { 710185089Sraj case PCIB_IVAR_BUS: 711185089Sraj *result = sc->sc_busnr; 712185089Sraj return (0); 713185089Sraj case PCIB_IVAR_DOMAIN: 714185089Sraj *result = device_get_unit(dev); 715185089Sraj return (0); 716185089Sraj } 717185089Sraj 718185089Sraj return (ENOENT); 719185089Sraj} 720185089Sraj 721185089Srajstatic int 722209131Srajmv_pcib_write_ivar(device_t dev, device_t child, int which, uintptr_t value) 723185089Sraj{ 724209131Sraj struct mv_pcib_softc *sc = device_get_softc(dev); 725185089Sraj 726185089Sraj switch (which) { 727185089Sraj case PCIB_IVAR_BUS: 728185089Sraj sc->sc_busnr = value; 729185089Sraj return (0); 730185089Sraj } 731185089Sraj 732185089Sraj return (ENOENT); 733185089Sraj} 734209131Sraj 735209131Srajstatic inline void 736209131Srajpcib_write_irq_mask(struct mv_pcib_softc *sc, uint32_t mask) 737209131Sraj{ 738209131Sraj 739209131Sraj if (!sc->sc_type != MV_TYPE_PCI) 740209131Sraj return; 741209131Sraj 742209131Sraj bus_space_write_4(sc->sc_bst, sc->sc_bsh, PCIE_REG_IRQ_MASK, mask); 743209131Sraj} 744209131Sraj 745209131Srajstatic void 746209131Srajmv_pcib_hw_cfginit(void) 747209131Sraj{ 748209131Sraj static int opened = 0; 749209131Sraj 750209131Sraj if (opened) 751209131Sraj return; 752209131Sraj 753209131Sraj mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN); 754209131Sraj opened = 1; 755209131Sraj} 756209131Sraj 757209131Srajstatic uint32_t 758209131Srajmv_pcib_hw_cfgread(struct mv_pcib_softc *sc, u_int bus, u_int slot, 759209131Sraj u_int func, u_int reg, int bytes) 760209131Sraj{ 761209131Sraj uint32_t addr, data, ca, cd; 762209131Sraj 763209131Sraj ca = (sc->sc_type != MV_TYPE_PCI) ? 764209131Sraj PCIE_REG_CFG_ADDR : PCI_REG_CFG_ADDR; 765209131Sraj cd = (sc->sc_type != MV_TYPE_PCI) ? 766209131Sraj PCIE_REG_CFG_DATA : PCI_REG_CFG_DATA; 767209131Sraj addr = PCI_CFG_ENA | PCI_CFG_BUS(bus) | PCI_CFG_DEV(slot) | 768209131Sraj PCI_CFG_FUN(func) | PCI_CFG_PCIE_REG(reg); 769209131Sraj 770209131Sraj mtx_lock_spin(&pcicfg_mtx); 771209131Sraj bus_space_write_4(sc->sc_bst, sc->sc_bsh, ca, addr); 772209131Sraj 773209131Sraj data = ~0; 774209131Sraj switch (bytes) { 775209131Sraj case 1: 776209131Sraj data = bus_space_read_1(sc->sc_bst, sc->sc_bsh, 777209131Sraj cd + (reg & 3)); 778209131Sraj break; 779209131Sraj case 2: 780209131Sraj data = le16toh(bus_space_read_2(sc->sc_bst, sc->sc_bsh, 781209131Sraj cd + (reg & 2))); 782209131Sraj break; 783209131Sraj case 4: 784209131Sraj data = le32toh(bus_space_read_4(sc->sc_bst, sc->sc_bsh, 785209131Sraj cd)); 786209131Sraj break; 787209131Sraj } 788209131Sraj mtx_unlock_spin(&pcicfg_mtx); 789209131Sraj return (data); 790209131Sraj} 791209131Sraj 792209131Srajstatic void 793209131Srajmv_pcib_hw_cfgwrite(struct mv_pcib_softc *sc, u_int bus, u_int slot, 794209131Sraj u_int func, u_int reg, uint32_t data, int bytes) 795209131Sraj{ 796209131Sraj uint32_t addr, ca, cd; 797209131Sraj 798209131Sraj ca = (sc->sc_type != MV_TYPE_PCI) ? 799209131Sraj PCIE_REG_CFG_ADDR : PCI_REG_CFG_ADDR; 800209131Sraj cd = (sc->sc_type != MV_TYPE_PCI) ? 801209131Sraj PCIE_REG_CFG_DATA : PCI_REG_CFG_DATA; 802209131Sraj addr = PCI_CFG_ENA | PCI_CFG_BUS(bus) | PCI_CFG_DEV(slot) | 803209131Sraj PCI_CFG_FUN(func) | PCI_CFG_PCIE_REG(reg); 804209131Sraj 805209131Sraj mtx_lock_spin(&pcicfg_mtx); 806209131Sraj bus_space_write_4(sc->sc_bst, sc->sc_bsh, ca, addr); 807209131Sraj 808209131Sraj switch (bytes) { 809209131Sraj case 1: 810209131Sraj bus_space_write_1(sc->sc_bst, sc->sc_bsh, 811209131Sraj cd + (reg & 3), data); 812209131Sraj break; 813209131Sraj case 2: 814209131Sraj bus_space_write_2(sc->sc_bst, sc->sc_bsh, 815209131Sraj cd + (reg & 2), htole16(data)); 816209131Sraj break; 817209131Sraj case 4: 818209131Sraj bus_space_write_4(sc->sc_bst, sc->sc_bsh, 819209131Sraj cd, htole32(data)); 820209131Sraj break; 821209131Sraj } 822209131Sraj mtx_unlock_spin(&pcicfg_mtx); 823209131Sraj} 824209131Sraj 825209131Srajstatic int 826209131Srajmv_pcib_maxslots(device_t dev) 827209131Sraj{ 828209131Sraj struct mv_pcib_softc *sc = device_get_softc(dev); 829209131Sraj 830209131Sraj return ((sc->sc_type != MV_TYPE_PCI) ? 1 : PCI_SLOTMAX); 831209131Sraj} 832209131Sraj 833209131Srajstatic uint32_t 834209131Srajmv_pcib_read_config(device_t dev, u_int bus, u_int slot, u_int func, 835209131Sraj u_int reg, int bytes) 836209131Sraj{ 837209131Sraj struct mv_pcib_softc *sc = device_get_softc(dev); 838209131Sraj 839240489Sgber /* Return ~0 if link is inactive or trying to read from Root */ 840240489Sgber if ((bus_space_read_4(sc->sc_bst, sc->sc_bsh, PCIE_REG_STATUS) & 841240489Sgber PCIE_STATUS_LINK_DOWN) || (slot == 0)) 842209131Sraj return (~0U); 843209131Sraj 844209131Sraj return (mv_pcib_hw_cfgread(sc, bus, slot, func, reg, bytes)); 845209131Sraj} 846209131Sraj 847209131Srajstatic void 848209131Srajmv_pcib_write_config(device_t dev, u_int bus, u_int slot, u_int func, 849209131Sraj u_int reg, uint32_t val, int bytes) 850209131Sraj{ 851209131Sraj struct mv_pcib_softc *sc = device_get_softc(dev); 852209131Sraj 853240489Sgber /* Return if link is inactive or trying to write to Root */ 854240489Sgber if ((bus_space_read_4(sc->sc_bst, sc->sc_bsh, PCIE_REG_STATUS) & 855240489Sgber PCIE_STATUS_LINK_DOWN) || (slot == 0)) 856209131Sraj return; 857209131Sraj 858209131Sraj mv_pcib_hw_cfgwrite(sc, bus, slot, func, reg, val, bytes); 859209131Sraj} 860209131Sraj 861209131Srajstatic int 862209131Srajmv_pcib_route_interrupt(device_t pcib, device_t dev, int pin) 863209131Sraj{ 864209131Sraj struct mv_pcib_softc *sc; 865209131Sraj int err, interrupt; 866209131Sraj 867209131Sraj sc = device_get_softc(pcib); 868209131Sraj 869209131Sraj err = fdt_pci_route_intr(pci_get_bus(dev), pci_get_slot(dev), 870209131Sraj pci_get_function(dev), pin, &sc->sc_intr_info, &interrupt); 871209131Sraj if (err == 0) 872209131Sraj return (interrupt); 873209131Sraj 874209131Sraj device_printf(pcib, "could not route pin %d for device %d.%d\n", 875209131Sraj pin, pci_get_slot(dev), pci_get_function(dev)); 876209131Sraj return (PCI_INVALID_IRQ); 877209131Sraj} 878209131Sraj 879209131Srajstatic int 880209131Srajmv_pcib_decode_win(phandle_t node, struct mv_pcib_softc *sc) 881209131Sraj{ 882209131Sraj struct fdt_pci_range io_space, mem_space; 883209131Sraj device_t dev; 884209131Sraj int error; 885209131Sraj 886209131Sraj dev = sc->sc_dev; 887209131Sraj 888209131Sraj if ((error = fdt_pci_ranges(node, &io_space, &mem_space)) != 0) { 889209131Sraj device_printf(dev, "could not retrieve 'ranges' data\n"); 890209131Sraj return (error); 891209131Sraj } 892209131Sraj 893209131Sraj /* Configure CPU decoding windows */ 894240489Sgber error = decode_win_cpu_set(sc->sc_win_target, 895240489Sgber sc->sc_io_win_attr, io_space.base_parent, io_space.len, ~0); 896209131Sraj if (error < 0) { 897209131Sraj device_printf(dev, "could not set up CPU decode " 898209131Sraj "window for PCI IO\n"); 899209131Sraj return (ENXIO); 900209131Sraj } 901240489Sgber error = decode_win_cpu_set(sc->sc_win_target, 902240489Sgber sc->sc_mem_win_attr, mem_space.base_parent, mem_space.len, 903240489Sgber mem_space.base_parent); 904209131Sraj if (error < 0) { 905209131Sraj device_printf(dev, "could not set up CPU decode " 906209131Sraj "windows for PCI MEM\n"); 907209131Sraj return (ENXIO); 908209131Sraj } 909209131Sraj 910209131Sraj sc->sc_io_base = io_space.base_parent; 911209131Sraj sc->sc_io_size = io_space.len; 912209131Sraj 913209131Sraj sc->sc_mem_base = mem_space.base_parent; 914209131Sraj sc->sc_mem_size = mem_space.len; 915209131Sraj 916209131Sraj return (0); 917209131Sraj} 918209131Sraj 919209131Srajstatic int 920209131Srajmv_pcib_intr_info(phandle_t node, struct mv_pcib_softc *sc) 921209131Sraj{ 922209131Sraj int error; 923209131Sraj 924209131Sraj if ((error = fdt_pci_intr_info(node, &sc->sc_intr_info)) != 0) 925209131Sraj return (error); 926209131Sraj 927209131Sraj return (0); 928209131Sraj} 929209131Sraj 930