Home | History | Annotate | Line # | Download | only in pci
msipic.c revision 1.4.2.2
      1 /*	$NetBSD: msipic.c,v 1.4.2.2 2015/06/06 14:40:04 skrll Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2015 Internet Initiative Japan Inc.
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     26  * POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #include <sys/cdefs.h>
     30 __KERNEL_RCSID(0, "$NetBSD: msipic.c,v 1.4.2.2 2015/06/06 14:40:04 skrll Exp $");
     31 
     32 #include <sys/types.h>
     33 #include <sys/param.h>
     34 #include <sys/systm.h>
     35 #include <sys/errno.h>
     36 #include <sys/kmem.h>
     37 #include <sys/malloc.h>
     38 #include <sys/mutex.h>
     39 
     40 #include <dev/pci/pcivar.h>
     41 
     42 #include <machine/i82489reg.h>
     43 #include <machine/i82093reg.h>
     44 #include <machine/i82093var.h>
     45 #include <machine/pic.h>
     46 #include <machine/lock.h>
     47 
     48 #include <x86/pci/msipic.h>
     49 
     50 #ifdef INTRDEBUG
     51 #define MSIPICDEBUG
     52 #endif
     53 
     54 #ifdef MSIPICDEBUG
     55 #define DPRINTF(msg) printf msg
     56 #else
     57 #define DPRINTF(msg)
     58 #endif
     59 
     60 #define BUS_SPACE_WRITE_FLUSH(pc, tag) (void)bus_space_read_4(pc, tag, 0)
     61 
     62 #define MSIPICNAMEBUF 16
     63 
     64 /*
     65  * A Pseudo pic for single MSI/MSI-X device.
     66  * The pic and MSI/MSI-X device are distinbuished by "devid". The "devid"
     67  * is managed by below "dev_seqs".
     68  */
     69 struct msipic {
     70 	int mp_bus;
     71 	int mp_dev;
     72 	int mp_fun;
     73 
     74 	int mp_devid; /* The device id for the MSI/MSI-X device. */
     75 	int mp_veccnt; /* The number of MSI/MSI-X vectors. */
     76 
     77 	char mp_pic_name[MSIPICNAMEBUF]; /* The MSI/MSI-X device's name. */
     78 
     79 	struct pci_attach_args mp_pa;
     80 	bus_space_tag_t mp_bstag;
     81 	bus_space_handle_t mp_bshandle;
     82 	bus_size_t mp_bssize;
     83 	struct pic *mp_pic;
     84 
     85 	LIST_ENTRY(msipic) mp_list;
     86 };
     87 
     88 static kmutex_t msipic_list_lock;
     89 
     90 static LIST_HEAD(, msipic) msipic_list =
     91 	LIST_HEAD_INITIALIZER(msipic_list);
     92 
     93 /*
     94  * This struct managements "devid" to use the same "devid" for the device
     95  * re-attached. If the device's bus number and device numer and function
     96  * number are equal, it is assumed re-attached.
     97  */
     98 struct dev_last_used_seq {
     99 	bool ds_using;
    100 	int ds_bus;
    101 	int ds_dev;
    102 	int ds_fun;
    103 };
    104 /* The number of MSI/MSI-X devices supported by system. */
    105 #define NUM_MSI_DEVS 256
    106 /* Record devids to use the same devid when the device is re-attached. */
    107 static struct dev_last_used_seq dev_seqs[NUM_MSI_DEVS];
    108 
    109 static int msipic_allocate_common_msi_devid(const struct pci_attach_args *);
    110 static void msipic_release_common_msi_devid(int);
    111 
    112 static struct pic *msipic_find_msi_pic_locked(int);
    113 static struct pic *msipic_construct_common_msi_pic(const struct pci_attach_args *,
    114 						   struct pic *);
    115 static void msipic_destruct_common_msi_pic(struct pic *);
    116 
    117 static void msi_set_msictl_enablebit(struct pic *, int, int);
    118 static void msi_hwmask(struct pic *, int);
    119 static void msi_hwunmask(struct pic *, int);
    120 static void msi_addroute(struct pic *, struct cpu_info *, int, int, int);
    121 static void msi_delroute(struct pic *, struct cpu_info *, int, int, int);
    122 
    123 static void msix_set_vecctl_mask(struct pic *, int, int);
    124 static void msix_hwmask(struct pic *, int);
    125 static void msix_hwunmask(struct pic *, int);
    126 static void msix_addroute(struct pic *, struct cpu_info *, int, int, int);
    127 static void msix_delroute(struct pic *, struct cpu_info *, int, int, int);
    128 
    129 /*
    130  * Return new "devid" for the device attached first.
    131  * Return the same "devid" for the device re-attached after dettached once.
    132  * Return -1 if the number of attached MSI/MSI-X devices is over NUM_MSI_DEVS.
    133  */
    134 static int
    135 msipic_allocate_common_msi_devid(const struct pci_attach_args *pa)
    136 {
    137 	pci_chipset_tag_t pc;
    138 	pcitag_t tag;
    139 	int bus, dev, fun, i;
    140 
    141 	KASSERT(mutex_owned(&msipic_list_lock));
    142 
    143 	pc = pa->pa_pc;
    144 	tag = pa->pa_tag;
    145 	pci_decompose_tag(pc, tag, &bus, &dev, &fun);
    146 
    147 	/* if the device was once attached, use same devid */
    148 	for (i = 0; i < NUM_MSI_DEVS; i++) {
    149 		/* skip host bridge */
    150 		if (dev_seqs[i].ds_bus == 0
    151 		    && dev_seqs[i].ds_dev == 0
    152 		    && dev_seqs[i].ds_fun == 0)
    153 			break;
    154 
    155 		if (dev_seqs[i].ds_bus == bus
    156 		    && dev_seqs[i].ds_dev == dev
    157 		    && dev_seqs[i].ds_fun == fun) {
    158 			dev_seqs[i].ds_using = true;
    159 			return i;
    160 		}
    161 	}
    162 
    163 	for (i = 0; i < NUM_MSI_DEVS; i++) {
    164 		if (dev_seqs[i].ds_using == 0) {
    165 			dev_seqs[i].ds_using = true;
    166 			dev_seqs[i].ds_bus = bus;
    167 			dev_seqs[i].ds_dev = dev;
    168 			dev_seqs[i].ds_fun = fun;
    169 			return i;
    170 		}
    171 	}
    172 
    173 	DPRINTF(("too many MSI devices.\n"));
    174 	return -1;
    175 }
    176 
    177 /*
    178  * Set the "devid" unused, but keep reserving the "devid" to reuse when
    179  * the device is re-attached.
    180  */
    181 static void
    182 msipic_release_common_msi_devid(int devid)
    183 {
    184 
    185 	KASSERT(mutex_owned(&msipic_list_lock));
    186 
    187 	if (devid < 0 || NUM_MSI_DEVS <= devid) {
    188 		DPRINTF(("%s: invalid devid.\n", __func__));
    189 		return;
    190 	}
    191 
    192 	dev_seqs[devid].ds_using = false;
    193 	/* Keep ds_* to reuse the same devid for the same device. */
    194 }
    195 
    196 static struct pic *
    197 msipic_find_msi_pic_locked(int devid)
    198 {
    199 	struct msipic *mpp;
    200 
    201 	KASSERT(mutex_owned(&msipic_list_lock));
    202 
    203 	LIST_FOREACH(mpp, &msipic_list, mp_list) {
    204 		if(mpp->mp_devid == devid)
    205 			return mpp->mp_pic;
    206 	}
    207 	return NULL;
    208 }
    209 
    210 /*
    211  * Return the msi_pic whose device is already registered.
    212  * If the device is not registered yet, return NULL.
    213  */
    214 struct pic *
    215 msipic_find_msi_pic(int devid)
    216 {
    217 	struct pic *msipic;
    218 
    219 	mutex_enter(&msipic_list_lock);
    220 	msipic = msipic_find_msi_pic_locked(devid);
    221 	mutex_exit(&msipic_list_lock);
    222 
    223 	return msipic;
    224 }
    225 
    226 /*
    227  * A common construct process of MSI and MSI-X.
    228  */
    229 static struct pic *
    230 msipic_construct_common_msi_pic(const struct pci_attach_args *pa,
    231     struct pic *pic_tmpl)
    232 {
    233 	struct pic *pic;
    234 	struct msipic *msipic;
    235 	int devid;
    236 
    237 	pic = kmem_alloc(sizeof(*pic), KM_SLEEP);
    238 	if (pic == NULL)
    239 		return NULL;
    240 
    241 	msipic = kmem_zalloc(sizeof(*msipic), KM_SLEEP);
    242 	if (msipic == NULL) {
    243 		kmem_free(pic, sizeof(*pic));
    244 		return NULL;
    245 	}
    246 
    247 	mutex_enter(&msipic_list_lock);
    248 
    249 	devid = msipic_allocate_common_msi_devid(pa);
    250 	if (devid == -1) {
    251 		mutex_exit(&msipic_list_lock);
    252 		kmem_free(pic, sizeof(*pic));
    253 		kmem_free(msipic, sizeof(*msipic));
    254 		return NULL;
    255 	}
    256 
    257 	memcpy(pic, pic_tmpl, sizeof(*pic));
    258 	pic->pic_msipic = msipic;
    259 	msipic->mp_pic = pic;
    260 	pci_decompose_tag(pa->pa_pc, pa->pa_tag,
    261 	    &msipic->mp_bus, &msipic->mp_dev, &msipic->mp_fun);
    262 	memcpy(&msipic->mp_pa, pa, sizeof(msipic->mp_pa));
    263 	msipic->mp_devid = devid;
    264 	/*
    265 	 * pci_msi{,x}_alloc() must be called only once in the device driver.
    266 	 */
    267 	KASSERT(msipic_find_msi_pic_locked(msipic->mp_devid) == NULL);
    268 
    269 	LIST_INSERT_HEAD(&msipic_list, msipic, mp_list);
    270 
    271 	mutex_exit(&msipic_list_lock);
    272 
    273 	return pic;
    274 }
    275 
    276 static void
    277 msipic_destruct_common_msi_pic(struct pic *msi_pic)
    278 {
    279 	struct msipic *msipic;
    280 
    281 	if (msi_pic == NULL)
    282 		return;
    283 
    284 	msipic = msi_pic->pic_msipic;
    285 	mutex_enter(&msipic_list_lock);
    286 	LIST_REMOVE(msipic, mp_list);
    287 	msipic_release_common_msi_devid(msipic->mp_devid);
    288 	mutex_exit(&msipic_list_lock);
    289 
    290 	kmem_free(msipic, sizeof(*msipic));
    291 	kmem_free(msi_pic, sizeof(*msi_pic));
    292 }
    293 
    294 /*
    295  * The pic is MSI/MSI-X pic or not.
    296  */
    297 bool
    298 msipic_is_msi_pic(struct pic *pic)
    299 {
    300 
    301 	return (pic->pic_msipic != NULL);
    302 }
    303 
    304 /*
    305  * Return the MSI/MSI-X devid which is unique for each devices.
    306  */
    307 int
    308 msipic_get_devid(struct pic *pic)
    309 {
    310 
    311 	KASSERT(msipic_is_msi_pic(pic));
    312 
    313 	return pic->pic_msipic->mp_devid;
    314 }
    315 
    316 #define MSI_MSICTL_ENABLE 1
    317 #define MSI_MSICTL_DISABLE 0
    318 static void
    319 msi_set_msictl_enablebit(struct pic *pic, int msi_vec, int flag)
    320 {
    321 	pci_chipset_tag_t pc;
    322 	struct pci_attach_args *pa;
    323 	pcitag_t tag;
    324 	pcireg_t ctl;
    325 	int off, err __diagused;
    326 
    327 	pc = NULL;
    328 	pa = &pic->pic_msipic->mp_pa;
    329 	tag = pa->pa_tag;
    330 	err = pci_get_capability(pc, tag, PCI_CAP_MSI, &off, NULL);
    331 	KASSERT(err != 0);
    332 
    333 	/*
    334 	 * MSI can establish only one vector at once.
    335 	 * So, use whole device mask bit instead of a vector mask bit.
    336 	 */
    337 	ctl = pci_conf_read(pc, tag, off + PCI_MSI_CTL);
    338 	if (flag == MSI_MSICTL_ENABLE)
    339 		ctl |= PCI_MSI_CTL_MSI_ENABLE;
    340 	else
    341 		ctl &= ~PCI_MSI_CTL_MSI_ENABLE;
    342 
    343 	pci_conf_write(pc, tag, off, ctl);
    344 }
    345 
    346 static void
    347 msi_hwmask(struct pic *pic, int msi_vec)
    348 {
    349 
    350 	msi_set_msictl_enablebit(pic, msi_vec, MSI_MSICTL_DISABLE);
    351 }
    352 
    353 /*
    354  * Do not use pic->hwunmask() immediately after pic->delroute().
    355  * It is required to use pic->addroute() before pic->hwunmask().
    356  */
    357 static void
    358 msi_hwunmask(struct pic *pic, int msi_vec)
    359 {
    360 
    361 	msi_set_msictl_enablebit(pic, msi_vec, MSI_MSICTL_ENABLE);
    362 }
    363 
    364 static void
    365 msi_addroute(struct pic *pic, struct cpu_info *ci,
    366 	     int unused, int idt_vec, int type)
    367 {
    368 	pci_chipset_tag_t pc;
    369 	struct pci_attach_args *pa;
    370 	pcitag_t tag;
    371 	pcireg_t addr, data, ctl;
    372 	int off, err __diagused;
    373 
    374 	pc = NULL;
    375 	pa = &pic->pic_msipic->mp_pa;
    376 	tag = pa->pa_tag;
    377 	err = pci_get_capability(pc, tag, PCI_CAP_MSI, &off, NULL);
    378 	KASSERT(err != 0);
    379 
    380 	/*
    381 	 * See Intel 64 and IA-32 Architectures Software Developer's Manual
    382 	 * Volume 3 10.11 Message Signalled Interrupts.
    383 	 */
    384 	/*
    385 	 * "cpuid" for MSI address is local APIC ID. In NetBSD, the ID is
    386 	 * the same as ci->ci_cpuid.
    387 	 */
    388 	addr = LAPIC_MSIADDR_BASE | __SHIFTIN(ci->ci_cpuid,
    389 	    LAPIC_MSIADDR_DSTID_MASK);
    390 	/* If trigger mode is edge, it don't care level for trigger mode. */
    391 	data = __SHIFTIN(idt_vec, LAPIC_MSIDATA_VECTOR_MASK)
    392 		| LAPIC_MSIDATA_TRGMODE_EDGE | LAPIC_MSIDATA_DM_FIXED;
    393 
    394 	ctl = pci_conf_read(pc, tag, off + PCI_MSI_CTL);
    395 	if (ctl & PCI_MSI_CTL_64BIT_ADDR) {
    396 		pci_conf_write(pc, tag, off + PCI_MSI_MADDR64_LO, addr);
    397 		pci_conf_write(pc, tag, off + PCI_MSI_MADDR64_HI, 0);
    398 		pci_conf_write(pc, tag, off + PCI_MSI_MDATA64, data);
    399 	} else {
    400 		pci_conf_write(pc, tag, off + PCI_MSI_MADDR, addr);
    401 		pci_conf_write(pc, tag, off + PCI_MSI_MDATA, data);
    402 	}
    403 	ctl |= PCI_MSI_CTL_MSI_ENABLE;
    404 	pci_conf_write(pc, tag, off + PCI_MSI_CTL, ctl);
    405 }
    406 
    407 /*
    408  * Do not use pic->hwunmask() immediately after pic->delroute().
    409  * It is required to use pic->addroute() before pic->hwunmask().
    410  */
    411 static void
    412 msi_delroute(struct pic *pic, struct cpu_info *ci,
    413 	     int msi_vec, int idt_vec, int type)
    414 {
    415 
    416 	msi_hwmask(pic, msi_vec);
    417 }
    418 
    419 /*
    420  * Template for MSI pic.
    421  * .pic_msipic is set later in construct_msi_pic().
    422  */
    423 static struct pic msi_pic_tmpl = {
    424 	.pic_type = PIC_MSI,
    425 	.pic_vecbase = 0,
    426 	.pic_apicid = 0,
    427 	.pic_lock = __SIMPLELOCK_UNLOCKED, /* not used for msi_pic */
    428 	.pic_hwmask = msi_hwmask,
    429 	.pic_hwunmask = msi_hwunmask,
    430 	.pic_addroute = msi_addroute,
    431 	.pic_delroute = msi_delroute,
    432 	.pic_edge_stubs = ioapic_edge_stubs,
    433 	.pic_ioapic = NULL,
    434 };
    435 
    436 /*
    437  * Create pseudo pic for a MSI device.
    438  */
    439 struct pic *
    440 msipic_construct_msi_pic(const struct pci_attach_args *pa)
    441 {
    442 	struct pic *msi_pic;
    443 	char pic_name_buf[MSIPICNAMEBUF];
    444 
    445 	msi_pic = msipic_construct_common_msi_pic(pa, &msi_pic_tmpl);
    446 	if (msi_pic == NULL) {
    447 		DPRINTF(("cannot allocate MSI pic.\n"));
    448 		return NULL;
    449 	}
    450 
    451 	memset(pic_name_buf, 0, MSIPICNAMEBUF);
    452 	snprintf(pic_name_buf, MSIPICNAMEBUF, "msi%d",
    453 	    msi_pic->pic_msipic->mp_devid);
    454 	strncpy(msi_pic->pic_msipic->mp_pic_name, pic_name_buf,
    455 	    MSIPICNAMEBUF - 1);
    456 	msi_pic->pic_name = msi_pic->pic_msipic->mp_pic_name;
    457 
    458 	return msi_pic;
    459 }
    460 
    461 /*
    462  * Delete pseudo pic for a MSI device.
    463  */
    464 void
    465 msipic_destruct_msi_pic(struct pic *msi_pic)
    466 {
    467 
    468 	msipic_destruct_common_msi_pic(msi_pic);
    469 }
    470 
    471 #define MSIX_VECCTL_HWMASK 1
    472 #define MSIX_VECCTL_HWUNMASK 0
    473 static void
    474 msix_set_vecctl_mask(struct pic *pic, int msix_vec, int flag)
    475 {
    476 	bus_space_tag_t bstag;
    477 	bus_space_handle_t bshandle;
    478 	uint64_t entry_base;
    479 	uint32_t vecctl;
    480 
    481 	if (msix_vec < 0) {
    482 		DPRINTF(("%s: invalid MSI-X table index, devid=%d vecid=%d",
    483 			__func__, msipic_get_devid(pic), msix_vec));
    484 		return;
    485 	}
    486 
    487 	entry_base = PCI_MSIX_TABLE_ENTRY_SIZE * msix_vec;
    488 
    489 	bstag = pic->pic_msipic->mp_bstag;
    490 	bshandle = pic->pic_msipic->mp_bshandle;
    491 	vecctl = bus_space_read_4(bstag, bshandle,
    492 	    entry_base + PCI_MSIX_TABLE_ENTRY_VECTCTL);
    493 	if (flag == MSIX_VECCTL_HWMASK)
    494 		vecctl |= PCI_MSIX_VECTCTL_HWMASK_MASK;
    495 	else
    496 		vecctl &= ~PCI_MSIX_VECTCTL_HWMASK_MASK;
    497 
    498 	bus_space_write_4(bstag, bshandle,
    499 	    entry_base + PCI_MSIX_TABLE_ENTRY_VECTCTL, vecctl);
    500 	BUS_SPACE_WRITE_FLUSH(bstag, bshandle);
    501 }
    502 
    503 static void
    504 msix_hwmask(struct pic *pic, int msix_vec)
    505 {
    506 
    507 	msix_set_vecctl_mask(pic, msix_vec, MSIX_VECCTL_HWMASK);
    508 }
    509 
    510 /*
    511  * Do not use pic->hwunmask() immediately after pic->delroute().
    512  * It is required to use pic->addroute() before pic->hwunmask().
    513  */
    514 static void
    515 msix_hwunmask(struct pic *pic, int msix_vec)
    516 {
    517 
    518 	msix_set_vecctl_mask(pic, msix_vec, MSIX_VECCTL_HWUNMASK);
    519 }
    520 
    521 static void
    522 msix_addroute(struct pic *pic, struct cpu_info *ci,
    523 	     int msix_vec, int idt_vec, int type)
    524 {
    525 	pci_chipset_tag_t pc;
    526 	struct pci_attach_args *pa;
    527 	pcitag_t tag;
    528 	bus_space_tag_t bstag;
    529 	bus_space_handle_t bshandle;
    530 	uint64_t entry_base;
    531 	pcireg_t addr, data, ctl;
    532 	int off, err __diagused;
    533 
    534 	if (msix_vec < 0) {
    535 		DPRINTF(("%s: invalid MSI-X table index, devid=%d vecid=%d",
    536 			__func__, msipic_get_devid(pic), msix_vec));
    537 		return;
    538 	}
    539 
    540 	pa = &pic->pic_msipic->mp_pa;
    541 	pc = pa->pa_pc;
    542 	tag = pa->pa_tag;
    543 	err = pci_get_capability(pc, tag, PCI_CAP_MSIX, &off, NULL);
    544 	KASSERT(err != 0);
    545 
    546 	entry_base = PCI_MSIX_TABLE_ENTRY_SIZE * msix_vec;
    547 
    548 	/*
    549 	 * See Intel 64 and IA-32 Architectures Software Developer's Manual
    550 	 * Volume 3 10.11 Message Signalled Interrupts.
    551 	 */
    552 	/*
    553 	 * "cpuid" for MSI-X address is local APIC ID. In NetBSD, the ID is
    554 	 * the same as ci->ci_cpuid.
    555 	 */
    556 	addr = LAPIC_MSIADDR_BASE | __SHIFTIN(ci->ci_cpuid,
    557 	    LAPIC_MSIADDR_DSTID_MASK);
    558 	/* If trigger mode is edge, it don't care level for trigger mode. */
    559 	data = __SHIFTIN(idt_vec, LAPIC_MSIDATA_VECTOR_MASK)
    560 		| LAPIC_MSIDATA_TRGMODE_EDGE | LAPIC_MSIDATA_DM_FIXED;
    561 
    562 	bstag = pic->pic_msipic->mp_bstag;
    563 	bshandle = pic->pic_msipic->mp_bshandle;
    564 	bus_space_write_4(bstag, bshandle,
    565 	    entry_base + PCI_MSIX_TABLE_ENTRY_ADDR_LO, addr);
    566 	bus_space_write_4(bstag, bshandle,
    567 	    entry_base + PCI_MSIX_TABLE_ENTRY_ADDR_HI, 0);
    568 	bus_space_write_4(bstag, bshandle,
    569 	    entry_base + PCI_MSIX_TABLE_ENTRY_DATA, data);
    570 	bus_space_write_4(bstag, bshandle,
    571 	    entry_base + PCI_MSIX_TABLE_ENTRY_VECTCTL, 0);
    572 	BUS_SPACE_WRITE_FLUSH(bstag, bshandle);
    573 
    574 	ctl = pci_conf_read(pc, tag, off + PCI_MSIX_CTL);
    575 	ctl |= PCI_MSIX_CTL_ENABLE;
    576 	pci_conf_write(pc, tag, off + PCI_MSIX_CTL, ctl);
    577 }
    578 
    579 /*
    580  * Do not use pic->hwunmask() immediately after pic->delroute().
    581  * It is required to use pic->addroute() before pic->hwunmask().
    582  */
    583 static void
    584 msix_delroute(struct pic *pic, struct cpu_info *ci,
    585 	     int msix_vec, int vec, int type)
    586 {
    587 
    588 	msix_hwmask(pic, msix_vec);
    589 }
    590 
    591 /*
    592  * Template for MSI-X pic.
    593  * .pic_msipic is set later in construct_msix_pic().
    594  */
    595 static struct pic msix_pic_tmpl = {
    596 	.pic_type = PIC_MSIX,
    597 	.pic_vecbase = 0,
    598 	.pic_apicid = 0,
    599 	.pic_lock = __SIMPLELOCK_UNLOCKED, /* not used for msix_pic */
    600 	.pic_hwmask = msix_hwmask,
    601 	.pic_hwunmask = msix_hwunmask,
    602 	.pic_addroute = msix_addroute,
    603 	.pic_delroute = msix_delroute,
    604 	.pic_edge_stubs = ioapic_edge_stubs,
    605 };
    606 
    607 struct pic *
    608 msipic_construct_msix_pic(const struct pci_attach_args *pa)
    609 {
    610 	struct pic *msix_pic;
    611 	pci_chipset_tag_t pc;
    612 	pcitag_t tag;
    613 	pcireg_t tbl;
    614 	bus_space_tag_t bstag;
    615 	bus_space_handle_t bshandle;
    616 	bus_size_t bssize;
    617 	size_t table_size;
    618 	uint32_t table_offset;
    619 	u_int memtype;
    620 	int bir, bar, err, off, table_nentry;
    621 	char pic_name_buf[MSIPICNAMEBUF];
    622 
    623 	table_nentry = pci_msix_count(pa);
    624 	if (table_nentry == 0) {
    625 		DPRINTF(("MSI-X table entry is 0.\n"));
    626 		return NULL;
    627 	}
    628 
    629 	pc = pa->pa_pc;
    630 	tag = pa->pa_tag;
    631 	if (pci_get_capability(pc, tag, PCI_CAP_MSIX, &off, NULL) == 0) {
    632 		DPRINTF(("%s: no msix capability", __func__));
    633 		return NULL;
    634 	}
    635 
    636 	msix_pic = msipic_construct_common_msi_pic(pa, &msix_pic_tmpl);
    637 	if (msix_pic == NULL) {
    638 		DPRINTF(("cannot allocate MSI-X pic.\n"));
    639 		return NULL;
    640 	}
    641 
    642 	memset(pic_name_buf, 0, MSIPICNAMEBUF);
    643 	snprintf(pic_name_buf, MSIPICNAMEBUF, "msix%d",
    644 	    msix_pic->pic_msipic->mp_devid);
    645 	strncpy(msix_pic->pic_msipic->mp_pic_name, pic_name_buf,
    646 	    MSIPICNAMEBUF - 1);
    647 	msix_pic->pic_name = msix_pic->pic_msipic->mp_pic_name;
    648 
    649 	tbl = pci_conf_read(pc, tag, off + PCI_MSIX_TBLOFFSET);
    650 	table_offset = tbl & PCI_MSIX_TBLOFFSET_MASK;
    651 	bir = tbl & PCI_MSIX_PBABIR_MASK;
    652 	switch(bir) {
    653 	case 0:
    654 		bar = PCI_BAR0;
    655 		break;
    656 	case 1:
    657 		bar = PCI_BAR1;
    658 		break;
    659 	case 2:
    660 		bar = PCI_BAR2;
    661 		break;
    662 	case 3:
    663 		bar = PCI_BAR3;
    664 		break;
    665 	case 4:
    666 		bar = PCI_BAR4;
    667 		break;
    668 	case 5:
    669 		bar = PCI_BAR5;
    670 		break;
    671 	default:
    672 		aprint_error("detect an illegal device! The device use reserved BIR values.\n");
    673 		msipic_destruct_common_msi_pic(msix_pic);
    674 		return NULL;
    675 	}
    676 	memtype = pci_mapreg_type(pc, tag, bar);
    677 	 /*
    678 	  * PCI_MSIX_TABLE_ENTRY_SIZE consists below
    679 	  *     - Vector Control (32bit)
    680 	  *     - Message Data (32bit)
    681 	  *     - Message Upper Address (32bit)
    682 	  *     - Message Lower Address (32bit)
    683 	  */
    684 	table_size = table_nentry * PCI_MSIX_TABLE_ENTRY_SIZE;
    685 	err = pci_mapreg_submap(pa, bar, memtype, BUS_SPACE_MAP_LINEAR,
    686 	    roundup(table_size, PAGE_SIZE), table_offset,
    687 	    &bstag, &bshandle, NULL, &bssize);
    688 	if (err) {
    689 		DPRINTF(("cannot map msix table.\n"));
    690 		msipic_destruct_common_msi_pic(msix_pic);
    691 		return NULL;
    692 	}
    693 	msix_pic->pic_msipic->mp_bstag = bstag;
    694 	msix_pic->pic_msipic->mp_bshandle = bshandle;
    695 	msix_pic->pic_msipic->mp_bssize = bssize;
    696 
    697 	return msix_pic;
    698 }
    699 
    700 /*
    701  * Delete pseudo pic for a MSI-X device.
    702  */
    703 void
    704 msipic_destruct_msix_pic(struct pic *msix_pic)
    705 {
    706 	struct msipic *msipic;
    707 
    708 	KASSERT(msipic_is_msi_pic(msix_pic));
    709 	KASSERT(msix_pic->pic_type == PIC_MSIX);
    710 
    711 	msipic = msix_pic->pic_msipic;
    712 	bus_space_unmap(msipic->mp_bstag, msipic->mp_bshandle,
    713 	    msipic->mp_bssize);
    714 
    715 	msipic_destruct_common_msi_pic(msix_pic);
    716 }
    717 
    718 /*
    719  * Set the number of MSI vectors for pseudo MSI pic.
    720  */
    721 int
    722 msipic_set_msi_vectors(struct pic *msi_pic, pci_intr_handle_t *pihs,
    723     int count)
    724 {
    725 
    726 	KASSERT(msipic_is_msi_pic(msi_pic));
    727 
    728 	msi_pic->pic_msipic->mp_veccnt = count;
    729 	return 0;
    730 }
    731 
    732 /*
    733  * Initialize the system to use MSI/MSI-X.
    734  */
    735 void
    736 msipic_init(void)
    737 {
    738 
    739 	mutex_init(&msipic_list_lock, MUTEX_DEFAULT, IPL_NONE);
    740 }
    741