1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * px_msi.c 28 */ 29 30 #include <sys/types.h> 31 #include <sys/kmem.h> 32 #include <sys/conf.h> 33 #include <sys/ddi.h> 34 #include <sys/sunddi.h> 35 #include <sys/sunndi.h> 36 #include <sys/modctl.h> 37 #include <sys/disp.h> 38 #include <sys/stat.h> 39 #include <sys/ddi_impldefs.h> 40 #include <sys/pci_impl.h> 41 #include "px_obj.h" 42 43 static int px_msi_get_props(px_t *px_p); 44 45 /* 46 * msi_attach() 47 */ 48 int 49 px_msi_attach(px_t *px_p) 50 { 51 dev_info_t *dip = px_p->px_dip; 52 px_msi_state_t *msi_state_p = &px_p->px_ib_p->ib_msi_state; 53 ddi_irm_pool_t *irm_pool_p = NULL; 54 ddi_irm_params_t irm_params; 55 msinum_t msi_num; 56 int i, ret; 57 58 DBG(DBG_MSIQ, dip, "px_msi_attach\n"); 59 60 mutex_init(&msi_state_p->msi_mutex, NULL, MUTEX_DRIVER, NULL); 61 62 /* 63 * Check for all MSI related properties and 64 * save all information. 65 */ 66 if (px_msi_get_props(px_p) != DDI_SUCCESS) { 67 px_msi_detach(px_p); 68 return (DDI_FAILURE); 69 } 70 71 msi_state_p->msi_p = kmem_zalloc(msi_state_p->msi_cnt * 72 sizeof (px_msi_t), KM_SLEEP); 73 74 for (i = 0, msi_num = msi_state_p->msi_1st_msinum; 75 i < msi_state_p->msi_cnt; i++, msi_num++) { 76 msi_state_p->msi_p[i].msi_msinum = msi_num; 77 msi_state_p->msi_p[i].msi_state = MSI_STATE_FREE; 78 } 79 80 /* 81 * Create IRM pool to manage interrupt allocations. 82 */ 83 bzero(&irm_params, sizeof (ddi_irm_params_t)); 84 irm_params.iparams_types = msi_state_p->msi_type; 85 irm_params.iparams_total = msi_state_p->msi_cnt; 86 if (ndi_irm_create(dip, &irm_params, &irm_pool_p) == DDI_SUCCESS) { 87 msi_state_p->msi_pool_p = irm_pool_p; 88 } else { 89 DBG(DBG_MSIQ, dip, "ndi_irm_create() failed\n"); 90 } 91 92 if ((ret = px_lib_msi_init(dip)) != DDI_SUCCESS) 93 px_msi_detach(px_p); 94 95 return (ret); 96 } 97 98 99 /* 100 * msi_detach() 101 */ 102 void 103 px_msi_detach(px_t *px_p) 104 { 105 dev_info_t *dip = px_p->px_dip; 106 px_msi_state_t *msi_state_p = &px_p->px_ib_p->ib_msi_state; 107 108 DBG(DBG_MSIQ, dip, "px_msi_detach\n"); 109 110 if (msi_state_p->msi_pool_p) { 111 (void) ndi_irm_destroy(msi_state_p->msi_pool_p); 112 } 113 114 if (msi_state_p->msi_addr64 && msi_state_p->msi_mem_flg) { 115 ndi_ra_free(dip, msi_state_p->msi_addr64, 116 msi_state_p->msi_addr64_len, 117 NDI_RA_TYPE_MEM, NDI_RA_PASS); 118 } 119 120 if (msi_state_p->msi_addr32 && msi_state_p->msi_mem_flg) { 121 ndi_ra_free(dip, msi_state_p->msi_addr32, 122 msi_state_p->msi_addr32_len, 123 NDI_RA_TYPE_MEM, NDI_RA_PASS); 124 125 pci_resource_destroy(dip); 126 } 127 128 if (msi_state_p->msi_p) { 129 kmem_free(msi_state_p->msi_p, 130 msi_state_p->msi_cnt * sizeof (px_msi_t)); 131 } 132 133 mutex_destroy(&msi_state_p->msi_mutex); 134 bzero(&px_p->px_ib_p->ib_msi_state, sizeof (px_msi_state_t)); 135 } 136 137 138 /* 139 * msi_alloc() 140 */ 141 /* ARGSUSED */ 142 int 143 px_msi_alloc(px_t *px_p, dev_info_t *rdip, int type, int inum, int msi_count, 144 int flag, int *actual_msi_count_p) 145 { 146 px_msi_state_t *msi_state_p = &px_p->px_ib_p->ib_msi_state; 147 int first, count, i, n; 148 149 DBG(DBG_A_MSIX, px_p->px_dip, "px_msi_alloc: rdip %s:%d " 150 "type 0x%x inum 0x%x msi_count 0x%x\n", ddi_driver_name(rdip), 151 ddi_get_instance(rdip), type, inum, msi_count); 152 153 mutex_enter(&msi_state_p->msi_mutex); 154 155 *actual_msi_count_p = 0; 156 157 /* 158 * MSI interrupts are allocated as contiguous ranges at 159 * power of 2 boundaries from the start of the MSI array. 160 */ 161 if (type == DDI_INTR_TYPE_MSI) { 162 163 /* Search for a range of available interrupts */ 164 for (count = msi_count; count; count >>= 1) { 165 for (first = 0; (first + count) < msi_state_p->msi_cnt; 166 first += count) { 167 for (i = first; i < (first + count); i++) { 168 if (msi_state_p->msi_p[i].msi_state 169 != MSI_STATE_FREE) { 170 break; 171 } 172 } 173 if (i == (first + count)) { 174 goto found_msi; 175 } 176 } 177 DBG(DBG_A_MSIX, px_p->px_dip, "px_msi_alloc: failed\n"); 178 if (count > 1) { 179 DBG(DBG_A_MSIX, px_p->px_dip, "px_msi_alloc: " 180 "Retry MSI allocation with new msi_count " 181 "0x%x\n", count >> 1); 182 } 183 } 184 185 found_msi: 186 /* Set number of available interrupts */ 187 *actual_msi_count_p = count; 188 189 /* Check if successful, and enforce strict behavior */ 190 if ((count == 0) || 191 ((flag == DDI_INTR_ALLOC_STRICT) && (count != msi_count))) { 192 mutex_exit(&msi_state_p->msi_mutex); 193 return (DDI_EAGAIN); 194 } 195 196 /* Allocate the interrupts */ 197 for (i = first; i < (first + count); i++, inum++) { 198 msi_state_p->msi_p[i].msi_state = MSI_STATE_INUSE; 199 msi_state_p->msi_p[i].msi_dip = rdip; 200 msi_state_p->msi_p[i].msi_inum = inum; 201 } 202 } 203 204 /* 205 * MSI-X interrupts are allocated from the end of the MSI 206 * array. There are no concerns about power of 2 boundaries 207 * and the allocated interrupts do not have to be contiguous. 208 */ 209 if (type == DDI_INTR_TYPE_MSIX) { 210 211 /* Count available interrupts, up to count requested */ 212 for (count = 0, i = (msi_state_p->msi_cnt - 1); i >= 0; i--) { 213 if (msi_state_p->msi_p[i].msi_state == MSI_STATE_FREE) { 214 if (count == 0) 215 first = i; 216 count++; 217 if (count == msi_count) 218 break; 219 } 220 } 221 222 /* Set number of available interrupts */ 223 *actual_msi_count_p = count; 224 225 /* Check if successful, and enforce strict behavior */ 226 if ((count == 0) || 227 ((flag == DDI_INTR_ALLOC_STRICT) && (count != msi_count))) { 228 mutex_exit(&msi_state_p->msi_mutex); 229 return (DDI_EAGAIN); 230 } 231 232 /* Allocate the interrupts */ 233 for (n = 0, i = first; n < count; i--) { 234 if (msi_state_p->msi_p[i].msi_state != MSI_STATE_FREE) 235 continue; 236 msi_state_p->msi_p[i].msi_state = MSI_STATE_INUSE; 237 msi_state_p->msi_p[i].msi_dip = rdip; 238 msi_state_p->msi_p[i].msi_inum = inum; 239 inum++; 240 n++; 241 } 242 } 243 244 DBG(DBG_A_MSIX, px_p->px_dip, "px_msi_alloc: rdip %s:%d " 245 "msi_num 0x%x count 0x%x\n", ddi_driver_name(rdip), 246 ddi_get_instance(rdip), first, count); 247 248 mutex_exit(&msi_state_p->msi_mutex); 249 250 return (DDI_SUCCESS); 251 } 252 253 254 /* 255 * msi_free() 256 */ 257 int 258 px_msi_free(px_t *px_p, dev_info_t *rdip, int inum, int msi_count) 259 { 260 px_msi_state_t *msi_state_p = &px_p->px_ib_p->ib_msi_state; 261 int i, n; 262 263 DBG(DBG_R_MSIX, px_p->px_dip, "px_msi_free: rdip 0x%p " 264 "inum 0x%x msi_count 0x%x\n", rdip, inum, msi_count); 265 266 mutex_enter(&msi_state_p->msi_mutex); 267 268 /* 269 * Find and release the specified MSI/X numbers. 270 * 271 * Because the allocations are not always contiguous, perform 272 * a full linear search of the MSI/X table looking for MSI/X 273 * vectors owned by the device with inum values in the range 274 * [inum .. (inum + msi_count - 1)]. 275 */ 276 for (i = 0, n = 0; (i < msi_state_p->msi_cnt) && (n < msi_count); i++) { 277 if ((msi_state_p->msi_p[i].msi_dip == rdip) && 278 (msi_state_p->msi_p[i].msi_inum >= inum) && 279 (msi_state_p->msi_p[i].msi_inum < (inum + msi_count))) { 280 msi_state_p->msi_p[i].msi_dip = NULL; 281 msi_state_p->msi_p[i].msi_inum = 0; 282 msi_state_p->msi_p[i].msi_msiq_id = 0; 283 msi_state_p->msi_p[i].msi_state = MSI_STATE_FREE; 284 n++; 285 } 286 } 287 288 mutex_exit(&msi_state_p->msi_mutex); 289 290 /* Fail if the MSI/X numbers were not found */ 291 if (n < msi_count) 292 return (DDI_FAILURE); 293 294 return (DDI_SUCCESS); 295 } 296 297 /* 298 * msi_get_msinum() 299 */ 300 int 301 px_msi_get_msinum(px_t *px_p, dev_info_t *rdip, int inum, msinum_t *msi_num_p) 302 { 303 px_msi_state_t *msi_state_p = &px_p->px_ib_p->ib_msi_state; 304 int i; 305 306 DBG(DBG_A_MSIX, px_p->px_dip, "px_msi_get_msinum: " 307 "rdip 0x%p inum 0x%x\n", rdip, inum); 308 309 mutex_enter(&msi_state_p->msi_mutex); 310 311 for (i = 0; i < msi_state_p->msi_cnt; i++) { 312 if ((msi_state_p->msi_p[i].msi_inum == inum) && 313 (msi_state_p->msi_p[i].msi_dip == rdip)) { 314 315 *msi_num_p = msi_state_p->msi_p[i].msi_msinum; 316 317 DBG(DBG_A_MSIX, px_p->px_dip, "px_msi_get_msinum: " 318 "inum 0x%x msi 0x%x\n", inum, *msi_num_p); 319 320 mutex_exit(&msi_state_p->msi_mutex); 321 return (DDI_SUCCESS); 322 } 323 } 324 325 if (i >= msi_state_p->msi_cnt) 326 DBG(DBG_A_MSIX, px_p->px_dip, "px_msi_get_msinum: " 327 "no msi for inum 0x%x\n", inum); 328 329 mutex_exit(&msi_state_p->msi_mutex); 330 return (DDI_FAILURE); 331 } 332 333 /* 334 * px_msi_get_props() 335 */ 336 static int 337 px_msi_get_props(px_t *px_p) 338 { 339 dev_info_t *dip = px_p->px_dip; 340 px_msi_state_t *msi_state_p = &px_p->px_ib_p->ib_msi_state; 341 int ret = DDI_SUCCESS; 342 int length = sizeof (int); 343 int *valuep = NULL; 344 uint64_t msi_addr_hi, msi_addr_lo; 345 uint64_t mem_answer, mem_alen; 346 ndi_ra_request_t request; 347 348 DBG(DBG_MSIQ, dip, "px_msi_get_props\n"); 349 350 /* #msi */ 351 msi_state_p->msi_cnt = ddi_getprop(DDI_DEV_T_ANY, dip, 352 DDI_PROP_DONTPASS, "#msi", PX_DEFAULT_MSI_CNT); 353 354 DBG(DBG_MSIQ, dip, "obp: #msi=%d\n", 355 msi_state_p->msi_cnt); 356 357 /* msi-ranges: msi# field */ 358 ret = ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_ALLOC, 359 DDI_PROP_DONTPASS, "msi-ranges", (caddr_t)&valuep, &length); 360 361 if (ret == DDI_PROP_SUCCESS) { 362 msi_state_p->msi_1st_msinum = 363 ((px_msi_ranges_t *)valuep)->msi_no; 364 kmem_free(valuep, (size_t)length); 365 } else 366 msi_state_p->msi_1st_msinum = PX_DEFAULT_MSI_1ST_MSINUM; 367 368 DBG(DBG_MSIQ, dip, "obp: msi_1st_msinum=%d\n", 369 msi_state_p->msi_1st_msinum); 370 371 /* msi-data-mask */ 372 msi_state_p->msi_data_mask = ddi_getprop(DDI_DEV_T_ANY, dip, 373 DDI_PROP_DONTPASS, "msi-data-mask", PX_DEFAULT_MSI_DATA_MASK); 374 375 DBG(DBG_MSIQ, dip, "obp: msi-data-mask=0x%x\n", 376 msi_state_p->msi_data_mask); 377 378 /* msi-data-width */ 379 msi_state_p->msi_data_width = ddi_getprop(DDI_DEV_T_ANY, dip, 380 DDI_PROP_DONTPASS, "msix-data-width", PX_DEFAULT_MSI_DATA_WIDTH); 381 382 DBG(DBG_MSIQ, dip, "obp: msix-data-width=%d\n", 383 msi_state_p->msi_data_width); 384 385 /* 386 * Assume MSI is always supported, but also check if MSIX is supported 387 */ 388 if (msi_state_p->msi_data_width) { 389 msi_state_p->msi_type = DDI_INTR_TYPE_MSI; 390 if (msi_state_p->msi_data_width == PX_MSIX_WIDTH) 391 msi_state_p->msi_type |= DDI_INTR_TYPE_MSIX; 392 } 393 394 /* msi-address-ranges */ 395 ret = ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_ALLOC, 396 DDI_PROP_DONTPASS, "msi-address-ranges", (caddr_t)&valuep, 397 &length); 398 399 if (ret == DDI_PROP_SUCCESS) { 400 msi_addr_hi = 401 ((px_msi_address_ranges_t *)valuep)->msi_addr32_hi; 402 msi_addr_lo = 403 ((px_msi_address_ranges_t *)valuep)->msi_addr32_lo; 404 msi_state_p->msi_addr32 = 405 (msi_addr_hi << 32) | msi_addr_lo; 406 407 msi_state_p->msi_addr32_len = 408 ((px_msi_address_ranges_t *)valuep)->msi_addr32_len; 409 410 msi_addr_hi = 411 ((px_msi_address_ranges_t *)valuep)->msi_addr64_hi; 412 msi_addr_lo = 413 ((px_msi_address_ranges_t *)valuep)->msi_addr64_lo; 414 msi_state_p->msi_addr64 = 415 (msi_addr_hi << 32) | msi_addr_lo; 416 417 msi_state_p->msi_addr64_len = 418 ((px_msi_address_ranges_t *)valuep)->msi_addr64_len; 419 420 kmem_free(valuep, (size_t)length); 421 422 msi_state_p->msi_mem_flg = B_FALSE; 423 424 DBG(DBG_MSIQ, dip, "obp: msi_addr32=0x%llx\n", 425 msi_state_p->msi_addr32); 426 427 DBG(DBG_MSIQ, dip, "obp: msi_addr64=0x%llx\n", 428 msi_state_p->msi_addr64); 429 430 return (ret); 431 } 432 433 /* 434 * If msi-address-ranges property does not exist in OBP, Fire 435 * driver will need to allocate memory. 436 * 437 * Allocate 64KB of memory from unused PCI-E address space for the MSI 438 * transactions and program MSI 32-bit address register. 439 * 440 * This register is used by the Fire hardware to compare against the 441 * address of incoming PCI-E 32-bit addressed memory write commands. 442 * If the address matches bits 31:16 then PCI-E command is considered 443 * to be MSI transaction. 444 * 445 * pci_resource_setup() is called in context of PCI hotplug 446 * initialization. 447 * 448 * Setup resource maps for this bus node. 449 */ 450 if (pci_resource_setup(dip) != NDI_SUCCESS) { 451 DBG(DBG_MSIQ, dip, "px_msi_getprops: dip=%s%d" 452 "pci_resource_setup failed\n", 453 ddi_driver_name(dip), ddi_get_instance(dip)); 454 455 return (DDI_FAILURE); 456 } 457 458 msi_state_p->msi_mem_flg = B_TRUE; 459 460 /* 461 * Reserve PCI MEM 32 resources to perform 32 bit MSI transactions. 462 */ 463 bzero((caddr_t)&request, sizeof (ndi_ra_request_t)); 464 request.ra_flags = NDI_RA_ALLOC_BOUNDED; 465 request.ra_boundbase = 0; 466 request.ra_boundlen = PX_MSI_4GIG_LIMIT; 467 request.ra_len = PX_MSI_ADDR_LEN; 468 request.ra_align_mask = 0; 469 470 if (ndi_ra_alloc(dip, &request, &mem_answer, &mem_alen, 471 NDI_RA_TYPE_MEM, NDI_RA_PASS) != NDI_SUCCESS) { 472 DBG(DBG_MSIQ, dip, "px_msi_getprops: Failed to allocate " 473 "64KB mem\n"); 474 475 return (DDI_FAILURE); 476 } 477 478 msi_state_p->msi_addr32 = mem_answer; 479 msi_state_p->msi_addr32_len = mem_alen; 480 481 DBG(DBG_MSIQ, dip, "px_msi_getprops: 32 Addr 0x%llx\n", 482 msi_state_p->msi_addr32); 483 484 /* 485 * Reserve PCI MEM 64 resources to perform 64 bit MSI transactions. 486 * 487 * NOTE: 488 * 489 * Currently OBP do not export any "available" property or range in 490 * the MEM64 space. Hence ndi_ra_alloc() request will return failure. 491 * So, for time being ignore this failure. 492 */ 493 bzero((caddr_t)&request, sizeof (ndi_ra_request_t)); 494 request.ra_flags = NDI_RA_ALLOC_BOUNDED; 495 request.ra_boundbase = PX_MSI_4GIG_LIMIT + 1; 496 request.ra_boundlen = PX_MSI_4GIG_LIMIT; 497 request.ra_len = PX_MSI_ADDR_LEN; 498 request.ra_align_mask = 0; 499 500 if (ndi_ra_alloc(dip, &request, &mem_answer, &mem_alen, 501 NDI_RA_TYPE_MEM, NDI_RA_PASS) != NDI_SUCCESS) { 502 DBG(DBG_MSIQ, dip, "px_msi_getprops: Failed to allocate " 503 "64KB mem\n"); 504 505 return (DDI_SUCCESS); 506 } 507 508 msi_state_p->msi_addr64 = mem_answer; 509 msi_state_p->msi_addr64_len = mem_alen; 510 511 DBG(DBG_MSIQ, dip, "px_msi_getprops: 64 Addr 0x%llx\n", 512 msi_state_p->msi_addr64); 513 514 return (DDI_SUCCESS); 515 } 516