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 irm_params.iparams_default = DDI_DEFAULT_MSIX_ALLOC; 87 if (ndi_irm_create(dip, &irm_params, &irm_pool_p) == DDI_SUCCESS) { 88 msi_state_p->msi_pool_p = irm_pool_p; 89 } else { 90 DBG(DBG_MSIQ, dip, "ndi_irm_create() failed\n"); 91 } 92 93 if ((ret = px_lib_msi_init(dip)) != DDI_SUCCESS) 94 px_msi_detach(px_p); 95 96 return (ret); 97 } 98 99 100 /* 101 * msi_detach() 102 */ 103 void 104 px_msi_detach(px_t *px_p) 105 { 106 dev_info_t *dip = px_p->px_dip; 107 px_msi_state_t *msi_state_p = &px_p->px_ib_p->ib_msi_state; 108 109 DBG(DBG_MSIQ, dip, "px_msi_detach\n"); 110 111 if (msi_state_p->msi_pool_p) { 112 (void) ndi_irm_destroy(msi_state_p->msi_pool_p); 113 } 114 115 if (msi_state_p->msi_addr64 && msi_state_p->msi_mem_flg) { 116 ndi_ra_free(dip, msi_state_p->msi_addr64, 117 msi_state_p->msi_addr64_len, 118 NDI_RA_TYPE_MEM, NDI_RA_PASS); 119 } 120 121 if (msi_state_p->msi_addr32 && msi_state_p->msi_mem_flg) { 122 ndi_ra_free(dip, msi_state_p->msi_addr32, 123 msi_state_p->msi_addr32_len, 124 NDI_RA_TYPE_MEM, NDI_RA_PASS); 125 126 pci_resource_destroy(dip); 127 } 128 129 if (msi_state_p->msi_p) { 130 kmem_free(msi_state_p->msi_p, 131 msi_state_p->msi_cnt * sizeof (px_msi_t)); 132 } 133 134 mutex_destroy(&msi_state_p->msi_mutex); 135 bzero(&px_p->px_ib_p->ib_msi_state, sizeof (px_msi_state_t)); 136 } 137 138 139 /* 140 * msi_alloc() 141 */ 142 /* ARGSUSED */ 143 int 144 px_msi_alloc(px_t *px_p, dev_info_t *rdip, int type, int inum, int msi_count, 145 int flag, int *actual_msi_count_p) 146 { 147 px_msi_state_t *msi_state_p = &px_p->px_ib_p->ib_msi_state; 148 int first, count, i, n; 149 150 DBG(DBG_A_MSIX, px_p->px_dip, "px_msi_alloc: rdip %s:%d " 151 "type 0x%x inum 0x%x msi_count 0x%x\n", ddi_driver_name(rdip), 152 ddi_get_instance(rdip), type, inum, msi_count); 153 154 mutex_enter(&msi_state_p->msi_mutex); 155 156 *actual_msi_count_p = 0; 157 158 /* 159 * MSI interrupts are allocated as contiguous ranges at 160 * power of 2 boundaries from the start of the MSI array. 161 */ 162 if (type == DDI_INTR_TYPE_MSI) { 163 164 /* Search for a range of available interrupts */ 165 for (count = msi_count; count; count >>= 1) { 166 for (first = 0; (first + count) < msi_state_p->msi_cnt; 167 first += count) { 168 for (i = first; i < (first + count); i++) { 169 if (msi_state_p->msi_p[i].msi_state 170 != MSI_STATE_FREE) { 171 break; 172 } 173 } 174 if (i == (first + count)) { 175 goto found_msi; 176 } 177 } 178 DBG(DBG_A_MSIX, px_p->px_dip, "px_msi_alloc: failed\n"); 179 if (count > 1) { 180 DBG(DBG_A_MSIX, px_p->px_dip, "px_msi_alloc: " 181 "Retry MSI allocation with new msi_count " 182 "0x%x\n", count >> 1); 183 } 184 } 185 186 found_msi: 187 /* Set number of available interrupts */ 188 *actual_msi_count_p = count; 189 190 /* Check if successful, and enforce strict behavior */ 191 if ((count == 0) || 192 ((flag == DDI_INTR_ALLOC_STRICT) && (count != msi_count))) { 193 mutex_exit(&msi_state_p->msi_mutex); 194 return (DDI_EAGAIN); 195 } 196 197 /* Allocate the interrupts */ 198 for (i = first; i < (first + count); i++, inum++) { 199 msi_state_p->msi_p[i].msi_state = MSI_STATE_INUSE; 200 msi_state_p->msi_p[i].msi_dip = rdip; 201 msi_state_p->msi_p[i].msi_inum = inum; 202 } 203 } 204 205 /* 206 * MSI-X interrupts are allocated from the end of the MSI 207 * array. There are no concerns about power of 2 boundaries 208 * and the allocated interrupts do not have to be contiguous. 209 */ 210 if (type == DDI_INTR_TYPE_MSIX) { 211 212 /* Count available interrupts, up to count requested */ 213 for (count = 0, i = (msi_state_p->msi_cnt - 1); i >= 0; i--) { 214 if (msi_state_p->msi_p[i].msi_state == MSI_STATE_FREE) { 215 if (count == 0) 216 first = i; 217 count++; 218 if (count == msi_count) 219 break; 220 } 221 } 222 223 /* Set number of available interrupts */ 224 *actual_msi_count_p = count; 225 226 /* Check if successful, and enforce strict behavior */ 227 if ((count == 0) || 228 ((flag == DDI_INTR_ALLOC_STRICT) && (count != msi_count))) { 229 mutex_exit(&msi_state_p->msi_mutex); 230 return (DDI_EAGAIN); 231 } 232 233 /* Allocate the interrupts */ 234 for (n = 0, i = first; n < count; i--) { 235 if (msi_state_p->msi_p[i].msi_state != MSI_STATE_FREE) 236 continue; 237 msi_state_p->msi_p[i].msi_state = MSI_STATE_INUSE; 238 msi_state_p->msi_p[i].msi_dip = rdip; 239 msi_state_p->msi_p[i].msi_inum = inum; 240 inum++; 241 n++; 242 } 243 } 244 245 DBG(DBG_A_MSIX, px_p->px_dip, "px_msi_alloc: rdip %s:%d " 246 "msi_num 0x%x count 0x%x\n", ddi_driver_name(rdip), 247 ddi_get_instance(rdip), first, count); 248 249 mutex_exit(&msi_state_p->msi_mutex); 250 251 return (DDI_SUCCESS); 252 } 253 254 255 /* 256 * msi_free() 257 */ 258 int 259 px_msi_free(px_t *px_p, dev_info_t *rdip, int inum, int msi_count) 260 { 261 px_msi_state_t *msi_state_p = &px_p->px_ib_p->ib_msi_state; 262 int i, n; 263 264 DBG(DBG_R_MSIX, px_p->px_dip, "px_msi_free: rdip 0x%p " 265 "inum 0x%x msi_count 0x%x\n", rdip, inum, msi_count); 266 267 mutex_enter(&msi_state_p->msi_mutex); 268 269 /* 270 * Find and release the specified MSI/X numbers. 271 * 272 * Because the allocations are not always contiguous, perform 273 * a full linear search of the MSI/X table looking for MSI/X 274 * vectors owned by the device with inum values in the range 275 * [inum .. (inum + msi_count - 1)]. 276 */ 277 for (i = 0, n = 0; (i < msi_state_p->msi_cnt) && (n < msi_count); i++) { 278 if ((msi_state_p->msi_p[i].msi_dip == rdip) && 279 (msi_state_p->msi_p[i].msi_inum >= inum) && 280 (msi_state_p->msi_p[i].msi_inum < (inum + msi_count))) { 281 msi_state_p->msi_p[i].msi_dip = NULL; 282 msi_state_p->msi_p[i].msi_inum = 0; 283 msi_state_p->msi_p[i].msi_msiq_id = 0; 284 msi_state_p->msi_p[i].msi_state = MSI_STATE_FREE; 285 n++; 286 } 287 } 288 289 mutex_exit(&msi_state_p->msi_mutex); 290 291 /* Fail if the MSI/X numbers were not found */ 292 if (n < msi_count) 293 return (DDI_FAILURE); 294 295 return (DDI_SUCCESS); 296 } 297 298 /* 299 * msi_get_msinum() 300 */ 301 int 302 px_msi_get_msinum(px_t *px_p, dev_info_t *rdip, int inum, msinum_t *msi_num_p) 303 { 304 px_msi_state_t *msi_state_p = &px_p->px_ib_p->ib_msi_state; 305 int i; 306 307 DBG(DBG_A_MSIX, px_p->px_dip, "px_msi_get_msinum: " 308 "rdip 0x%p inum 0x%x\n", rdip, inum); 309 310 mutex_enter(&msi_state_p->msi_mutex); 311 312 for (i = 0; i < msi_state_p->msi_cnt; i++) { 313 if ((msi_state_p->msi_p[i].msi_inum == inum) && 314 (msi_state_p->msi_p[i].msi_dip == rdip)) { 315 316 *msi_num_p = msi_state_p->msi_p[i].msi_msinum; 317 318 DBG(DBG_A_MSIX, px_p->px_dip, "px_msi_get_msinum: " 319 "inum 0x%x msi 0x%x\n", inum, *msi_num_p); 320 321 mutex_exit(&msi_state_p->msi_mutex); 322 return (DDI_SUCCESS); 323 } 324 } 325 326 if (i >= msi_state_p->msi_cnt) 327 DBG(DBG_A_MSIX, px_p->px_dip, "px_msi_get_msinum: " 328 "no msi for inum 0x%x\n", inum); 329 330 mutex_exit(&msi_state_p->msi_mutex); 331 return (DDI_FAILURE); 332 } 333 334 /* 335 * px_msi_get_props() 336 */ 337 static int 338 px_msi_get_props(px_t *px_p) 339 { 340 dev_info_t *dip = px_p->px_dip; 341 px_msi_state_t *msi_state_p = &px_p->px_ib_p->ib_msi_state; 342 int ret = DDI_SUCCESS; 343 int length = sizeof (int); 344 int *valuep = NULL; 345 uint64_t msi_addr_hi, msi_addr_lo; 346 uint64_t mem_answer, mem_alen; 347 ndi_ra_request_t request; 348 349 DBG(DBG_MSIQ, dip, "px_msi_get_props\n"); 350 351 /* #msi */ 352 msi_state_p->msi_cnt = ddi_getprop(DDI_DEV_T_ANY, dip, 353 DDI_PROP_DONTPASS, "#msi", PX_DEFAULT_MSI_CNT); 354 355 DBG(DBG_MSIQ, dip, "obp: #msi=%d\n", 356 msi_state_p->msi_cnt); 357 358 /* msi-ranges: msi# field */ 359 ret = ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_ALLOC, 360 DDI_PROP_DONTPASS, "msi-ranges", (caddr_t)&valuep, &length); 361 362 if (ret == DDI_PROP_SUCCESS) { 363 msi_state_p->msi_1st_msinum = 364 ((px_msi_ranges_t *)valuep)->msi_no; 365 kmem_free(valuep, (size_t)length); 366 } else 367 msi_state_p->msi_1st_msinum = PX_DEFAULT_MSI_1ST_MSINUM; 368 369 DBG(DBG_MSIQ, dip, "obp: msi_1st_msinum=%d\n", 370 msi_state_p->msi_1st_msinum); 371 372 /* msi-data-mask */ 373 msi_state_p->msi_data_mask = ddi_getprop(DDI_DEV_T_ANY, dip, 374 DDI_PROP_DONTPASS, "msi-data-mask", PX_DEFAULT_MSI_DATA_MASK); 375 376 DBG(DBG_MSIQ, dip, "obp: msi-data-mask=0x%x\n", 377 msi_state_p->msi_data_mask); 378 379 /* msi-data-width */ 380 msi_state_p->msi_data_width = ddi_getprop(DDI_DEV_T_ANY, dip, 381 DDI_PROP_DONTPASS, "msix-data-width", PX_DEFAULT_MSI_DATA_WIDTH); 382 383 DBG(DBG_MSIQ, dip, "obp: msix-data-width=%d\n", 384 msi_state_p->msi_data_width); 385 386 /* 387 * Assume MSI is always supported, but also check if MSIX is supported 388 */ 389 if (msi_state_p->msi_data_width) { 390 msi_state_p->msi_type = DDI_INTR_TYPE_MSI; 391 if (msi_state_p->msi_data_width == PX_MSIX_WIDTH) 392 msi_state_p->msi_type |= DDI_INTR_TYPE_MSIX; 393 } 394 395 /* msi-address-ranges */ 396 ret = ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_ALLOC, 397 DDI_PROP_DONTPASS, "msi-address-ranges", (caddr_t)&valuep, 398 &length); 399 400 if (ret == DDI_PROP_SUCCESS) { 401 msi_addr_hi = 402 ((px_msi_address_ranges_t *)valuep)->msi_addr32_hi; 403 msi_addr_lo = 404 ((px_msi_address_ranges_t *)valuep)->msi_addr32_lo; 405 msi_state_p->msi_addr32 = 406 (msi_addr_hi << 32) | msi_addr_lo; 407 408 msi_state_p->msi_addr32_len = 409 ((px_msi_address_ranges_t *)valuep)->msi_addr32_len; 410 411 msi_addr_hi = 412 ((px_msi_address_ranges_t *)valuep)->msi_addr64_hi; 413 msi_addr_lo = 414 ((px_msi_address_ranges_t *)valuep)->msi_addr64_lo; 415 msi_state_p->msi_addr64 = 416 (msi_addr_hi << 32) | msi_addr_lo; 417 418 msi_state_p->msi_addr64_len = 419 ((px_msi_address_ranges_t *)valuep)->msi_addr64_len; 420 421 kmem_free(valuep, (size_t)length); 422 423 msi_state_p->msi_mem_flg = B_FALSE; 424 425 DBG(DBG_MSIQ, dip, "obp: msi_addr32=0x%llx\n", 426 msi_state_p->msi_addr32); 427 428 DBG(DBG_MSIQ, dip, "obp: msi_addr64=0x%llx\n", 429 msi_state_p->msi_addr64); 430 431 return (ret); 432 } 433 434 /* 435 * If msi-address-ranges property does not exist in OBP, Fire 436 * driver will need to allocate memory. 437 * 438 * Allocate 64KB of memory from unused PCI-E address space for the MSI 439 * transactions and program MSI 32-bit address register. 440 * 441 * This register is used by the Fire hardware to compare against the 442 * address of incoming PCI-E 32-bit addressed memory write commands. 443 * If the address matches bits 31:16 then PCI-E command is considered 444 * to be MSI transaction. 445 * 446 * pci_resource_setup() is called in context of PCI hotplug 447 * initialization. 448 * 449 * Setup resource maps for this bus node. 450 */ 451 if (pci_resource_setup(dip) != NDI_SUCCESS) { 452 DBG(DBG_MSIQ, dip, "px_msi_getprops: dip=%s%d" 453 "pci_resource_setup failed\n", 454 ddi_driver_name(dip), ddi_get_instance(dip)); 455 456 return (DDI_FAILURE); 457 } 458 459 msi_state_p->msi_mem_flg = B_TRUE; 460 461 /* 462 * Reserve PCI MEM 32 resources to perform 32 bit MSI transactions. 463 */ 464 bzero((caddr_t)&request, sizeof (ndi_ra_request_t)); 465 request.ra_flags = NDI_RA_ALLOC_BOUNDED; 466 request.ra_boundbase = 0; 467 request.ra_boundlen = PX_MSI_4GIG_LIMIT; 468 request.ra_len = PX_MSI_ADDR_LEN; 469 request.ra_align_mask = 0; 470 471 if (ndi_ra_alloc(dip, &request, &mem_answer, &mem_alen, 472 NDI_RA_TYPE_MEM, NDI_RA_PASS) != NDI_SUCCESS) { 473 DBG(DBG_MSIQ, dip, "px_msi_getprops: Failed to allocate " 474 "64KB mem\n"); 475 476 return (DDI_FAILURE); 477 } 478 479 msi_state_p->msi_addr32 = mem_answer; 480 msi_state_p->msi_addr32_len = mem_alen; 481 482 DBG(DBG_MSIQ, dip, "px_msi_getprops: 32 Addr 0x%llx\n", 483 msi_state_p->msi_addr32); 484 485 /* 486 * Reserve PCI MEM 64 resources to perform 64 bit MSI transactions. 487 * 488 * NOTE: 489 * 490 * Currently OBP do not export any "available" property or range in 491 * the MEM64 space. Hence ndi_ra_alloc() request will return failure. 492 * So, for time being ignore this failure. 493 */ 494 bzero((caddr_t)&request, sizeof (ndi_ra_request_t)); 495 request.ra_flags = NDI_RA_ALLOC_BOUNDED; 496 request.ra_boundbase = PX_MSI_4GIG_LIMIT + 1; 497 request.ra_boundlen = PX_MSI_4GIG_LIMIT; 498 request.ra_len = PX_MSI_ADDR_LEN; 499 request.ra_align_mask = 0; 500 501 if (ndi_ra_alloc(dip, &request, &mem_answer, &mem_alen, 502 NDI_RA_TYPE_MEM, NDI_RA_PASS) != NDI_SUCCESS) { 503 DBG(DBG_MSIQ, dip, "px_msi_getprops: Failed to allocate " 504 "64KB mem\n"); 505 506 return (DDI_SUCCESS); 507 } 508 509 msi_state_p->msi_addr64 = mem_answer; 510 msi_state_p->msi_addr64_len = mem_alen; 511 512 DBG(DBG_MSIQ, dip, "px_msi_getprops: 64 Addr 0x%llx\n", 513 msi_state_p->msi_addr64); 514 515 return (DDI_SUCCESS); 516 } 517