mv_pci.c revision 240489
190075Sobrien/*- 290075Sobrien * Copyright (c) 2008 MARVELL INTERNATIONAL LTD. 390075Sobrien * Copyright (c) 2010 The FreeBSD Foundation 490075Sobrien * Copyright (c) 2010-2012 Semihalf 590075Sobrien * All rights reserved. 690075Sobrien * 790075Sobrien * Developed by Semihalf. 890075Sobrien * 990075Sobrien * Portions of this software were developed by Semihalf 1090075Sobrien * under sponsorship from the FreeBSD Foundation. 1190075Sobrien * 1290075Sobrien * Redistribution and use in source and binary forms, with or without 1390075Sobrien * modification, are permitted provided that the following conditions 1490075Sobrien * are met: 1590075Sobrien * 1. Redistributions of source code must retain the above copyright 1690075Sobrien * notice, this list of conditions and the following disclaimer. 1790075Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1890075Sobrien * notice, this list of conditions and the following disclaimer in the 1990075Sobrien * documentation and/or other materials provided with the distribution. 2090075Sobrien * 3. Neither the name of MARVELL nor the names of contributors 2190075Sobrien * may be used to endorse or promote products derived from this software 2290075Sobrien * without specific prior written permission. 2390075Sobrien * 2490075Sobrien * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 2590075Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2690075Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2790075Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 2890075Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2990075Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3090075Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3190075Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3290075Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3390075Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3490075Sobrien * SUCH DAMAGE. 3590075Sobrien */ 3690075Sobrien 3790075Sobrien/* 3890075Sobrien * Marvell integrated PCI/PCI-Express controller driver. 3990075Sobrien */ 4090075Sobrien 41117395Skan#include <sys/cdefs.h> 42117395Skan__FBSDID("$FreeBSD: head/sys/arm/mv/mv_pci.c 240489 2012-09-14 09:57:41Z gber $"); 43117395Skan 44117395Skan#include <sys/param.h> 45117395Skan#include <sys/systm.h> 46117395Skan#include <sys/kernel.h> 4790075Sobrien#include <sys/lock.h> 4890075Sobrien#include <sys/malloc.h> 49117395Skan#include <sys/module.h> 50117395Skan#include <sys/mutex.h> 5190075Sobrien#include <sys/queue.h> 5290075Sobrien#include <sys/bus.h> 5390075Sobrien#include <sys/rman.h> 5490075Sobrien#include <sys/endian.h> 5590075Sobrien 5690075Sobrien#include <vm/vm.h> 5790075Sobrien#include <vm/pmap.h> 5890075Sobrien 5990075Sobrien#include <dev/fdt/fdt_common.h> 6090075Sobrien#include <dev/ofw/ofw_bus.h> 6190075Sobrien#include <dev/ofw/ofw_bus_subr.h> 6290075Sobrien#include <dev/pci/pcivar.h> 6390075Sobrien#include <dev/pci/pcireg.h> 6490075Sobrien#include <dev/pci/pcib_private.h> 6590075Sobrien 6690075Sobrien#include "ofw_bus_if.h" 6790075Sobrien#include "pcib_if.h" 6890075Sobrien 6990075Sobrien#include <machine/resource.h> 7090075Sobrien#include <machine/bus.h> 7190075Sobrien 7290075Sobrien#include <arm/mv/mvreg.h> 7390075Sobrien#include <arm/mv/mvvar.h> 7490075Sobrien#include <arm/mv/mvwin.h> 7590075Sobrien 7690075Sobrien#define PCI_CFG_ENA (1 << 31) 7790075Sobrien#define PCI_CFG_BUS(bus) (((bus) & 0xff) << 16) 7890075Sobrien#define PCI_CFG_DEV(dev) (((dev) & 0x1f) << 11) 7990075Sobrien#define PCI_CFG_FUN(fun) (((fun) & 0x7) << 8) 8090075Sobrien#define PCI_CFG_PCIE_REG(reg) ((reg) & 0xfc) 8190075Sobrien 8290075Sobrien#define PCI_REG_CFG_ADDR 0x0C78 8390075Sobrien#define PCI_REG_CFG_DATA 0x0C7C 8490075Sobrien#define PCI_REG_P2P_CONF 0x1D14 8590075Sobrien 8690075Sobrien#define PCIE_REG_CFG_ADDR 0x18F8 8790075Sobrien#define PCIE_REG_CFG_DATA 0x18FC 8890075Sobrien#define PCIE_REG_CONTROL 0x1A00 8990075Sobrien#define PCIE_CTRL_LINK1X 0x00000001 9090075Sobrien#define PCIE_REG_STATUS 0x1A04 9190075Sobrien#define PCIE_REG_IRQ_MASK 0x1910 9290075Sobrien 9390075Sobrien#define PCIE_CONTROL_ROOT_CMPLX (1 << 1) 9490075Sobrien#define PCIE_CONTROL_HOT_RESET (1 << 24) 9590075Sobrien 9690075Sobrien#define PCIE_LINK_TIMEOUT 1000000 9790075Sobrien 9890075Sobrien#define PCIE_STATUS_LINK_DOWN 1 9990075Sobrien#define PCIE_STATUS_DEV_OFFS 16 10090075Sobrien 10190075Sobrien/* Minimum PCI Memory and I/O allocations taken from PCI spec (in bytes) */ 10290075Sobrien#define PCI_MIN_IO_ALLOC 4 10390075Sobrien#define PCI_MIN_MEM_ALLOC 16 10490075Sobrien 10590075Sobrien#define BITS_PER_UINT32 (NBBY * sizeof(uint32_t)) 10690075Sobrien 10790075Sobrienstruct mv_pcib_softc { 10890075Sobrien device_t sc_dev; 10990075Sobrien 11090075Sobrien struct rman sc_mem_rman; 11190075Sobrien bus_addr_t sc_mem_base; 11290075Sobrien bus_addr_t sc_mem_size; 11390075Sobrien uint32_t sc_mem_map[MV_PCI_MEM_SLICE_SIZE / 11490075Sobrien (PCI_MIN_MEM_ALLOC * BITS_PER_UINT32)]; 11590075Sobrien int sc_win_target; 11690075Sobrien int sc_mem_win_attr; 117117395Skan 118117395Skan struct rman sc_io_rman; 11990075Sobrien bus_addr_t sc_io_base; 12090075Sobrien bus_addr_t sc_io_size; 121117395Skan uint32_t sc_io_map[MV_PCI_IO_SLICE_SIZE / 122117395Skan (PCI_MIN_IO_ALLOC * BITS_PER_UINT32)]; 123117395Skan int sc_io_win_attr; 124117395Skan 125117395Skan struct resource *sc_res; 126117395Skan bus_space_handle_t sc_bsh; 127117395Skan bus_space_tag_t sc_bst; 128117395Skan int sc_rid; 129117395Skan 13090075Sobrien int sc_busnr; /* Host bridge bus number */ 13190075Sobrien int sc_devnr; /* Host bridge device number */ 13290075Sobrien int sc_type; 13390075Sobrien int sc_mode; /* Endpoint / Root Complex */ 13490075Sobrien 13590075Sobrien struct fdt_pci_intr sc_intr_info; 13690075Sobrien}; 13790075Sobrien 13890075Sobrien/* Local forward prototypes */ 13990075Sobrienstatic int mv_pcib_decode_win(phandle_t, struct mv_pcib_softc *); 14090075Sobrienstatic void mv_pcib_hw_cfginit(void); 14190075Sobrienstatic uint32_t mv_pcib_hw_cfgread(struct mv_pcib_softc *, u_int, u_int, 14290075Sobrien u_int, u_int, int); 14390075Sobrienstatic void mv_pcib_hw_cfgwrite(struct mv_pcib_softc *, u_int, u_int, 14490075Sobrien u_int, u_int, uint32_t, int); 14590075Sobrienstatic int mv_pcib_init(struct mv_pcib_softc *, int, int); 14690075Sobrienstatic int mv_pcib_init_all_bars(struct mv_pcib_softc *, int, int, int, int); 14790075Sobrienstatic void mv_pcib_init_bridge(struct mv_pcib_softc *, int, int, int); 14890075Sobrienstatic int mv_pcib_intr_info(phandle_t, struct mv_pcib_softc *); 14990075Sobrienstatic inline void pcib_write_irq_mask(struct mv_pcib_softc *, uint32_t); 15090075Sobrienstatic void mv_pcib_enable(struct mv_pcib_softc *, uint32_t); 15190075Sobrienstatic int mv_pcib_mem_init(struct mv_pcib_softc *); 15290075Sobrien 15390075Sobrien/* Forward prototypes */ 15490075Sobrienstatic int mv_pcib_probe(device_t); 15590075Sobrienstatic int mv_pcib_attach(device_t); 15690075Sobrien 15790075Sobrienstatic struct resource *mv_pcib_alloc_resource(device_t, device_t, int, int *, 15890075Sobrien u_long, u_long, u_long, u_int); 15990075Sobrienstatic int mv_pcib_release_resource(device_t, device_t, int, int, 16090075Sobrien struct resource *); 16190075Sobrienstatic int mv_pcib_read_ivar(device_t, device_t, int, uintptr_t *); 16290075Sobrienstatic int mv_pcib_write_ivar(device_t, device_t, int, uintptr_t); 16390075Sobrien 16490075Sobrienstatic int mv_pcib_maxslots(device_t); 16590075Sobrienstatic uint32_t mv_pcib_read_config(device_t, u_int, u_int, u_int, u_int, int); 16690075Sobrienstatic void mv_pcib_write_config(device_t, u_int, u_int, u_int, u_int, 16790075Sobrien uint32_t, int); 16890075Sobrienstatic int mv_pcib_route_interrupt(device_t, device_t, int); 16990075Sobrien 17090075Sobrien/* 17190075Sobrien * Bus interface definitions. 17290075Sobrien */ 17390075Sobrienstatic device_method_t mv_pcib_methods[] = { 17490075Sobrien /* Device interface */ 17590075Sobrien DEVMETHOD(device_probe, mv_pcib_probe), 176117395Skan DEVMETHOD(device_attach, mv_pcib_attach), 17790075Sobrien 17890075Sobrien /* Bus interface */ 17990075Sobrien DEVMETHOD(bus_read_ivar, mv_pcib_read_ivar), 18090075Sobrien DEVMETHOD(bus_write_ivar, mv_pcib_write_ivar), 18190075Sobrien DEVMETHOD(bus_alloc_resource, mv_pcib_alloc_resource), 18290075Sobrien DEVMETHOD(bus_release_resource, mv_pcib_release_resource), 18390075Sobrien DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 18490075Sobrien DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 185117395Skan DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 18690075Sobrien DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 18790075Sobrien 18890075Sobrien /* pcib interface */ 18990075Sobrien DEVMETHOD(pcib_maxslots, mv_pcib_maxslots), 19090075Sobrien DEVMETHOD(pcib_read_config, mv_pcib_read_config), 19190075Sobrien DEVMETHOD(pcib_write_config, mv_pcib_write_config), 19290075Sobrien DEVMETHOD(pcib_route_interrupt, mv_pcib_route_interrupt), 19390075Sobrien 19490075Sobrien /* OFW bus interface */ 19590075Sobrien DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), 19690075Sobrien DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), 197117395Skan DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), 198117395Skan DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), 19990075Sobrien DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), 20090075Sobrien 20190075Sobrien DEVMETHOD_END 20290075Sobrien}; 20390075Sobrien 20490075Sobrienstatic driver_t mv_pcib_driver = { 20590075Sobrien "pcib", 20690075Sobrien mv_pcib_methods, 20790075Sobrien sizeof(struct mv_pcib_softc), 20890075Sobrien}; 209117395Skan 21090075Sobriendevclass_t pcib_devclass; 21190075Sobrien 21290075SobrienDRIVER_MODULE(pcib, fdtbus, mv_pcib_driver, pcib_devclass, 0, 0); 21390075Sobrien 214117395Skanstatic struct mtx pcicfg_mtx; 215117395Skan 216117395Skanstatic int 217117395Skanmv_pcib_probe(device_t self) 21890075Sobrien{ 21990075Sobrien phandle_t node; 220117395Skan 22190075Sobrien node = ofw_bus_get_node(self); 22290075Sobrien if (!fdt_is_type(node, "pci")) 223117395Skan return (ENXIO); 224117395Skan 225117395Skan if (!(fdt_is_compatible(node, "mrvl,pcie") || 226117395Skan fdt_is_compatible(node, "mrvl,pci"))) 22790075Sobrien return (ENXIO); 22890075Sobrien 22990075Sobrien device_set_desc(self, "Marvell Integrated PCI/PCI-E Controller"); 230117395Skan return (BUS_PROBE_DEFAULT); 23190075Sobrien} 23290075Sobrien 23390075Sobrienstatic int 23490075Sobrienmv_pcib_attach(device_t self) 23590075Sobrien{ 23690075Sobrien struct mv_pcib_softc *sc; 23790075Sobrien phandle_t node, parnode; 23890075Sobrien uint32_t val, unit; 23990075Sobrien int err; 24090075Sobrien 24190075Sobrien sc = device_get_softc(self); 24290075Sobrien sc->sc_dev = self; 24390075Sobrien unit = fdt_get_unit(self); 24490075Sobrien 24590075Sobrien 24690075Sobrien node = ofw_bus_get_node(self); 24790075Sobrien parnode = OF_parent(node); 24890075Sobrien if (fdt_is_compatible(node, "mrvl,pcie")) { 24990075Sobrien sc->sc_type = MV_TYPE_PCIE; 25090075Sobrien sc->sc_win_target = MV_WIN_PCIE_TARGET(unit); 25190075Sobrien sc->sc_mem_win_attr = MV_WIN_PCIE_MEM_ATTR(unit); 25290075Sobrien sc->sc_io_win_attr = MV_WIN_PCIE_IO_ATTR(unit); 25390075Sobrien } else if (fdt_is_compatible(node, "mrvl,pci")) { 25490075Sobrien sc->sc_type = MV_TYPE_PCI; 25590075Sobrien sc->sc_win_target = MV_WIN_PCI_TARGET; 25690075Sobrien sc->sc_mem_win_attr = MV_WIN_PCI_MEM_ATTR; 25790075Sobrien sc->sc_io_win_attr = MV_WIN_PCI_IO_ATTR; 25890075Sobrien } else 25990075Sobrien return (ENXIO); 26090075Sobrien 26190075Sobrien /* 26290075Sobrien * Retrieve our mem-mapped registers range. 26390075Sobrien */ 26490075Sobrien sc->sc_rid = 0; 26590075Sobrien sc->sc_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &sc->sc_rid, 26690075Sobrien RF_ACTIVE); 26790075Sobrien if (sc->sc_res == NULL) { 26890075Sobrien device_printf(self, "could not map memory\n"); 26990075Sobrien return (ENXIO); 27090075Sobrien } 27190075Sobrien sc->sc_bst = rman_get_bustag(sc->sc_res); 27290075Sobrien sc->sc_bsh = rman_get_bushandle(sc->sc_res); 27390075Sobrien 27490075Sobrien val = bus_space_read_4(sc->sc_bst, sc->sc_bsh, PCIE_REG_CONTROL); 27590075Sobrien sc->sc_mode = (val & PCIE_CONTROL_ROOT_CMPLX ? MV_MODE_ROOT : 27690075Sobrien MV_MODE_ENDPOINT); 27790075Sobrien 27890075Sobrien /* 27990075Sobrien * Get PCI interrupt info. 28090075Sobrien */ 28190075Sobrien if ((sc->sc_mode == MV_MODE_ROOT) && 28290075Sobrien (mv_pcib_intr_info(node, sc) != 0)) { 28390075Sobrien device_printf(self, "could not retrieve interrupt info\n"); 28490075Sobrien return (ENXIO); 28590075Sobrien } 28690075Sobrien 28790075Sobrien /* 288117395Skan * Configure decode windows for PCI(E) access. 28990075Sobrien */ 29090075Sobrien if (mv_pcib_decode_win(node, sc) != 0) 29190075Sobrien return (ENXIO); 29290075Sobrien 29390075Sobrien mv_pcib_hw_cfginit(); 29490075Sobrien 29590075Sobrien /* 29690075Sobrien * Enable PCIE device. 29790075Sobrien */ 29890075Sobrien mv_pcib_enable(sc, unit); 29990075Sobrien 30090075Sobrien /* 30190075Sobrien * Memory management. 30290075Sobrien */ 30390075Sobrien err = mv_pcib_mem_init(sc); 30490075Sobrien if (err) 30590075Sobrien return (err); 30690075Sobrien 30790075Sobrien if (sc->sc_mode == MV_MODE_ROOT) { 30890075Sobrien err = mv_pcib_init(sc, sc->sc_busnr, 30990075Sobrien mv_pcib_maxslots(sc->sc_dev)); 31090075Sobrien if (err) 31190075Sobrien goto error; 31290075Sobrien 31390075Sobrien device_add_child(self, "pci", -1); 31490075Sobrien } else { 31590075Sobrien sc->sc_devnr = 1; 31690075Sobrien bus_space_write_4(sc->sc_bst, sc->sc_bsh, 31790075Sobrien PCIE_REG_STATUS, 1 << PCIE_STATUS_DEV_OFFS); 31890075Sobrien device_add_child(self, "pci_ep", -1); 31990075Sobrien } 320117395Skan 321117395Skan return (bus_generic_attach(self)); 322117395Skan 323117395Skanerror: 324117395Skan /* XXX SYS_RES_ should be released here */ 325117395Skan rman_fini(&sc->sc_mem_rman); 326117395Skan rman_fini(&sc->sc_io_rman); 327117395Skan 328117395Skan return (err); 329117395Skan} 330117395Skan 331117395Skanstatic void 332117395Skanmv_pcib_enable(struct mv_pcib_softc *sc, uint32_t unit) 333117395Skan{ 334117395Skan uint32_t val; 335117395Skan#if !defined(SOC_MV_ARMADAXP) 336117395Skan int timeout; 337117395Skan 338117395Skan /* 339117395Skan * Check if PCIE device is enabled. 340117395Skan */ 341117395Skan if (read_cpu_ctrl(CPU_CONTROL) & CPU_CONTROL_PCIE_DISABLE(unit)) { 342117395Skan write_cpu_ctrl(CPU_CONTROL, read_cpu_ctrl(CPU_CONTROL) & 343117395Skan ~(CPU_CONTROL_PCIE_DISABLE(unit))); 344117395Skan 345117395Skan timeout = PCIE_LINK_TIMEOUT; 346117395Skan val = bus_space_read_4(sc->sc_bst, sc->sc_bsh, 347117395Skan PCIE_REG_STATUS); 348117395Skan while (((val & PCIE_STATUS_LINK_DOWN) == 1) && (timeout > 0)) { 349117395Skan DELAY(1000); 350117395Skan timeout -= 1000; 351117395Skan val = bus_space_read_4(sc->sc_bst, sc->sc_bsh, 352117395Skan PCIE_REG_STATUS); 353117395Skan } 354117395Skan } 355117395Skan#endif 356117395Skan 357117395Skan 358117395Skan if (sc->sc_mode == MV_MODE_ROOT) { 359117395Skan /* 360117395Skan * Enable PCI bridge. 361117395Skan */ 362117395Skan val = bus_space_read_4(sc->sc_bst, sc->sc_bsh, PCIR_COMMAND); 363117395Skan val |= PCIM_CMD_SERRESPEN | PCIM_CMD_BUSMASTEREN | 364117395Skan PCIM_CMD_MEMEN | PCIM_CMD_PORTEN; 365117395Skan bus_space_write_4(sc->sc_bst, sc->sc_bsh, PCIR_COMMAND, val); 366117395Skan } 367117395Skan} 368117395Skan 369117395Skanstatic int 370117395Skanmv_pcib_mem_init(struct mv_pcib_softc *sc) 371117395Skan{ 372117395Skan int err; 373117395Skan 374117395Skan /* 375117395Skan * Memory management. 376117395Skan */ 377117395Skan sc->sc_mem_rman.rm_type = RMAN_ARRAY; 378117395Skan err = rman_init(&sc->sc_mem_rman); 379117395Skan if (err) 380117395Skan return (err); 381117395Skan 382117395Skan sc->sc_io_rman.rm_type = RMAN_ARRAY; 38390075Sobrien err = rman_init(&sc->sc_io_rman); 38490075Sobrien if (err) { 385117395Skan rman_fini(&sc->sc_mem_rman); 38690075Sobrien return (err); 38790075Sobrien } 38890075Sobrien 38990075Sobrien err = rman_manage_region(&sc->sc_mem_rman, sc->sc_mem_base, 39090075Sobrien sc->sc_mem_base + sc->sc_mem_size - 1); 39190075Sobrien if (err) 39290075Sobrien goto error; 39390075Sobrien 39490075Sobrien err = rman_manage_region(&sc->sc_io_rman, sc->sc_io_base, 39590075Sobrien sc->sc_io_base + sc->sc_io_size - 1); 39690075Sobrien if (err) 39790075Sobrien goto error; 39890075Sobrien 39990075Sobrien return (0); 40090075Sobrien 40190075Sobrienerror: 40290075Sobrien rman_fini(&sc->sc_mem_rman); 40390075Sobrien rman_fini(&sc->sc_io_rman); 40490075Sobrien 40590075Sobrien return (err); 40690075Sobrien} 40790075Sobrien 40890075Sobrienstatic inline uint32_t 409117395Skanpcib_bit_get(uint32_t *map, uint32_t bit) 41090075Sobrien{ 41190075Sobrien uint32_t n = bit / BITS_PER_UINT32; 41290075Sobrien 41390075Sobrien bit = bit % BITS_PER_UINT32; 41490075Sobrien return (map[n] & (1 << bit)); 41590075Sobrien} 41690075Sobrien 41790075Sobrienstatic inline void 41890075Sobrienpcib_bit_set(uint32_t *map, uint32_t bit) 41990075Sobrien{ 42090075Sobrien uint32_t n = bit / BITS_PER_UINT32; 42190075Sobrien 42290075Sobrien bit = bit % BITS_PER_UINT32; 42390075Sobrien map[n] |= (1 << bit); 42490075Sobrien} 42590075Sobrien 42690075Sobrienstatic inline uint32_t 42790075Sobrienpcib_map_check(uint32_t *map, uint32_t start, uint32_t bits) 42890075Sobrien{ 42990075Sobrien uint32_t i; 43090075Sobrien 43190075Sobrien for (i = start; i < start + bits; i++) 43290075Sobrien if (pcib_bit_get(map, i)) 43390075Sobrien return (0); 43490075Sobrien 435117395Skan return (1); 436117395Skan} 437117395Skan 438117395Skanstatic inline void 439117395Skanpcib_map_set(uint32_t *map, uint32_t start, uint32_t bits) 44090075Sobrien{ 44190075Sobrien uint32_t i; 442117395Skan 44390075Sobrien for (i = start; i < start + bits; i++) 444117395Skan pcib_bit_set(map, i); 445117395Skan} 446117395Skan 44790075Sobrien/* 44890075Sobrien * The idea of this allocator is taken from ARM No-Cache memory 44990075Sobrien * management code (sys/arm/arm/vm_machdep.c). 45090075Sobrien */ 45190075Sobrienstatic bus_addr_t 452117395Skanpcib_alloc(struct mv_pcib_softc *sc, uint32_t smask) 45390075Sobrien{ 45490075Sobrien uint32_t bits, bits_limit, i, *map, min_alloc, size; 45590075Sobrien bus_addr_t addr = 0; 456117395Skan bus_addr_t base; 457117395Skan 458117395Skan if (smask & 1) { 459117395Skan base = sc->sc_io_base; 460117395Skan min_alloc = PCI_MIN_IO_ALLOC; 46190075Sobrien bits_limit = sc->sc_io_size / min_alloc; 46290075Sobrien map = sc->sc_io_map; 46390075Sobrien smask &= ~0x3; 46490075Sobrien } else { 465117395Skan base = sc->sc_mem_base; 466117395Skan min_alloc = PCI_MIN_MEM_ALLOC; 46790075Sobrien bits_limit = sc->sc_mem_size / min_alloc; 46890075Sobrien map = sc->sc_mem_map; 469117395Skan smask &= ~0xF; 47090075Sobrien } 47190075Sobrien 47290075Sobrien size = ~smask + 1; 47390075Sobrien bits = size / min_alloc; 474117395Skan 475117395Skan for (i = 0; i + bits <= bits_limit; i += bits) 476117395Skan if (pcib_map_check(map, i, bits)) { 477117395Skan pcib_map_set(map, i, bits); 478117395Skan addr = base + (i * min_alloc); 479117395Skan return (addr); 480117395Skan } 481117395Skan 48290075Sobrien return (addr); 48390075Sobrien} 48490075Sobrien 48590075Sobrienstatic int 48690075Sobrienmv_pcib_init_bar(struct mv_pcib_softc *sc, int bus, int slot, int func, 48790075Sobrien int barno) 48890075Sobrien{ 48990075Sobrien uint32_t addr, bar; 49090075Sobrien int reg, width; 49190075Sobrien 49290075Sobrien reg = PCIR_BAR(barno); 49390075Sobrien 49490075Sobrien /* 49590075Sobrien * Need to init the BAR register with 0xffffffff before correct 49690075Sobrien * value can be read. 49790075Sobrien */ 49890075Sobrien mv_pcib_write_config(sc->sc_dev, bus, slot, func, reg, ~0, 4); 49990075Sobrien bar = mv_pcib_read_config(sc->sc_dev, bus, slot, func, reg, 4); 50090075Sobrien if (bar == 0) 50190075Sobrien return (1); 50290075Sobrien 50390075Sobrien /* Calculate BAR size: 64 or 32 bit (in 32-bit units) */ 50490075Sobrien width = ((bar & 7) == 4) ? 2 : 1; 50590075Sobrien 50690075Sobrien addr = pcib_alloc(sc, bar); 50790075Sobrien if (!addr) 50890075Sobrien return (-1); 50990075Sobrien 51090075Sobrien if (bootverbose) 51190075Sobrien printf("PCI %u:%u:%u: reg %x: smask=%08x: addr=%08x\n", 51290075Sobrien bus, slot, func, reg, bar, addr); 513117395Skan 51490075Sobrien mv_pcib_write_config(sc->sc_dev, bus, slot, func, reg, addr, 4); 51590075Sobrien if (width == 2) 51690075Sobrien mv_pcib_write_config(sc->sc_dev, bus, slot, func, reg + 4, 51790075Sobrien 0, 4); 518117395Skan 519117395Skan return (width); 520117395Skan} 521117395Skan 522117395Skanstatic void 523117395Skanmv_pcib_init_bridge(struct mv_pcib_softc *sc, int bus, int slot, int func) 524117395Skan{ 525117395Skan bus_addr_t io_base, mem_base; 526117395Skan uint32_t io_limit, mem_limit; 527117395Skan int secbus; 528117395Skan 529117395Skan io_base = sc->sc_io_base; 530117395Skan io_limit = io_base + sc->sc_io_size - 1; 531117395Skan mem_base = sc->sc_mem_base; 532117395Skan mem_limit = mem_base + sc->sc_mem_size - 1; 533117395Skan 534117395Skan /* Configure I/O decode registers */ 535117395Skan mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_IOBASEL_1, 536117395Skan io_base >> 8, 1); 537117395Skan mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_IOBASEH_1, 538117395Skan io_base >> 16, 2); 539117395Skan mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_IOLIMITL_1, 540117395Skan io_limit >> 8, 1); 541117395Skan mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_IOLIMITH_1, 54290075Sobrien io_limit >> 16, 2); 54390075Sobrien 54490075Sobrien /* Configure memory decode registers */ 54590075Sobrien mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_MEMBASE_1, 54690075Sobrien mem_base >> 16, 2); 54790075Sobrien mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_MEMLIMIT_1, 54890075Sobrien mem_limit >> 16, 2); 54990075Sobrien 55090075Sobrien /* Disable memory prefetch decode */ 55190075Sobrien mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_PMBASEL_1, 55290075Sobrien 0x10, 2); 55390075Sobrien mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_PMBASEH_1, 55490075Sobrien 0x0, 4); 55590075Sobrien mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_PMLIMITL_1, 55690075Sobrien 0xF, 2); 55790075Sobrien mv_pcib_write_config(sc->sc_dev, bus, slot, func, PCIR_PMLIMITH_1, 55890075Sobrien 0x0, 4); 55990075Sobrien 56090075Sobrien secbus = mv_pcib_read_config(sc->sc_dev, bus, slot, func, 56190075Sobrien PCIR_SECBUS_1, 1); 56290075Sobrien 56390075Sobrien /* Configure buses behind the bridge */ 56490075Sobrien mv_pcib_init(sc, secbus, PCI_SLOTMAX); 56590075Sobrien} 56690075Sobrien 56790075Sobrienstatic int 56890075Sobrienmv_pcib_init(struct mv_pcib_softc *sc, int bus, int maxslot) 56990075Sobrien{ 57090075Sobrien int slot, func, maxfunc, error; 57190075Sobrien uint8_t hdrtype, command, class, subclass; 57290075Sobrien 57390075Sobrien for (slot = 0; slot <= maxslot; slot++) { 57490075Sobrien maxfunc = 0; 57590075Sobrien for (func = 0; func <= maxfunc; func++) { 57690075Sobrien hdrtype = mv_pcib_read_config(sc->sc_dev, bus, slot, 57790075Sobrien func, PCIR_HDRTYPE, 1); 57890075Sobrien 57990075Sobrien if ((hdrtype & PCIM_HDRTYPE) > PCI_MAXHDRTYPE) 58090075Sobrien continue; 58190075Sobrien 58290075Sobrien if (func == 0 && (hdrtype & PCIM_MFDEV)) 58390075Sobrien maxfunc = PCI_FUNCMAX; 58490075Sobrien 58590075Sobrien command = mv_pcib_read_config(sc->sc_dev, bus, slot, 58690075Sobrien func, PCIR_COMMAND, 1); 58790075Sobrien command &= ~(PCIM_CMD_MEMEN | PCIM_CMD_PORTEN); 58890075Sobrien mv_pcib_write_config(sc->sc_dev, bus, slot, func, 58990075Sobrien PCIR_COMMAND, command, 1); 59090075Sobrien 59190075Sobrien error = mv_pcib_init_all_bars(sc, bus, slot, func, 59290075Sobrien hdrtype); 59390075Sobrien 59490075Sobrien if (error) 59590075Sobrien return (error); 59690075Sobrien 59790075Sobrien command |= PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN | 59890075Sobrien PCIM_CMD_PORTEN; 59990075Sobrien mv_pcib_write_config(sc->sc_dev, bus, slot, func, 600117395Skan PCIR_COMMAND, command, 1); 60190075Sobrien 602117395Skan /* Handle PCI-PCI bridges */ 603117395Skan class = mv_pcib_read_config(sc->sc_dev, bus, slot, 604117395Skan func, PCIR_CLASS, 1); 60590075Sobrien subclass = mv_pcib_read_config(sc->sc_dev, bus, slot, 60690075Sobrien func, PCIR_SUBCLASS, 1); 60790075Sobrien 608117395Skan if (class != PCIC_BRIDGE || 609117395Skan subclass != PCIS_BRIDGE_PCI) 610117395Skan continue; 61190075Sobrien 61290075Sobrien mv_pcib_init_bridge(sc, bus, slot, func); 61390075Sobrien } 61490075Sobrien } 61590075Sobrien 616117395Skan /* Enable all ABCD interrupts */ 617117395Skan pcib_write_irq_mask(sc, (0xF << 24)); 618117395Skan 61990075Sobrien return (0); 62090075Sobrien} 62190075Sobrien 62290075Sobrienstatic int 62390075Sobrienmv_pcib_init_all_bars(struct mv_pcib_softc *sc, int bus, int slot, 62490075Sobrien int func, int hdrtype) 62590075Sobrien{ 62690075Sobrien int maxbar, bar, i; 62790075Sobrien 62890075Sobrien maxbar = (hdrtype & PCIM_HDRTYPE) ? 0 : 6; 62990075Sobrien bar = 0; 63090075Sobrien 631117395Skan /* Program the base address registers */ 63290075Sobrien while (bar < maxbar) { 633117395Skan i = mv_pcib_init_bar(sc, bus, slot, func, bar); 634117395Skan bar += i; 63590075Sobrien if (i < 0) { 63690075Sobrien device_printf(sc->sc_dev, 637117395Skan "PCI IO/Memory space exhausted\n"); 63890075Sobrien return (ENOMEM); 63990075Sobrien } 64096263Sobrien } 64196263Sobrien 64290075Sobrien return (0); 64390075Sobrien} 64490075Sobrien 64590075Sobrienstatic struct resource * 64690075Sobrienmv_pcib_alloc_resource(device_t dev, device_t child, int type, int *rid, 64790075Sobrien u_long start, u_long end, u_long count, u_int flags) 64890075Sobrien{ 64990075Sobrien struct mv_pcib_softc *sc = device_get_softc(dev); 65090075Sobrien struct rman *rm = NULL; 65190075Sobrien struct resource *res; 65290075Sobrien 65390075Sobrien switch (type) { 65490075Sobrien case SYS_RES_IOPORT: 65590075Sobrien rm = &sc->sc_io_rman; 65690075Sobrien break; 65790075Sobrien case SYS_RES_MEMORY: 65890075Sobrien rm = &sc->sc_mem_rman; 65990075Sobrien break; 66090075Sobrien default: 66190075Sobrien return (BUS_ALLOC_RESOURCE(device_get_parent(dev), dev, 66290075Sobrien type, rid, start, end, count, flags)); 66390075Sobrien }; 66490075Sobrien 66590075Sobrien if ((start == 0UL) && (end == ~0UL)) { 666117395Skan start = sc->sc_mem_base; 667117395Skan end = sc->sc_mem_base + sc->sc_mem_size - 1; 668117395Skan count = sc->sc_mem_size; 669117395Skan } 67090075Sobrien 67190075Sobrien if ((start < sc->sc_mem_base) || (start + count - 1 != end) || 67290075Sobrien (end > sc->sc_mem_base + sc->sc_mem_size - 1)) 67390075Sobrien return (NULL); 67490075Sobrien 67590075Sobrien res = rman_reserve_resource(rm, start, end, count, flags, child); 67690075Sobrien if (res == NULL) 67790075Sobrien return (NULL); 67890075Sobrien 679117395Skan rman_set_rid(res, *rid); 680117395Skan rman_set_bustag(res, fdtbus_bs_tag); 681117395Skan rman_set_bushandle(res, start); 682117395Skan 683117395Skan if (flags & RF_ACTIVE) 684117395Skan if (bus_activate_resource(child, type, *rid, res)) { 685117395Skan rman_release_resource(res); 686117395Skan return (NULL); 687117395Skan } 688117395Skan 689117395Skan return (res); 69090075Sobrien} 69190075Sobrien 69290075Sobrienstatic int 69390075Sobrienmv_pcib_release_resource(device_t dev, device_t child, int type, int rid, 69490075Sobrien struct resource *res) 695117395Skan{ 69690075Sobrien 69790075Sobrien if (type != SYS_RES_IOPORT && type != SYS_RES_MEMORY) 698117395Skan return (BUS_RELEASE_RESOURCE(device_get_parent(dev), child, 69990075Sobrien type, rid, res)); 70090075Sobrien 70190075Sobrien return (rman_release_resource(res)); 702117395Skan} 703117395Skan 704117395Skanstatic int 705117395Skanmv_pcib_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) 70690075Sobrien{ 70790075Sobrien struct mv_pcib_softc *sc = device_get_softc(dev); 70890075Sobrien 70990075Sobrien switch (which) { 71090075Sobrien case PCIB_IVAR_BUS: 71190075Sobrien *result = sc->sc_busnr; 71290075Sobrien return (0); 713117395Skan case PCIB_IVAR_DOMAIN: 71490075Sobrien *result = device_get_unit(dev); 71590075Sobrien return (0); 71690075Sobrien } 71790075Sobrien 71890075Sobrien return (ENOENT); 71990075Sobrien} 72090075Sobrien 72190075Sobrienstatic int 72290075Sobrienmv_pcib_write_ivar(device_t dev, device_t child, int which, uintptr_t value) 72390075Sobrien{ 72490075Sobrien struct mv_pcib_softc *sc = device_get_softc(dev); 72590075Sobrien 72690075Sobrien switch (which) { 72790075Sobrien case PCIB_IVAR_BUS: 72890075Sobrien sc->sc_busnr = value; 72990075Sobrien return (0); 73090075Sobrien } 73190075Sobrien 73290075Sobrien return (ENOENT); 73390075Sobrien} 73490075Sobrien 73590075Sobrienstatic inline void 73690075Sobrienpcib_write_irq_mask(struct mv_pcib_softc *sc, uint32_t mask) 73790075Sobrien{ 73896263Sobrien 73996263Sobrien if (!sc->sc_type != MV_TYPE_PCI) 740117395Skan return; 741117395Skan 74296263Sobrien bus_space_write_4(sc->sc_bst, sc->sc_bsh, PCIE_REG_IRQ_MASK, mask); 74396263Sobrien} 74496263Sobrien 74596263Sobrienstatic void 74696263Sobrienmv_pcib_hw_cfginit(void) 74796263Sobrien{ 74896263Sobrien static int opened = 0; 74996263Sobrien 75096263Sobrien if (opened) 751117395Skan return; 752117395Skan 753117395Skan mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN); 754117395Skan opened = 1; 755117395Skan} 756117395Skan 757117395Skanstatic uint32_t 758117395Skanmv_pcib_hw_cfgread(struct mv_pcib_softc *sc, u_int bus, u_int slot, 759117395Skan u_int func, u_int reg, int bytes) 760117395Skan{ 761117395Skan uint32_t addr, data, ca, cd; 762117395Skan 763117395Skan ca = (sc->sc_type != MV_TYPE_PCI) ? 764117395Skan PCIE_REG_CFG_ADDR : PCI_REG_CFG_ADDR; 76590075Sobrien cd = (sc->sc_type != MV_TYPE_PCI) ? 76690075Sobrien PCIE_REG_CFG_DATA : PCI_REG_CFG_DATA; 767117395Skan addr = PCI_CFG_ENA | PCI_CFG_BUS(bus) | PCI_CFG_DEV(slot) | 76890075Sobrien PCI_CFG_FUN(func) | PCI_CFG_PCIE_REG(reg); 76990075Sobrien 77090075Sobrien mtx_lock_spin(&pcicfg_mtx); 77190075Sobrien bus_space_write_4(sc->sc_bst, sc->sc_bsh, ca, addr); 77296263Sobrien 77390075Sobrien data = ~0; 77490075Sobrien switch (bytes) { 77590075Sobrien case 1: 77690075Sobrien data = bus_space_read_1(sc->sc_bst, sc->sc_bsh, 77790075Sobrien cd + (reg & 3)); 77890075Sobrien break; 77990075Sobrien case 2: 78090075Sobrien data = le16toh(bus_space_read_2(sc->sc_bst, sc->sc_bsh, 78190075Sobrien cd + (reg & 2))); 78290075Sobrien break; 78390075Sobrien case 4: 78490075Sobrien data = le32toh(bus_space_read_4(sc->sc_bst, sc->sc_bsh, 785117395Skan cd)); 786117395Skan break; 787117395Skan } 788117395Skan mtx_unlock_spin(&pcicfg_mtx); 78990075Sobrien return (data); 79090075Sobrien} 79190075Sobrien 79290075Sobrienstatic void 79390075Sobrienmv_pcib_hw_cfgwrite(struct mv_pcib_softc *sc, u_int bus, u_int slot, 79490075Sobrien u_int func, u_int reg, uint32_t data, int bytes) 79590075Sobrien{ 796117395Skan uint32_t addr, ca, cd; 79790075Sobrien 79890075Sobrien ca = (sc->sc_type != MV_TYPE_PCI) ? 79990075Sobrien PCIE_REG_CFG_ADDR : PCI_REG_CFG_ADDR; 80090075Sobrien cd = (sc->sc_type != MV_TYPE_PCI) ? 801117395Skan PCIE_REG_CFG_DATA : PCI_REG_CFG_DATA; 802117395Skan addr = PCI_CFG_ENA | PCI_CFG_BUS(bus) | PCI_CFG_DEV(slot) | 803117395Skan PCI_CFG_FUN(func) | PCI_CFG_PCIE_REG(reg); 804117395Skan 805117395Skan mtx_lock_spin(&pcicfg_mtx); 806117395Skan bus_space_write_4(sc->sc_bst, sc->sc_bsh, ca, addr); 80790075Sobrien 80890075Sobrien switch (bytes) { 80990075Sobrien case 1: 810117395Skan bus_space_write_1(sc->sc_bst, sc->sc_bsh, 81190075Sobrien cd + (reg & 3), data); 812117395Skan break; 81390075Sobrien case 2: 81490075Sobrien bus_space_write_2(sc->sc_bst, sc->sc_bsh, 81590075Sobrien cd + (reg & 2), htole16(data)); 81690075Sobrien break; 81790075Sobrien case 4: 81890075Sobrien bus_space_write_4(sc->sc_bst, sc->sc_bsh, 819117395Skan cd, htole32(data)); 82090075Sobrien break; 821117395Skan } 822117395Skan mtx_unlock_spin(&pcicfg_mtx); 823117395Skan} 82490075Sobrien 82590075Sobrienstatic int 82690075Sobrienmv_pcib_maxslots(device_t dev) 827117395Skan{ 82890075Sobrien struct mv_pcib_softc *sc = device_get_softc(dev); 82990075Sobrien 83090075Sobrien return ((sc->sc_type != MV_TYPE_PCI) ? 1 : PCI_SLOTMAX); 83190075Sobrien} 83290075Sobrien 83390075Sobrienstatic uint32_t 83490075Sobrienmv_pcib_read_config(device_t dev, u_int bus, u_int slot, u_int func, 83590075Sobrien u_int reg, int bytes) 83690075Sobrien{ 83790075Sobrien struct mv_pcib_softc *sc = device_get_softc(dev); 83890075Sobrien 83990075Sobrien /* Return ~0 if link is inactive or trying to read from Root */ 84090075Sobrien if ((bus_space_read_4(sc->sc_bst, sc->sc_bsh, PCIE_REG_STATUS) & 84190075Sobrien PCIE_STATUS_LINK_DOWN) || (slot == 0)) 84290075Sobrien return (~0U); 84390075Sobrien 84490075Sobrien return (mv_pcib_hw_cfgread(sc, bus, slot, func, reg, bytes)); 84590075Sobrien} 84690075Sobrien 84790075Sobrienstatic void 84890075Sobrienmv_pcib_write_config(device_t dev, u_int bus, u_int slot, u_int func, 84990075Sobrien u_int reg, uint32_t val, int bytes) 85090075Sobrien{ 85190075Sobrien struct mv_pcib_softc *sc = device_get_softc(dev); 85290075Sobrien 853117395Skan /* Return if link is inactive or trying to write to Root */ 854117395Skan if ((bus_space_read_4(sc->sc_bst, sc->sc_bsh, PCIE_REG_STATUS) & 855117395Skan PCIE_STATUS_LINK_DOWN) || (slot == 0)) 856117395Skan return; 857117395Skan 858117395Skan mv_pcib_hw_cfgwrite(sc, bus, slot, func, reg, val, bytes); 859117395Skan} 860117395Skan 861117395Skanstatic int 862117395Skanmv_pcib_route_interrupt(device_t pcib, device_t dev, int pin) 863117395Skan{ 864117395Skan struct mv_pcib_softc *sc; 86590075Sobrien int err, interrupt; 86690075Sobrien 86790075Sobrien sc = device_get_softc(pcib); 86890075Sobrien 86990075Sobrien err = fdt_pci_route_intr(pci_get_bus(dev), pci_get_slot(dev), 87090075Sobrien pci_get_function(dev), pin, &sc->sc_intr_info, &interrupt); 871117395Skan if (err == 0) 87290075Sobrien return (interrupt); 87390075Sobrien 87490075Sobrien device_printf(pcib, "could not route pin %d for device %d.%d\n", 87590075Sobrien pin, pci_get_slot(dev), pci_get_function(dev)); 87690075Sobrien return (PCI_INVALID_IRQ); 87790075Sobrien} 87890075Sobrien 87990075Sobrienstatic int 880117395Skanmv_pcib_decode_win(phandle_t node, struct mv_pcib_softc *sc) 881117395Skan{ 882117395Skan struct fdt_pci_range io_space, mem_space; 883117395Skan device_t dev; 884117395Skan int error; 885117395Skan 886117395Skan dev = sc->sc_dev; 887117395Skan 888117395Skan if ((error = fdt_pci_ranges(node, &io_space, &mem_space)) != 0) { 889117395Skan device_printf(dev, "could not retrieve 'ranges' data\n"); 890117395Skan return (error); 891117395Skan } 892117395Skan 893117395Skan /* Configure CPU decoding windows */ 894117395Skan error = decode_win_cpu_set(sc->sc_win_target, 895117395Skan sc->sc_io_win_attr, io_space.base_parent, io_space.len, ~0); 896117395Skan if (error < 0) { 897117395Skan device_printf(dev, "could not set up CPU decode " 898117395Skan "window for PCI IO\n"); 899117395Skan return (ENXIO); 900117395Skan } 901117395Skan error = decode_win_cpu_set(sc->sc_win_target, 902117395Skan sc->sc_mem_win_attr, mem_space.base_parent, mem_space.len, 903117395Skan mem_space.base_parent); 904117395Skan if (error < 0) { 905117395Skan device_printf(dev, "could not set up CPU decode " 906117395Skan "windows for PCI MEM\n"); 907117395Skan return (ENXIO); 908117395Skan } 909117395Skan 910117395Skan sc->sc_io_base = io_space.base_parent; 911117395Skan sc->sc_io_size = io_space.len; 912117395Skan 913117395Skan sc->sc_mem_base = mem_space.base_parent; 914117395Skan sc->sc_mem_size = mem_space.len; 915117395Skan 916117395Skan return (0); 917117395Skan} 918117395Skan 919117395Skanstatic int 920117395Skanmv_pcib_intr_info(phandle_t node, struct mv_pcib_softc *sc) 921117395Skan{ 922117395Skan int error; 923117395Skan 924117395Skan if ((error = fdt_pci_intr_info(node, &sc->sc_intr_info)) != 0) 925117395Skan return (error); 926117395Skan 92790075Sobrien return (0); 92890075Sobrien} 92990075Sobrien 93090075Sobrien