diff --git a/sys/powerpc/mpc85xx/fsl_diu.c b/sys/powerpc/mpc85xx/fsl_diu.c index b116809011dc..c29df5e5393b 100644 --- a/sys/powerpc/mpc85xx/fsl_diu.c +++ b/sys/powerpc/mpc85xx/fsl_diu.c @@ -1,469 +1,467 @@ /*- * Copyright (c) 2016 Justin Hibbits * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fb_if.h" #define DIU_DESC_1 0x000 /* Plane1 Area Descriptor Pointer Register */ #define DIU_DESC_2 0x004 /* Plane2 Area Descriptor Pointer Register */ #define DIU_DESC_3 0x008 /* Plane3 Area Descriptor Pointer Register */ #define DIU_GAMMA 0x00C /* Gamma Register */ #define DIU_PALETTE 0x010 /* Palette Register */ #define DIU_CURSOR 0x014 /* Cursor Register */ #define DIU_CURS_POS 0x018 /* Cursor Position Register */ #define CURSOR_Y_SHIFT 16 #define CURSOR_X_SHIFT 0 #define DIU_DIU_MODE 0x01C /* DIU4 Mode */ #define DIU_MODE_M 0x7 #define DIU_MODE_S 0 #define DIU_MODE_NORMAL 0x1 #define DIU_MODE_2 0x2 #define DIU_MODE_3 0x3 #define DIU_MODE_COLBAR 0x4 #define DIU_BGND 0x020 /* Background */ #define DIU_BGND_WB 0x024 /* Background Color in write back Mode Register */ #define DIU_DISP_SIZE 0x028 /* Display Size */ #define DELTA_Y_S 16 #define DELTA_X_S 0 #define DIU_WB_SIZE 0x02C /* Write back Plane Size Register */ #define DELTA_Y_WB_S 16 #define DELTA_X_WB_S 0 #define DIU_WB_MEM_ADDR 0x030 /* Address to Store the write back Plane Register */ #define DIU_HSYN_PARA 0x034 /* Horizontal Sync Parameter */ #define BP_H_SHIFT 22 #define PW_H_SHIFT 11 #define FP_H_SHIFT 0 #define DIU_VSYN_PARA 0x038 /* Vertical Sync Parameter */ #define BP_V_SHIFT 22 #define PW_V_SHIFT 11 #define FP_V_SHIFT 0 #define DIU_SYNPOL 0x03C /* Synchronize Polarity */ #define BP_VS (1 << 4) #define BP_HS (1 << 3) #define INV_CS (1 << 2) #define INV_VS (1 << 1) #define INV_HS (1 << 0) #define INV_PDI_VS (1 << 8) /* Polarity of PDI input VSYNC. */ #define INV_PDI_HS (1 << 9) /* Polarity of PDI input HSYNC. */ #define INV_PDI_DE (1 << 10) /* Polarity of PDI input DE. */ #define DIU_THRESHOLD 0x040 /* Threshold */ #define LS_BF_VS_SHIFT 16 #define OUT_BUF_LOW_SHIFT 0 #define DIU_INT_STATUS 0x044 /* Interrupt Status */ #define DIU_INT_MASK 0x048 /* Interrupt Mask */ #define DIU_COLBAR_1 0x04C /* COLBAR_1 */ #define DIU_COLORBARn_R(x) ((x & 0xff) << 16) #define DIU_COLORBARn_G(x) ((x & 0xff) << 8) #define DIU_COLORBARn_B(x) ((x & 0xff) << 0) #define DIU_COLBAR_2 0x050 /* COLBAR_2 */ #define DIU_COLBAR_3 0x054 /* COLBAR_3 */ #define DIU_COLBAR_4 0x058 /* COLBAR_4 */ #define DIU_COLBAR_5 0x05c /* COLBAR_5 */ #define DIU_COLBAR_6 0x060 /* COLBAR_6 */ #define DIU_COLBAR_7 0x064 /* COLBAR_7 */ #define DIU_COLBAR_8 0x068 /* COLBAR_8 */ #define DIU_FILLING 0x06C /* Filling Register */ #define DIU_PLUT 0x070 /* Priority Look Up Table Register */ /* Control Descriptor */ #define DIU_CTRLDESCL(n, m) 0x200 + (0x40 * n) + 0x4 * (m - 1) #define DIU_CTRLDESCLn_1(n) DIU_CTRLDESCL(n, 1) #define DIU_CTRLDESCLn_2(n) DIU_CTRLDESCL(n, 2) #define DIU_CTRLDESCLn_3(n) DIU_CTRLDESCL(n, 3) #define TRANS_SHIFT 20 #define DIU_CTRLDESCLn_4(n) DIU_CTRLDESCL(n, 4) #define BPP_MASK 0xf /* Bit per pixel Mask */ #define BPP_SHIFT 16 /* Bit per pixel Shift */ #define BPP24 0x5 #define EN_LAYER (1 << 31) /* Enable the layer */ #define DIU_CTRLDESCLn_5(n) DIU_CTRLDESCL(n, 5) #define DIU_CTRLDESCLn_6(n) DIU_CTRLDESCL(n, 6) #define DIU_CTRLDESCLn_7(n) DIU_CTRLDESCL(n, 7) #define DIU_CTRLDESCLn_8(n) DIU_CTRLDESCL(n, 8) #define DIU_CTRLDESCLn_9(n) DIU_CTRLDESCL(n, 9) #define NUM_LAYERS 1 struct panel_info { uint32_t panel_width; uint32_t panel_height; uint32_t panel_hbp; uint32_t panel_hpw; uint32_t panel_hfp; uint32_t panel_vbp; uint32_t panel_vpw; uint32_t panel_vfp; uint32_t panel_freq; uint32_t clk_div; }; struct diu_area_descriptor { uint32_t pixel_format; uint32_t bitmap_address; uint32_t source_size; uint32_t aoi_size; uint32_t aoi_offset; uint32_t display_offset; uint32_t chroma_key_max; uint32_t chroma_key_min; uint32_t next_ad_addr; } __aligned(32); struct diu_softc { struct resource *res[2]; void *ih; device_t sc_dev; device_t sc_fbd; /* fbd child */ struct fb_info sc_info; struct panel_info sc_panel; struct diu_area_descriptor *sc_planes[3]; uint8_t *sc_gamma; uint8_t *sc_cursor; }; static struct resource_spec diu_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { -1, 0 } }; static int diu_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "fsl,diu")) return (ENXIO); device_set_desc(dev, "Freescale Display Interface Unit"); return (BUS_PROBE_DEFAULT); } static void diu_intr(void *arg) { struct diu_softc *sc; int reg; sc = arg; /* Ack interrupts */ reg = bus_read_4(sc->res[0], DIU_INT_STATUS); bus_write_4(sc->res[0], DIU_INT_STATUS, reg); /* TODO interrupt handler */ } static int diu_set_pxclk(device_t dev, unsigned int freq) { - phandle_t node; unsigned long bus_freq; uint32_t pxclk_set; uint32_t clkdvd; - node = ofw_bus_get_node(device_get_parent(dev)); if ((bus_freq = mpc85xx_get_platform_clock()) <= 0) { device_printf(dev, "Unable to get bus frequency\n"); return (ENXIO); } /* freq is in kHz */ freq *= 1000; /* adding freq/2 to round-to-closest */ pxclk_set = min(max((bus_freq + freq/2) / freq, 2), 255) << 16; pxclk_set |= OCP85XX_CLKDVDR_PXCKEN; clkdvd = ccsr_read4(OCP85XX_CLKDVDR); clkdvd &= ~(OCP85XX_CLKDVDR_PXCKEN | OCP85XX_CLKDVDR_PXCKINV | OCP85XX_CLKDVDR_PXCLK_MASK); ccsr_write4(OCP85XX_CLKDVDR, clkdvd); ccsr_write4(OCP85XX_CLKDVDR, clkdvd | pxclk_set); return (0); } static int diu_init(struct diu_softc *sc) { struct panel_info *panel; int reg; panel = &sc->sc_panel; /* Temporarily disable the DIU while configuring */ reg = bus_read_4(sc->res[0], DIU_DIU_MODE); reg &= ~(DIU_MODE_M << DIU_MODE_S); bus_write_4(sc->res[0], DIU_DIU_MODE, reg); if (diu_set_pxclk(sc->sc_dev, panel->panel_freq) < 0) { return (ENXIO); } /* Configure DIU */ /* Need to set these somehow later... */ bus_write_4(sc->res[0], DIU_GAMMA, vtophys(sc->sc_gamma)); bus_write_4(sc->res[0], DIU_CURSOR, vtophys(sc->sc_cursor)); bus_write_4(sc->res[0], DIU_CURS_POS, 0); reg = ((sc->sc_info.fb_height) << DELTA_Y_S); reg |= sc->sc_info.fb_width; bus_write_4(sc->res[0], DIU_DISP_SIZE, reg); reg = (panel->panel_hbp << BP_H_SHIFT); reg |= (panel->panel_hpw << PW_H_SHIFT); reg |= (panel->panel_hfp << FP_H_SHIFT); bus_write_4(sc->res[0], DIU_HSYN_PARA, reg); reg = (panel->panel_vbp << BP_V_SHIFT); reg |= (panel->panel_vpw << PW_V_SHIFT); reg |= (panel->panel_vfp << FP_V_SHIFT); bus_write_4(sc->res[0], DIU_VSYN_PARA, reg); bus_write_4(sc->res[0], DIU_BGND, 0); /* Mask all the interrupts */ bus_write_4(sc->res[0], DIU_INT_MASK, 0x3f); /* Reset all layers */ sc->sc_planes[0] = contigmalloc(sizeof(struct diu_area_descriptor), M_DEVBUF, M_ZERO, 0, BUS_SPACE_MAXADDR_32BIT, 32, 0); bus_write_4(sc->res[0], DIU_DESC_1, vtophys(sc->sc_planes[0])); bus_write_4(sc->res[0], DIU_DESC_2, 0); bus_write_4(sc->res[0], DIU_DESC_3, 0); /* Setup first plane */ /* Area descriptor fields are little endian, so byte swap. */ /* Word 0: Pixel format */ /* Set to 8:8:8:8 ARGB, 4 bytes per pixel, no flip. */ #define MAKE_PXLFMT(as,rs,gs,bs,a,r,g,b,f,s) \ htole32((as << (4 * a)) | (rs << 4 * r) | \ (gs << 4 * g) | (bs << 4 * b) | \ (f << 28) | (s << 16) | \ (a << 25) | (r << 19) | \ (g << 21) | (b << 24)) reg = MAKE_PXLFMT(8, 8, 8, 8, 3, 2, 1, 0, 1, 3); sc->sc_planes[0]->pixel_format = reg; /* Word 1: Bitmap address */ sc->sc_planes[0]->bitmap_address = htole32(sc->sc_info.fb_pbase); /* Word 2: Source size/global alpha */ reg = (sc->sc_info.fb_width | (sc->sc_info.fb_height << 12)); sc->sc_planes[0]->source_size = htole32(reg); /* Word 3: AOI Size */ reg = (sc->sc_info.fb_width | (sc->sc_info.fb_height << 16)); sc->sc_planes[0]->aoi_size = htole32(reg); /* Word 4: AOI Offset */ sc->sc_planes[0]->aoi_offset = 0; /* Word 5: Display offset */ sc->sc_planes[0]->display_offset = 0; /* Word 6: Chroma key max */ sc->sc_planes[0]->chroma_key_max = 0; /* Word 7: Chroma key min */ reg = 255 << 16 | 255 << 8 | 255; sc->sc_planes[0]->chroma_key_min = htole32(reg); /* Word 8: Next AD */ sc->sc_planes[0]->next_ad_addr = 0; /* TODO: derive this from the panel size */ bus_write_4(sc->res[0], DIU_PLUT, 0x1f5f666); /* Enable DIU in normal mode */ reg = bus_read_4(sc->res[0], DIU_DIU_MODE); reg &= ~(DIU_MODE_M << DIU_MODE_S); reg |= (DIU_MODE_NORMAL << DIU_MODE_S); bus_write_4(sc->res[0], DIU_DIU_MODE, reg); return (0); } static int diu_attach(device_t dev) { struct edid_info edid; struct diu_softc *sc; const struct videomode *videomode; void *edid_cells; const char *vm_name; phandle_t node; int h, r, w; int err, i; sc = device_get_softc(dev); sc->sc_dev = dev; if (bus_alloc_resources(dev, diu_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } node = ofw_bus_get_node(dev); /* Setup interrupt handler */ err = bus_setup_intr(dev, sc->res[1], INTR_TYPE_BIO | INTR_MPSAFE, NULL, diu_intr, sc, &sc->ih); if (err) { device_printf(dev, "Unable to alloc interrupt resource.\n"); return (ENXIO); } /* TODO: Eventually, allow EDID to be dynamically provided. */ if (OF_getprop_alloc(node, "edid", &edid_cells) <= 0) { /* Get a resource hint: hint.fb.N.mode */ if (resource_string_value(device_get_name(dev), device_get_unit(dev), "mode", &vm_name) != 0) { device_printf(dev, "No EDID data and no video-mode env set\n"); return (ENXIO); } } if (edid_cells != NULL) { if (edid_parse(edid_cells, &edid) != 0) { device_printf(dev, "Error parsing EDID\n"); OF_prop_free(edid_cells); return (ENXIO); } videomode = edid.edid_preferred_mode; } else { /* Parse video-mode kenv variable. */ if ((err = sscanf(vm_name, "%dx%d@%d", &w, &h, &r)) != 3) { device_printf(dev, "Cannot parse video mode: %s\n", vm_name); return (ENXIO); } videomode = pick_mode_by_ref(w, h, r); if (videomode == NULL) { device_printf(dev, "Cannot find mode for %dx%d@%d", w, h, r); return (ENXIO); } } sc->sc_panel.panel_width = videomode->hdisplay; sc->sc_panel.panel_height = videomode->vdisplay; sc->sc_panel.panel_hbp = videomode->hsync_start - videomode->hdisplay; sc->sc_panel.panel_hfp = videomode->htotal - videomode->hsync_end; sc->sc_panel.panel_hpw = videomode->hsync_end - videomode->hsync_start; sc->sc_panel.panel_vbp = videomode->vsync_start - videomode->vdisplay; sc->sc_panel.panel_vfp = videomode->vtotal - videomode->vsync_end; sc->sc_panel.panel_vpw = videomode->vsync_end - videomode->vsync_start; sc->sc_panel.panel_freq = videomode->dot_clock; sc->sc_info.fb_width = sc->sc_panel.panel_width; sc->sc_info.fb_height = sc->sc_panel.panel_height; sc->sc_info.fb_stride = sc->sc_info.fb_width * 4; sc->sc_info.fb_bpp = sc->sc_info.fb_depth = 32; sc->sc_info.fb_size = sc->sc_info.fb_height * sc->sc_info.fb_stride; sc->sc_info.fb_vbase = (intptr_t)contigmalloc(sc->sc_info.fb_size, M_DEVBUF, M_ZERO, 0, BUS_SPACE_MAXADDR_32BIT, PAGE_SIZE, 0); sc->sc_info.fb_pbase = (intptr_t)vtophys(sc->sc_info.fb_vbase); sc->sc_info.fb_flags = FB_FLAG_MEMATTR; sc->sc_info.fb_memattr = VM_MEMATTR_DEFAULT; /* Gamma table is 3 consecutive segments of 256 bytes. */ sc->sc_gamma = contigmalloc(3 * 256, M_DEVBUF, 0, 0, BUS_SPACE_MAXADDR_32BIT, PAGE_SIZE, 0); /* Initialize gamma to default */ for (i = 0; i < 3 * 256; i++) sc->sc_gamma[i] = (i % 256); /* Cursor format is 32x32x16bpp */ sc->sc_cursor = contigmalloc(32 * 32 * 2, M_DEVBUF, M_ZERO, 0, BUS_SPACE_MAXADDR_32BIT, PAGE_SIZE, 0); diu_init(sc); sc->sc_info.fb_name = device_get_nameunit(dev); /* Ask newbus to attach framebuffer device to me. */ sc->sc_fbd = device_add_child(dev, "fbd", device_get_unit(dev)); if (sc->sc_fbd == NULL) device_printf(dev, "Can't attach fbd device\n"); if ((err = device_probe_and_attach(sc->sc_fbd)) != 0) { device_printf(dev, "Failed to attach fbd device: %d\n", err); } return (0); } static struct fb_info * diu_fb_getinfo(device_t dev) { struct diu_softc *sc = device_get_softc(dev); return (&sc->sc_info); } static device_method_t diu_methods[] = { DEVMETHOD(device_probe, diu_probe), DEVMETHOD(device_attach, diu_attach), /* Framebuffer service methods */ DEVMETHOD(fb_getinfo, diu_fb_getinfo), { 0, 0 } }; static driver_t diu_driver = { "fb", diu_methods, sizeof(struct diu_softc), }; static devclass_t diu_devclass; DRIVER_MODULE(fb, simplebus, diu_driver, diu_devclass, 0, 0); diff --git a/sys/powerpc/mpc85xx/pci_mpc85xx.c b/sys/powerpc/mpc85xx/pci_mpc85xx.c index 8d0c21342215..b6f7867f7f74 100644 --- a/sys/powerpc/mpc85xx/pci_mpc85xx.c +++ b/sys/powerpc/mpc85xx/pci_mpc85xx.c @@ -1,958 +1,954 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright 2006-2007 by Juniper Networks. * Copyright 2008 Semihalf. * Copyright 2010 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by Semihalf * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * From: FreeBSD: src/sys/powerpc/mpc85xx/pci_ocp.c,v 1.9 2010/03/23 23:46:28 marcel */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ofw_bus_if.h" #include "pcib_if.h" #include "pic_if.h" #include #include #include #include #define REG_CFG_ADDR 0x0000 #define CONFIG_ACCESS_ENABLE 0x80000000 #define REG_CFG_DATA 0x0004 #define REG_INT_ACK 0x0008 #define REG_PEX_IP_BLK_REV1 0x0bf8 #define IP_MJ_M 0x0000ff00 #define IP_MJ_S 8 #define IP_MN_M 0x000000ff #define IP_MN_S 0 #define REG_POTAR(n) (0x0c00 + 0x20 * (n)) #define REG_POTEAR(n) (0x0c04 + 0x20 * (n)) #define REG_POWBAR(n) (0x0c08 + 0x20 * (n)) #define REG_POWAR(n) (0x0c10 + 0x20 * (n)) #define REG_PITAR(n) (0x0e00 - 0x20 * (n)) #define REG_PIWBAR(n) (0x0e08 - 0x20 * (n)) #define REG_PIWBEAR(n) (0x0e0c - 0x20 * (n)) #define REG_PIWAR(n) (0x0e10 - 0x20 * (n)) #define PIWAR_EN 0x80000000 #define PIWAR_PF 0x40000000 #define PIWAR_TRGT_M 0x00f00000 #define PIWAR_TRGT_S 20 #define PIWAR_TRGT_CCSR 0xe #define PIWAR_TRGT_LOCAL 0xf #define REG_PEX_MES_DR 0x0020 #define REG_PEX_MES_IER 0x0028 #define REG_PEX_ERR_DR 0x0e00 #define REG_PEX_ERR_EN 0x0e08 #define REG_PEX_ERR_DR 0x0e00 #define REG_PEX_ERR_DR_ME 0x80000000 #define REG_PEX_ERR_DR_PCT 0x800000 #define REG_PEX_ERR_DR_PAT 0x400000 #define REG_PEX_ERR_DR_PCAC 0x200000 #define REG_PEX_ERR_DR_PNM 0x100000 #define REG_PEX_ERR_DR_CDNSC 0x80000 #define REG_PEX_ERR_DR_CRSNC 0x40000 #define REG_PEX_ERR_DR_ICCA 0x20000 #define REG_PEX_ERR_DR_IACA 0x10000 #define REG_PEX_ERR_DR_CRST 0x8000 #define REG_PEX_ERR_DR_MIS 0x4000 #define REG_PEX_ERR_DR_IOIS 0x2000 #define REG_PEX_ERR_DR_CIS 0x1000 #define REG_PEX_ERR_DR_CIEP 0x800 #define REG_PEX_ERR_DR_IOIEP 0x400 #define REG_PEX_ERR_DR_OAC 0x200 #define REG_PEX_ERR_DR_IOIA 0x100 #define REG_PEX_ERR_DR_IMBA 0x80 #define REG_PEX_ERR_DR_IIOBA 0x40 #define REG_PEX_ERR_DR_LDDE 0x20 #define REG_PEX_ERR_EN 0x0e08 #define PCIR_LTSSM 0x404 #define LTSSM_STAT_L0 0x16 #define DEVFN(b, s, f) ((b << 16) | (s << 8) | f) #define FSL_NUM_MSIS 256 /* 8 registers of 32 bits (8 hardware IRQs) */ #define PCI_SLOT_FIRST 0x1 /* used to be 0x11 but qemu-ppce500 starts from 0x1 */ struct fsl_pcib_softc { struct ofw_pci_softc pci_sc; device_t sc_dev; struct mtx sc_cfg_mtx; int sc_ip_maj; int sc_ip_min; int sc_iomem_target; bus_addr_t sc_iomem_start, sc_iomem_end; int sc_ioport_target; bus_addr_t sc_ioport_start, sc_ioport_end; struct resource *sc_res; bus_space_handle_t sc_bsh; bus_space_tag_t sc_bst; int sc_rid; struct resource *sc_irq_res; void *sc_ih; int sc_busnr; int sc_pcie; uint8_t sc_pcie_capreg; /* PCI-E Capability Reg Set */ }; struct fsl_pcib_err_dr { const char *msg; uint32_t err_dr_mask; }; struct fsl_msi_map { SLIST_ENTRY(fsl_msi_map) slist; uint32_t irq_base; bus_addr_t target; }; SLIST_HEAD(msi_head, fsl_msi_map) fsl_msis = SLIST_HEAD_INITIALIZER(msi_head); static const struct fsl_pcib_err_dr pci_err[] = { {"ME", REG_PEX_ERR_DR_ME}, {"PCT", REG_PEX_ERR_DR_PCT}, {"PAT", REG_PEX_ERR_DR_PAT}, {"PCAC", REG_PEX_ERR_DR_PCAC}, {"PNM", REG_PEX_ERR_DR_PNM}, {"CDNSC", REG_PEX_ERR_DR_CDNSC}, {"CRSNC", REG_PEX_ERR_DR_CRSNC}, {"ICCA", REG_PEX_ERR_DR_ICCA}, {"IACA", REG_PEX_ERR_DR_IACA}, {"CRST", REG_PEX_ERR_DR_CRST}, {"MIS", REG_PEX_ERR_DR_MIS}, {"IOIS", REG_PEX_ERR_DR_IOIS}, {"CIS", REG_PEX_ERR_DR_CIS}, {"CIEP", REG_PEX_ERR_DR_CIEP}, {"IOIEP", REG_PEX_ERR_DR_IOIEP}, {"OAC", REG_PEX_ERR_DR_OAC}, {"IOIA", REG_PEX_ERR_DR_IOIA}, {"IMBA", REG_PEX_ERR_DR_IMBA}, {"IIOBA", REG_PEX_ERR_DR_IIOBA}, {"LDDE", REG_PEX_ERR_DR_LDDE} }; /* Local forward declerations. */ static uint32_t fsl_pcib_cfgread(struct fsl_pcib_softc *, u_int, u_int, u_int, u_int, int); static void fsl_pcib_cfgwrite(struct fsl_pcib_softc *, u_int, u_int, u_int, u_int, uint32_t, int); static int fsl_pcib_decode_win(phandle_t, struct fsl_pcib_softc *); static void fsl_pcib_err_init(device_t); static void fsl_pcib_inbound(struct fsl_pcib_softc *, int, int, uint64_t, uint64_t, uint64_t); static void fsl_pcib_outbound(struct fsl_pcib_softc *, int, int, uint64_t, uint64_t, uint64_t); /* Forward declerations. */ static int fsl_pcib_attach(device_t); static int fsl_pcib_detach(device_t); static int fsl_pcib_probe(device_t); static int fsl_pcib_maxslots(device_t); static uint32_t fsl_pcib_read_config(device_t, u_int, u_int, u_int, u_int, int); static void fsl_pcib_write_config(device_t, u_int, u_int, u_int, u_int, uint32_t, int); static int fsl_pcib_alloc_msi(device_t dev, device_t child, int count, int maxcount, int *irqs); static int fsl_pcib_release_msi(device_t dev, device_t child, int count, int *irqs); static int fsl_pcib_alloc_msix(device_t dev, device_t child, int *irq); static int fsl_pcib_release_msix(device_t dev, device_t child, int irq); static int fsl_pcib_map_msi(device_t dev, device_t child, int irq, uint64_t *addr, uint32_t *data); static vmem_t *msi_vmem; /* Global MSI vmem, holds all MSI ranges. */ /* * Bus interface definitions. */ static device_method_t fsl_pcib_methods[] = { /* Device interface */ DEVMETHOD(device_probe, fsl_pcib_probe), DEVMETHOD(device_attach, fsl_pcib_attach), DEVMETHOD(device_detach, fsl_pcib_detach), /* pcib interface */ DEVMETHOD(pcib_maxslots, fsl_pcib_maxslots), DEVMETHOD(pcib_read_config, fsl_pcib_read_config), DEVMETHOD(pcib_write_config, fsl_pcib_write_config), DEVMETHOD(pcib_alloc_msi, fsl_pcib_alloc_msi), DEVMETHOD(pcib_release_msi, fsl_pcib_release_msi), DEVMETHOD(pcib_alloc_msix, fsl_pcib_alloc_msix), DEVMETHOD(pcib_release_msix, fsl_pcib_release_msix), DEVMETHOD(pcib_map_msi, fsl_pcib_map_msi), DEVMETHOD_END }; static devclass_t fsl_pcib_devclass; DEFINE_CLASS_1(pcib, fsl_pcib_driver, fsl_pcib_methods, sizeof(struct fsl_pcib_softc), ofw_pcib_driver); EARLY_DRIVER_MODULE(pcib, ofwbus, fsl_pcib_driver, fsl_pcib_devclass, 0, 0, BUS_PASS_BUS); static void fsl_pcib_err_intr(void *v) { struct fsl_pcib_softc *sc; device_t dev; uint32_t err_reg, clear_reg; uint8_t i; dev = (device_t)v; sc = device_get_softc(dev); clear_reg = 0; err_reg = bus_space_read_4(sc->sc_bst, sc->sc_bsh, REG_PEX_ERR_DR); /* Check which one error occurred */ for (i = 0; i < sizeof(pci_err)/sizeof(struct fsl_pcib_err_dr); i++) { if (err_reg & pci_err[i].err_dr_mask) { device_printf(dev, "PCI %d: report %s error\n", device_get_unit(dev), pci_err[i].msg); clear_reg |= pci_err[i].err_dr_mask; } } /* Clear pending errors */ bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PEX_ERR_DR, clear_reg); } static int fsl_pcib_probe(device_t dev) { if (ofw_bus_get_type(dev) == NULL || strcmp(ofw_bus_get_type(dev), "pci") != 0) return (ENXIO); if (!(ofw_bus_is_compatible(dev, "fsl,mpc8540-pci") || ofw_bus_is_compatible(dev, "fsl,mpc8540-pcie") || ofw_bus_is_compatible(dev, "fsl,mpc8548-pcie") || ofw_bus_is_compatible(dev, "fsl,p5020-pcie") || ofw_bus_is_compatible(dev, "fsl,qoriq-pcie-v2.2") || ofw_bus_is_compatible(dev, "fsl,qoriq-pcie"))) return (ENXIO); device_set_desc(dev, "Freescale Integrated PCI/PCI-E Controller"); return (BUS_PROBE_DEFAULT); } static int fsl_pcib_attach(device_t dev) { struct fsl_pcib_softc *sc; phandle_t node; uint32_t cfgreg, brctl, ipreg; int error, rid; uint8_t ltssm, capptr; sc = device_get_softc(dev); sc->sc_dev = dev; sc->sc_rid = 0; sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid, RF_ACTIVE); if (sc->sc_res == NULL) { device_printf(dev, "could not map I/O memory\n"); return (ENXIO); } sc->sc_bst = rman_get_bustag(sc->sc_res); sc->sc_bsh = rman_get_bushandle(sc->sc_res); sc->sc_busnr = 0; ipreg = bus_read_4(sc->sc_res, REG_PEX_IP_BLK_REV1); sc->sc_ip_min = (ipreg & IP_MN_M) >> IP_MN_S; sc->sc_ip_maj = (ipreg & IP_MJ_M) >> IP_MJ_S; mtx_init(&sc->sc_cfg_mtx, "pcicfg", NULL, MTX_SPIN); cfgreg = fsl_pcib_cfgread(sc, 0, 0, 0, PCIR_VENDOR, 2); if (cfgreg != 0x1057 && cfgreg != 0x1957) goto err; capptr = fsl_pcib_cfgread(sc, 0, 0, 0, PCIR_CAP_PTR, 1); while (capptr != 0) { cfgreg = fsl_pcib_cfgread(sc, 0, 0, 0, capptr, 2); switch (cfgreg & 0xff) { case PCIY_PCIX: break; case PCIY_EXPRESS: sc->sc_pcie = 1; sc->sc_pcie_capreg = capptr; break; } capptr = (cfgreg >> 8) & 0xff; } node = ofw_bus_get_node(dev); /* * Initialize generic OF PCI interface (ranges, etc.) */ error = ofw_pcib_init(dev); if (error) return (error); /* * Configure decode windows for PCI(E) access. */ if (fsl_pcib_decode_win(node, sc) != 0) goto err; cfgreg = fsl_pcib_cfgread(sc, 0, 0, 0, PCIR_COMMAND, 2); cfgreg |= PCIM_CMD_SERRESPEN | PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN | PCIM_CMD_PORTEN; fsl_pcib_cfgwrite(sc, 0, 0, 0, PCIR_COMMAND, cfgreg, 2); /* Reset the bus. Needed for Radeon video cards. */ brctl = fsl_pcib_read_config(sc->sc_dev, 0, 0, 0, PCIR_BRIDGECTL_1, 1); brctl |= PCIB_BCR_SECBUS_RESET; fsl_pcib_write_config(sc->sc_dev, 0, 0, 0, PCIR_BRIDGECTL_1, brctl, 1); DELAY(100000); brctl &= ~PCIB_BCR_SECBUS_RESET; fsl_pcib_write_config(sc->sc_dev, 0, 0, 0, PCIR_BRIDGECTL_1, brctl, 1); DELAY(100000); if (sc->sc_pcie) { ltssm = fsl_pcib_cfgread(sc, 0, 0, 0, PCIR_LTSSM, 1); if (ltssm < LTSSM_STAT_L0) { if (bootverbose) printf("PCI %d: no PCIE link, skipping\n", device_get_unit(dev)); return (0); } } /* Allocate irq */ rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | RF_SHAREABLE); if (sc->sc_irq_res == NULL) { error = fsl_pcib_detach(dev); if (error != 0) { device_printf(dev, "Detach of the driver failed with error %d\n", error); } return (ENXIO); } /* Setup interrupt handler */ error = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, NULL, fsl_pcib_err_intr, dev, &sc->sc_ih); if (error != 0) { device_printf(dev, "Could not setup irq, %d\n", error); sc->sc_ih = NULL; error = fsl_pcib_detach(dev); if (error != 0) { device_printf(dev, "Detach of the driver failed with error %d\n", error); } return (ENXIO); } fsl_pcib_err_init(dev); return (ofw_pcib_attach(dev)); err: return (ENXIO); } static uint32_t fsl_pcib_cfgread(struct fsl_pcib_softc *sc, u_int bus, u_int slot, u_int func, u_int reg, int bytes) { uint32_t addr, data; addr = CONFIG_ACCESS_ENABLE; addr |= (bus & 0xff) << 16; addr |= (slot & 0x1f) << 11; addr |= (func & 0x7) << 8; addr |= reg & 0xfc; if (sc->sc_pcie) addr |= (reg & 0xf00) << 16; mtx_lock_spin(&sc->sc_cfg_mtx); bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_CFG_ADDR, addr); switch (bytes) { case 1: data = bus_space_read_1(sc->sc_bst, sc->sc_bsh, REG_CFG_DATA + (reg & 3)); break; case 2: data = le16toh(bus_space_read_2(sc->sc_bst, sc->sc_bsh, REG_CFG_DATA + (reg & 2))); break; case 4: data = le32toh(bus_space_read_4(sc->sc_bst, sc->sc_bsh, REG_CFG_DATA)); break; default: data = ~0; break; } mtx_unlock_spin(&sc->sc_cfg_mtx); return (data); } static void fsl_pcib_cfgwrite(struct fsl_pcib_softc *sc, u_int bus, u_int slot, u_int func, u_int reg, uint32_t data, int bytes) { uint32_t addr; addr = CONFIG_ACCESS_ENABLE; addr |= (bus & 0xff) << 16; addr |= (slot & 0x1f) << 11; addr |= (func & 0x7) << 8; addr |= reg & 0xfc; if (sc->sc_pcie) addr |= (reg & 0xf00) << 16; mtx_lock_spin(&sc->sc_cfg_mtx); bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_CFG_ADDR, addr); switch (bytes) { case 1: bus_space_write_1(sc->sc_bst, sc->sc_bsh, REG_CFG_DATA + (reg & 3), data); break; case 2: bus_space_write_2(sc->sc_bst, sc->sc_bsh, REG_CFG_DATA + (reg & 2), htole16(data)); break; case 4: bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_CFG_DATA, htole32(data)); break; } mtx_unlock_spin(&sc->sc_cfg_mtx); } #if 0 static void dump(struct fsl_pcib_softc *sc) { unsigned int i; #define RD(o) bus_space_read_4(sc->sc_bst, sc->sc_bsh, o) for (i = 0; i < 5; i++) { printf("POTAR%u =0x%08x\n", i, RD(REG_POTAR(i))); printf("POTEAR%u =0x%08x\n", i, RD(REG_POTEAR(i))); printf("POWBAR%u =0x%08x\n", i, RD(REG_POWBAR(i))); printf("POWAR%u =0x%08x\n", i, RD(REG_POWAR(i))); } printf("\n"); for (i = 1; i < 4; i++) { printf("PITAR%u =0x%08x\n", i, RD(REG_PITAR(i))); printf("PIWBAR%u =0x%08x\n", i, RD(REG_PIWBAR(i))); printf("PIWBEAR%u=0x%08x\n", i, RD(REG_PIWBEAR(i))); printf("PIWAR%u =0x%08x\n", i, RD(REG_PIWAR(i))); } printf("\n"); #undef RD for (i = 0; i < 0x48; i += 4) { printf("cfg%02x=0x%08x\n", i, fsl_pcib_cfgread(sc, 0, 0, 0, i, 4)); } } #endif static int fsl_pcib_maxslots(device_t dev) { struct fsl_pcib_softc *sc = device_get_softc(dev); return ((sc->sc_pcie) ? 0 : PCI_SLOTMAX); } static uint32_t fsl_pcib_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, int bytes) { struct fsl_pcib_softc *sc = device_get_softc(dev); - u_int devfn; if (bus == sc->sc_busnr && !sc->sc_pcie && slot < PCI_SLOT_FIRST) return (~0); - devfn = DEVFN(bus, slot, func); return (fsl_pcib_cfgread(sc, bus, slot, func, reg, bytes)); } static void fsl_pcib_write_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, uint32_t val, int bytes) { struct fsl_pcib_softc *sc = device_get_softc(dev); if (bus == sc->sc_busnr && !sc->sc_pcie && slot < PCI_SLOT_FIRST) return; fsl_pcib_cfgwrite(sc, bus, slot, func, reg, val, bytes); } static void fsl_pcib_inbound(struct fsl_pcib_softc *sc, int wnd, int tgt, uint64_t start, uint64_t size, uint64_t pci_start) { uint32_t attr, bar, tar; KASSERT(wnd > 0, ("%s: inbound window 0 is invalid", __func__)); attr = PIWAR_EN; switch (tgt) { case -1: attr &= ~PIWAR_EN; break; case PIWAR_TRGT_LOCAL: attr |= (ffsl(size) - 2); default: attr |= (tgt << PIWAR_TRGT_S); break; } tar = start >> 12; bar = pci_start >> 12; bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PITAR(wnd), tar); bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PIWBEAR(wnd), 0); bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PIWBAR(wnd), bar); bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PIWAR(wnd), attr); } static void fsl_pcib_outbound(struct fsl_pcib_softc *sc, int wnd, int res, uint64_t start, uint64_t size, uint64_t pci_start) { uint32_t attr, bar, tar; switch (res) { case SYS_RES_MEMORY: attr = 0x80044000 | (ffsll(size) - 2); break; case SYS_RES_IOPORT: attr = 0x80088000 | (ffsll(size) - 2); break; default: attr = 0x0004401f; break; } bar = start >> 12; tar = pci_start >> 12; bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_POTAR(wnd), tar); bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_POTEAR(wnd), 0); bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_POWBAR(wnd), bar); bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_POWAR(wnd), attr); } static void fsl_pcib_err_init(device_t dev) { struct fsl_pcib_softc *sc; uint16_t sec_stat, dsr; uint32_t dcr, err_en; sc = device_get_softc(dev); sec_stat = fsl_pcib_cfgread(sc, 0, 0, 0, PCIR_SECSTAT_1, 2); if (sec_stat) fsl_pcib_cfgwrite(sc, 0, 0, 0, PCIR_SECSTAT_1, 0xffff, 2); if (sc->sc_pcie) { /* Clear error bits */ bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PEX_MES_IER, 0xffffffff); bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PEX_MES_DR, 0xffffffff); bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PEX_ERR_DR, 0xffffffff); dsr = fsl_pcib_cfgread(sc, 0, 0, 0, sc->sc_pcie_capreg + PCIER_DEVICE_STA, 2); if (dsr) fsl_pcib_cfgwrite(sc, 0, 0, 0, sc->sc_pcie_capreg + PCIER_DEVICE_STA, 0xffff, 2); /* Enable all errors reporting */ err_en = 0x00bfff00; bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PEX_ERR_EN, err_en); /* Enable error reporting: URR, FER, NFER */ dcr = fsl_pcib_cfgread(sc, 0, 0, 0, sc->sc_pcie_capreg + PCIER_DEVICE_CTL, 4); dcr |= PCIEM_CTL_URR_ENABLE | PCIEM_CTL_FER_ENABLE | PCIEM_CTL_NFER_ENABLE; fsl_pcib_cfgwrite(sc, 0, 0, 0, sc->sc_pcie_capreg + PCIER_DEVICE_CTL, dcr, 4); } } static int fsl_pcib_detach(device_t dev) { struct fsl_pcib_softc *sc; sc = device_get_softc(dev); mtx_destroy(&sc->sc_cfg_mtx); return (bus_generic_detach(dev)); } static int fsl_pcib_decode_win(phandle_t node, struct fsl_pcib_softc *sc) { device_t dev; int error, i, trgt; dev = sc->sc_dev; fsl_pcib_outbound(sc, 0, -1, 0, 0, 0); /* * Configure LAW decode windows. */ error = law_pci_target(sc->sc_res, &sc->sc_iomem_target, &sc->sc_ioport_target); if (error != 0) { device_printf(dev, "could not retrieve PCI LAW target info\n"); return (error); } for (i = 0; i < sc->pci_sc.sc_nrange; i++) { switch (sc->pci_sc.sc_range[i].pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) { case OFW_PCI_PHYS_HI_SPACE_CONFIG: continue; case OFW_PCI_PHYS_HI_SPACE_IO: trgt = sc->sc_ioport_target; fsl_pcib_outbound(sc, 2, SYS_RES_IOPORT, sc->pci_sc.sc_range[i].host, sc->pci_sc.sc_range[i].size, sc->pci_sc.sc_range[i].pci); sc->sc_ioport_start = sc->pci_sc.sc_range[i].pci; sc->sc_ioport_end = sc->pci_sc.sc_range[i].pci + sc->pci_sc.sc_range[i].size - 1; break; case OFW_PCI_PHYS_HI_SPACE_MEM32: case OFW_PCI_PHYS_HI_SPACE_MEM64: trgt = sc->sc_iomem_target; fsl_pcib_outbound(sc, 1, SYS_RES_MEMORY, sc->pci_sc.sc_range[i].host, sc->pci_sc.sc_range[i].size, sc->pci_sc.sc_range[i].pci); sc->sc_iomem_start = sc->pci_sc.sc_range[i].pci; sc->sc_iomem_end = sc->pci_sc.sc_range[i].pci + sc->pci_sc.sc_range[i].size - 1; break; default: panic("Unknown range type %#x\n", sc->pci_sc.sc_range[i].pci_hi & OFW_PCI_PHYS_HI_SPACEMASK); } error = law_enable(trgt, sc->pci_sc.sc_range[i].host, sc->pci_sc.sc_range[i].size); if (error != 0) { device_printf(dev, "could not program LAW for range " "%d\n", i); return (error); } } /* * Set outbout and inbound windows. */ fsl_pcib_outbound(sc, 3, -1, 0, 0, 0); fsl_pcib_outbound(sc, 4, -1, 0, 0, 0); fsl_pcib_inbound(sc, 1, -1, 0, 0, 0); fsl_pcib_inbound(sc, 2, -1, 0, 0, 0); fsl_pcib_inbound(sc, 3, PIWAR_TRGT_LOCAL, 0, ptoa(Maxmem), 0); /* Direct-map the CCSR for MSIs. */ /* Freescale PCIe 2.x has a dedicated MSI window. */ /* inbound window 8 makes it hit 0xD00 offset, the MSI window. */ if (sc->sc_ip_maj >= 2) fsl_pcib_inbound(sc, 8, PIWAR_TRGT_CCSR, ccsrbar_pa, ccsrbar_size, ccsrbar_pa); else fsl_pcib_inbound(sc, 1, PIWAR_TRGT_CCSR, ccsrbar_pa, ccsrbar_size, ccsrbar_pa); return (0); } static int fsl_pcib_alloc_msi(device_t dev, device_t child, int count, int maxcount, int *irqs) { - struct fsl_pcib_softc *sc; vmem_addr_t start; int err, i; - sc = device_get_softc(dev); if (msi_vmem == NULL) return (ENODEV); err = vmem_xalloc(msi_vmem, count, powerof2(count), 0, 0, VMEM_ADDR_MIN, VMEM_ADDR_MAX, M_BESTFIT | M_WAITOK, &start); if (err) return (err); for (i = 0; i < count; i++) irqs[i] = start + i; return (0); } static int fsl_pcib_release_msi(device_t dev, device_t child, int count, int *irqs) { if (msi_vmem == NULL) return (ENODEV); vmem_xfree(msi_vmem, irqs[0], count); return (0); } static int fsl_pcib_alloc_msix(device_t dev, device_t child, int *irq) { return (fsl_pcib_alloc_msi(dev, child, 1, 1, irq)); } static int fsl_pcib_release_msix(device_t dev, device_t child, int irq) { return (fsl_pcib_release_msi(dev, child, 1, &irq)); } static int fsl_pcib_map_msi(device_t dev, device_t child, int irq, uint64_t *addr, uint32_t *data) { struct fsl_msi_map *mp; SLIST_FOREACH(mp, &fsl_msis, slist) { if (irq >= mp->irq_base && irq < mp->irq_base + FSL_NUM_MSIS) break; } if (mp == NULL) return (ENODEV); *data = (irq & 255); *addr = ccsrbar_pa + mp->target; return (0); } /* * Linux device trees put the msi@ as children of the SoC, with ranges based * on the CCSR. Since rman doesn't permit overlapping or sub-ranges between * devices (bus_space_subregion(9) could do it, but let's not touch the PIC * driver just to allocate a subregion for a sibling driver). This driver will * use ccsr_write() and ccsr_read() instead. */ #define FSL_NUM_IRQS 8 #define FSL_NUM_MSI_PER_IRQ 32 #define FSL_MSI_TARGET 0x140 struct fsl_msi_softc { vm_offset_t sc_base; vm_offset_t sc_target; int sc_msi_base_irq; struct fsl_msi_map sc_map; struct fsl_msi_irq { /* This struct gets passed as the filter private data. */ struct fsl_msi_softc *sc_ptr; /* Pointer back to softc. */ struct resource *res; int irq; void *cookie; int vectors[FSL_NUM_MSI_PER_IRQ]; vm_offset_t reg; } sc_msi_irq[FSL_NUM_IRQS]; }; static int fsl_msi_intr_filter(void *priv) { struct fsl_msi_irq *data = priv; uint32_t reg; int i; reg = ccsr_read4(ccsrbar_va + data->reg); i = 0; while (reg != 0) { if (reg & 1) powerpc_dispatch_intr(data->vectors[i], NULL); reg >>= 1; i++; } return (FILTER_HANDLED); } static int fsl_msi_probe(device_t dev) { if (!ofw_bus_is_compatible(dev, "fsl,mpic-msi")) return (ENXIO); device_set_desc(dev, "Freescale MSI"); return (BUS_PROBE_DEFAULT); } static int fsl_msi_attach(device_t dev) { struct fsl_msi_softc *sc; struct fsl_msi_irq *irq; int i; sc = device_get_softc(dev); if (msi_vmem == NULL) msi_vmem = vmem_create("MPIC MSI", 0, 0, 1, 0, M_BESTFIT | M_WAITOK); /* Manually play with resource entries. */ sc->sc_base = bus_get_resource_start(dev, SYS_RES_MEMORY, 0); sc->sc_map.target = bus_get_resource_start(dev, SYS_RES_MEMORY, 1); if (sc->sc_map.target == 0) sc->sc_map.target = sc->sc_base + FSL_MSI_TARGET; for (i = 0; i < FSL_NUM_IRQS; i++) { irq = &sc->sc_msi_irq[i]; irq->irq = i; irq->reg = sc->sc_base + 16 * i; irq->res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &irq->irq, RF_ACTIVE); bus_setup_intr(dev, irq->res, INTR_TYPE_MISC | INTR_MPSAFE, fsl_msi_intr_filter, NULL, irq, &irq->cookie); } sc->sc_map.irq_base = powerpc_register_pic(dev, ofw_bus_get_node(dev), FSL_NUM_MSIS, 0, 0); /* Let vmem and the IRQ subsystem work their magic for allocations. */ vmem_add(msi_vmem, sc->sc_map.irq_base, FSL_NUM_MSIS, M_WAITOK); SLIST_INSERT_HEAD(&fsl_msis, &sc->sc_map, slist); return (0); } static void fsl_msi_enable(device_t dev, u_int irq, u_int vector, void **priv) { struct fsl_msi_softc *sc; struct fsl_msi_irq *irqd; sc = device_get_softc(dev); irqd = &sc->sc_msi_irq[irq / FSL_NUM_MSI_PER_IRQ]; irqd->vectors[irq % FSL_NUM_MSI_PER_IRQ] = vector; } static device_method_t fsl_msi_methods[] = { DEVMETHOD(device_probe, fsl_msi_probe), DEVMETHOD(device_attach, fsl_msi_attach), DEVMETHOD(pic_enable, fsl_msi_enable), DEVMETHOD_END }; static devclass_t fsl_msi_devclass; static driver_t fsl_msi_driver = { "fsl_msi", fsl_msi_methods, sizeof(struct fsl_msi_softc) }; EARLY_DRIVER_MODULE(fsl_msi, simplebus, fsl_msi_driver, fsl_msi_devclass, 0, 0, BUS_PASS_INTERRUPT + 1);