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 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 * Copyright 2019 Joyent, Inc.
25 */
26
27 /*
28 * This file contains PCI HotPlug functionality that is compatible with the
29 * PCI SHPC specification 1.x.
30 *
31 * NOTE: This file is compiled and delivered through misc/pcie module.
32 */
33
34 #include <sys/note.h>
35 #include <sys/conf.h>
36 #include <sys/kmem.h>
37 #include <sys/kstat.h>
38 #include <sys/debug.h>
39 #include <sys/vtrace.h>
40 #include <sys/autoconf.h>
41 #include <sys/varargs.h>
42 #include <sys/hwconf.h>
43 #include <sys/ddi_impldefs.h>
44 #include <sys/callb.h>
45 #include <sys/ddi.h>
46 #include <sys/sunddi.h>
47 #include <sys/sunndi.h>
48 #include <sys/sysevent/dr.h>
49 #include <sys/ndi_impldefs.h>
50 #include <sys/pci_impl.h>
51 #include <sys/hotplug/pci/pcie_hp.h>
52 #include <sys/hotplug/pci/pcishpc.h>
53
54 typedef struct pcishpc_prop {
55 char *prop_name;
56 char *prop_value;
57 } pcishpc_prop_t;
58
59 static pcishpc_prop_t pcishpc_props[] = {
60 { PCIEHPC_PROP_LED_FAULT, PCIEHPC_PROP_VALUE_LED },
61 { PCIEHPC_PROP_LED_POWER, PCIEHPC_PROP_VALUE_LED },
62 { PCIEHPC_PROP_LED_ATTN, PCIEHPC_PROP_VALUE_LED },
63 { PCIEHPC_PROP_LED_ACTIVE, PCIEHPC_PROP_VALUE_LED },
64 { PCIEHPC_PROP_CARD_TYPE, PCIEHPC_PROP_VALUE_TYPE },
65 { PCIEHPC_PROP_BOARD_TYPE, PCIEHPC_PROP_VALUE_TYPE },
66 { PCIEHPC_PROP_SLOT_CONDITION, PCIEHPC_PROP_VALUE_TYPE }
67 };
68
69 /* reset delay to 1 sec. */
70 static int pcishpc_reset_delay = 1000000;
71
72 /* Local function prototype */
73 static pcie_hp_ctrl_t *pcishpc_create_controller(dev_info_t *dip);
74 static int pcishpc_setup_controller(pcie_hp_ctrl_t *ctrl_p);
75 static int pcishpc_destroy_controller(dev_info_t *dip);
76 static pcie_hp_slot_t *pcishpc_create_slot(pcie_hp_ctrl_t *ctrl_p);
77 static int pcishpc_register_slot(pcie_hp_ctrl_t *ctrl_p, int slot);
78 static int pcishpc_destroy_slots(pcie_hp_ctrl_t *ctrl_p);
79 static int pcishpc_slot_get_property(pcie_hp_slot_t *slot_p,
80 ddi_hp_property_t *arg, ddi_hp_property_t *rval);
81 static int pcishpc_slot_set_property(pcie_hp_slot_t *slot_p,
82 ddi_hp_property_t *arg, ddi_hp_property_t *rval);
83 static int pcishpc_issue_command(pcie_hp_ctrl_t *ctrl_p,
84 uint32_t cmd_code);
85 static int pcishpc_wait_busy(pcie_hp_ctrl_t *ctrl_p);
86 static void pcishpc_attn_btn_handler(pcie_hp_slot_t *slot_p);
87 static void pcishpc_get_slot_state(pcie_hp_slot_t *slot_p);
88 static int pcishpc_set_slot_state(pcie_hp_slot_t *slot_p,
89 ddi_hp_cn_state_t new_slot_state);
90 static void pcishpc_set_slot_name(pcie_hp_ctrl_t *ctrl_p, int slot);
91 static int pcishpc_set_bus_speed(pcie_hp_slot_t *slot_p);
92 static int pcishpc_setled(pcie_hp_slot_t *slot_p, pcie_hp_led_t led,
93 pcie_hp_led_state_t state);
94 static int pcishpc_led_shpc_to_hpc(int state);
95 static int pcishpc_led_hpc_to_shpc(int state);
96 static int pcishpc_slot_shpc_to_hpc(int shpc_state);
97 static int pcishpc_slot_hpc_to_shpc(int state);
98 static char *pcishpc_slot_textslotstate(ddi_hp_cn_state_t state);
99 static char *pcishpc_slot_textledstate(pcie_hp_led_state_t state);
100
101 static uint32_t pcishpc_read_reg(pcie_hp_ctrl_t *ctrl_p, int reg);
102 static void pcishpc_write_reg(pcie_hp_ctrl_t *ctrl_p, int reg,
103 uint32_t data);
104
105 static int pcishpc_upgrade_slot_state(pcie_hp_slot_t *slot_p,
106 ddi_hp_cn_state_t target_state);
107 static int pcishpc_downgrade_slot_state(pcie_hp_slot_t *slot_p,
108 ddi_hp_cn_state_t target_state);
109 static int pcishpc_change_slot_state(pcie_hp_slot_t *slot_p,
110 ddi_hp_cn_state_t target_state);
111
112 static int pcishpc_slot_poweron(pcie_hp_slot_t *slot_p,
113 ddi_hp_cn_state_t *result_state);
114 static int pcishpc_slot_poweroff(pcie_hp_slot_t *slot_p,
115 ddi_hp_cn_state_t *result_state);
116 static int pcishpc_slot_probe(pcie_hp_slot_t *slot_p);
117 static int pcishpc_slot_unprobe(pcie_hp_slot_t *slot_p);
118 #ifdef DEBUG
119 static void pcishpc_dump_regs(pcie_hp_ctrl_t *ctrl_p);
120 #endif /* DEBUG */
121
122
123 /*
124 * Global functions (called by other drivers/modules)
125 */
126
127 /*
128 * pcishpc_init()
129 *
130 * Install and configure an SHPC controller and register the HotPlug slots
131 * with the Solaris HotPlug framework. This function is usually called by
132 * a PCI bridge Nexus driver that has a built in SHPC controller.
133 */
134 int
pcishpc_init(dev_info_t * dip)135 pcishpc_init(dev_info_t *dip)
136 {
137 pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
138 pcie_hp_ctrl_t *ctrl_p;
139 int i;
140
141 PCIE_DBG("pcishpc_init() called from %s#%d\n",
142 ddi_driver_name(dip), ddi_get_instance(dip));
143
144 if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) != NULL) {
145 PCIE_DBG("pcishpc_init() shpc instance already "
146 "initialized!\n");
147 return (DDI_SUCCESS);
148 }
149
150 /* Initialize soft state structure for the SHPC instance. */
151 ctrl_p = pcishpc_create_controller(dip);
152
153 if (ctrl_p == NULL) {
154 PCIE_DBG("pcishpc_init() failed to create shpc softstate\n");
155 return (DDI_FAILURE);
156 }
157
158 if (pcishpc_setup_controller(ctrl_p) != DDI_SUCCESS) {
159 PCIE_DBG("pcishpc_init() failed to setup controller\n");
160 goto cleanup;
161 }
162
163 /*
164 * Setup resource maps for this bus node.
165 */
166 (void) pci_resource_setup(dip);
167
168 #ifdef DEBUG
169 PCIE_DBG("%s%d: P2P bridge register dump:\n",
170 ddi_driver_name(dip), ddi_get_instance(dip));
171
172 for (i = 0; i < 0x100; i += 4) {
173 PCIE_DBG("SHPC Cfg reg 0x%02x: %08x\n", i,
174 pci_config_get32(bus_p->bus_cfg_hdl, i));
175 }
176 #endif /* DEBUG */
177
178 /* Setup each HotPlug slot on this SHPC controller. */
179 for (i = 0; i < ctrl_p->hc_num_slots_impl; i++) {
180 if (pcishpc_register_slot(ctrl_p, i) != DDI_SUCCESS) {
181 PCIE_DBG("pcishpc_init() failed to register "
182 "slot %d\n", i);
183 goto cleanup1;
184 }
185 if (pcie_create_minor_node(ctrl_p, i) != DDI_SUCCESS) {
186 PCIE_DBG("pcishpc_init() failed to create "
187 "minor node for slot %d\n", i);
188 goto cleanup1;
189 }
190 }
191
192 #ifdef DEBUG
193 /* Dump out the SHPC registers. */
194 pcishpc_dump_regs(ctrl_p);
195 #endif /* DEBUG */
196
197 PCIE_DBG("pcishpc_init() success(dip=%p)\n", dip);
198 return (DDI_SUCCESS);
199
200 cleanup1:
201 for (i = 0; i < ctrl_p->hc_num_slots_impl; i++) {
202 if (ctrl_p->hc_slots[i] == NULL)
203 continue;
204
205 pcie_remove_minor_node(ctrl_p, i);
206 }
207 (void) pci_resource_destroy(dip);
208 cleanup:
209 (void) pcishpc_destroy_controller(dip);
210 return (DDI_FAILURE);
211 }
212
213 /*
214 * pcishpc_uninit()
215 * Unload the HogPlug controller driver and deallocate all resources.
216 */
217 int
pcishpc_uninit(dev_info_t * dip)218 pcishpc_uninit(dev_info_t *dip)
219 {
220 pcie_hp_ctrl_t *ctrl_p;
221 int i;
222
223 PCIE_DBG("pcishpc_uninit() called(dip=%p)\n", dip);
224
225 ctrl_p = PCIE_GET_HP_CTRL(dip);
226
227 if (!ctrl_p) {
228 PCIE_DBG("pcishpc_uninit() Unable to find softstate\n");
229 return (DDI_FAILURE);
230 }
231
232 for (i = 0; i < PCIE_HP_MAX_SLOTS; i++) {
233 if (ctrl_p->hc_slots[i] == NULL)
234 continue;
235
236 pcie_remove_minor_node(ctrl_p, i);
237 }
238
239 ctrl_p->hc_flags = 0;
240
241 /*
242 * Destroy resource maps for this bus node.
243 */
244 (void) pci_resource_destroy(dip);
245
246 (void) pcishpc_destroy_controller(dip);
247
248 PCIE_DBG("pcishpc_uninit() success(dip=%p)\n", dip);
249
250 return (DDI_SUCCESS);
251 }
252
253 /*
254 * pcishpc_intr()
255 *
256 * This is the SHPC controller interrupt handler.
257 */
258 int
pcishpc_intr(dev_info_t * dip)259 pcishpc_intr(dev_info_t *dip)
260 {
261 pcie_hp_ctrl_t *ctrl_p;
262 uint32_t irq_locator, irq_serr_locator, reg;
263 int slot;
264
265 PCIE_DBG("pcishpc_intr() called\n");
266
267 /* get the soft state structure for this dip */
268 if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL)
269 return (DDI_INTR_UNCLAIMED);
270
271 mutex_enter(&ctrl_p->hc_mutex);
272
273 if (!(ctrl_p->hc_flags & PCIE_HP_INITIALIZED_FLAG)) {
274 PCIE_DBG("pcishpc_intr() unclaimed\n");
275 mutex_exit(&ctrl_p->hc_mutex);
276 return (DDI_INTR_UNCLAIMED);
277 }
278
279 PCIE_DBG("pcishpc_intr() interrupt received\n");
280
281 reg = pcishpc_read_reg(ctrl_p, PCI_HP_CTRL_SERR_INT_REG);
282
283 if (reg & PCI_HP_SERR_INT_CMD_COMPLETE_IRQ) {
284 PCIE_DBG("pcishpc_intr() "
285 "PCI_HP_SERR_INT_CMD_COMPLETE_IRQ detected\n");
286 ctrl_p->hc_cmd_pending = B_FALSE;
287 cv_signal(&ctrl_p->hc_cmd_comp_cv);
288 }
289
290 if (reg & PCI_HP_SERR_INT_ARBITER_IRQ) {
291 PCIE_DBG("pcishpc_intr() PCI_HP_SERR_INT_ARBITER_IRQ "
292 "detected\n");
293 ctrl_p->hc_arbiter_timeout = B_TRUE;
294 }
295
296 /* Write back the SERR INT register to acknowledge the IRQs. */
297 pcishpc_write_reg(ctrl_p, PCI_HP_CTRL_SERR_INT_REG, reg);
298
299 irq_locator = pcishpc_read_reg(ctrl_p, PCI_HP_IRQ_LOCATOR_REG);
300 irq_serr_locator = pcishpc_read_reg(ctrl_p, PCI_HP_SERR_LOCATOR_REG);
301
302 /* Check for slot events that might have occured. */
303 for (slot = 0; slot < ctrl_p->hc_num_slots_impl; slot++) {
304 if ((irq_locator & (PCI_HP_IRQ_SLOT_N_PENDING<<slot)) ||
305 (irq_serr_locator &
306 (PCI_HP_IRQ_SERR_SLOT_N_PENDING<<slot))) {
307 PCIE_DBG("pcishpc_intr() slot %d and "
308 "pending IRQ\n", slot+1);
309
310 reg = pcishpc_read_reg(ctrl_p,
311 PCI_HP_LOGICAL_SLOT_REGS+slot);
312
313 if (reg & PCI_HP_SLOT_PRESENCE_DETECTED)
314 PCIE_DBG("slot %d: "
315 "PCI_HP_SLOT_PRESENCE_DETECTED\n",
316 slot+1);
317
318 if (reg & PCI_HP_SLOT_ISO_PWR_DETECTED)
319 PCIE_DBG("slot %d: "
320 "PCI_HP_SLOT_ISO_PWR_DETECTED\n",
321 slot+1);
322
323 if (reg & PCI_HP_SLOT_ATTN_DETECTED) {
324 PCIE_DBG("slot %d: "
325 "PCI_HP_SLOT_ATTN_DETECTED\n", slot+1);
326
327 /*
328 * if ATTN button event is still pending
329 * then cancel it
330 */
331 if (ctrl_p->hc_slots[slot]->
332 hs_attn_btn_pending == B_TRUE)
333 ctrl_p->hc_slots[slot]->
334 hs_attn_btn_pending = B_FALSE;
335
336 /* wake up the ATTN event handler */
337 cv_signal(&ctrl_p->hc_slots[slot]->
338 hs_attn_btn_cv);
339 }
340
341 if (reg & PCI_HP_SLOT_MRL_DETECTED)
342 PCIE_DBG("slot %d: "
343 "PCI_HP_SLOT_MRL_DETECTED\n", slot+1);
344
345 if (reg & PCI_HP_SLOT_POWER_DETECTED)
346 PCIE_DBG("slot %d: "
347 "PCI_HP_SLOT_POWER_DETECTED\n", slot+1);
348
349 /* Acknoledge any slot interrupts */
350 pcishpc_write_reg(ctrl_p, PCI_HP_LOGICAL_SLOT_REGS+slot,
351 reg);
352 }
353 }
354
355 mutex_exit(&ctrl_p->hc_mutex);
356
357 PCIE_DBG("pcishpc_intr() claimed\n");
358
359 return (DDI_INTR_CLAIMED);
360 }
361
362 int
pcishpc_slot_get_property(pcie_hp_slot_t * slot_p,ddi_hp_property_t * arg,ddi_hp_property_t * rval)363 pcishpc_slot_get_property(pcie_hp_slot_t *slot_p, ddi_hp_property_t *arg,
364 ddi_hp_property_t *rval)
365 {
366 ddi_hp_property_t request, result;
367 #ifdef _SYSCALL32_IMPL
368 ddi_hp_property32_t request32, result32;
369 #endif
370 pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
371 nvlist_t *prop_list;
372 nvlist_t *prop_rlist; /* nvlist for return values */
373 nvpair_t *prop_pair;
374 char *name, *value;
375 int ret = DDI_SUCCESS;
376 int i, n;
377 boolean_t get_all_prop = B_FALSE;
378
379 if (get_udatamodel() == DATAMODEL_NATIVE) {
380 if (copyin(arg, &request, sizeof (ddi_hp_property_t)) ||
381 copyin(rval, &result, sizeof (ddi_hp_property_t)))
382 return (DDI_FAILURE);
383 }
384 #ifdef _SYSCALL32_IMPL
385 else {
386 bzero(&request, sizeof (request));
387 bzero(&result, sizeof (result));
388 if (copyin(arg, &request32, sizeof (ddi_hp_property32_t)) ||
389 copyin(rval, &result32, sizeof (ddi_hp_property32_t)))
390 return (DDI_FAILURE);
391 request.nvlist_buf = (char *)(uintptr_t)request32.nvlist_buf;
392 request.buf_size = request32.buf_size;
393 result.nvlist_buf = (char *)(uintptr_t)result32.nvlist_buf;
394 result.buf_size = result32.buf_size;
395 }
396 #endif
397
398 if ((ret = pcie_copyin_nvlist(request.nvlist_buf, request.buf_size,
399 &prop_list)) != DDI_SUCCESS)
400 return (ret);
401
402 if (nvlist_alloc(&prop_rlist, NV_UNIQUE_NAME, 0)) {
403 ret = DDI_ENOMEM;
404 goto get_prop_cleanup;
405 }
406
407 /* check whether the requested property is "all" or "help" */
408 prop_pair = nvlist_next_nvpair(prop_list, NULL);
409 if (prop_pair && !nvlist_next_nvpair(prop_list, prop_pair)) {
410 name = nvpair_name(prop_pair);
411 n = sizeof (pcishpc_props) / sizeof (pcishpc_prop_t);
412
413 if (strcmp(name, PCIEHPC_PROP_ALL) == 0) {
414 (void) nvlist_remove_all(prop_list, PCIEHPC_PROP_ALL);
415
416 /*
417 * Add all properties into the request list, so that we
418 * will get the values in the following for loop.
419 */
420 for (i = 0; i < n; i++) {
421 if (nvlist_add_string(prop_list,
422 pcishpc_props[i].prop_name, "") != 0) {
423 ret = DDI_FAILURE;
424 goto get_prop_cleanup1;
425 }
426 }
427 get_all_prop = B_TRUE;
428 } else if (strcmp(name, PCIEHPC_PROP_HELP) == 0) {
429 /*
430 * Empty the request list, and add help strings into the
431 * return list. We will pass the following for loop.
432 */
433 (void) nvlist_remove_all(prop_list, PCIEHPC_PROP_HELP);
434
435 for (i = 0; i < n; i++) {
436 if (nvlist_add_string(prop_rlist,
437 pcishpc_props[i].prop_name,
438 pcishpc_props[i].prop_value) != 0) {
439 ret = DDI_FAILURE;
440 goto get_prop_cleanup1;
441 }
442 }
443 }
444 }
445
446 mutex_enter(&ctrl_p->hc_mutex);
447
448 /* get the current slot state */
449 pcishpc_get_slot_state(slot_p);
450
451 /* for each requested property, get the value and add it to nvlist */
452 prop_pair = NULL;
453 while ((prop_pair = nvlist_next_nvpair(prop_list, prop_pair)) != NULL) {
454 name = nvpair_name(prop_pair);
455 value = NULL;
456
457 if (strcmp(name, PCIEHPC_PROP_LED_FAULT) == 0) {
458 value = pcie_led_state_text(
459 slot_p->hs_fault_led_state);
460 } else if (strcmp(name, PCIEHPC_PROP_LED_POWER) == 0) {
461 value = pcie_led_state_text(
462 slot_p->hs_power_led_state);
463 } else if (strcmp(name, PCIEHPC_PROP_LED_ATTN) == 0) {
464 value = pcie_led_state_text(
465 slot_p->hs_attn_led_state);
466 } else if (strcmp(name, PCIEHPC_PROP_LED_ACTIVE) == 0) {
467 value = pcie_led_state_text(
468 slot_p->hs_active_led_state);
469 } else if (strcmp(name, PCIEHPC_PROP_CARD_TYPE) == 0) {
470 ddi_acc_handle_t handle;
471 dev_info_t *cdip;
472 uint8_t prog_class, base_class, sub_class;
473 size_t i;
474
475 mutex_exit(&ctrl_p->hc_mutex);
476 cdip = pcie_hp_devi_find(
477 ctrl_p->hc_dip, slot_p->hs_device_num, 0);
478 mutex_enter(&ctrl_p->hc_mutex);
479
480 if ((slot_p->hs_info.cn_state !=
481 DDI_HP_CN_STATE_ENABLED) || (cdip == NULL)) {
482 /*
483 * When getting all properties, just ignore the
484 * one that's not available under certain state.
485 */
486 if (get_all_prop)
487 continue;
488
489 ret = DDI_ENOTSUP;
490 goto get_prop_cleanup2;
491 }
492
493 if (pci_config_setup(cdip, &handle) != DDI_SUCCESS) {
494 ret = DDI_FAILURE;
495 goto get_prop_cleanup2;
496 }
497
498 prog_class = pci_config_get8(handle,
499 PCI_CONF_PROGCLASS);
500 base_class = pci_config_get8(handle, PCI_CONF_BASCLASS);
501 sub_class = pci_config_get8(handle, PCI_CONF_SUBCLASS);
502 pci_config_teardown(&handle);
503
504 for (i = 0; i < class_pci_items; i++) {
505 if ((base_class == class_pci[i].base_class) &&
506 (sub_class == class_pci[i].sub_class) &&
507 (prog_class == class_pci[i].prog_class)) {
508 value = class_pci[i].short_desc;
509 break;
510 }
511 }
512 if (i == class_pci_items)
513 value = PCIEHPC_PROP_VALUE_UNKNOWN;
514 } else if (strcmp(name, PCIEHPC_PROP_BOARD_TYPE) == 0) {
515 if (slot_p->hs_info.cn_state <= DDI_HP_CN_STATE_EMPTY)
516 value = PCIEHPC_PROP_VALUE_UNKNOWN;
517 else
518 value = PCIEHPC_PROP_VALUE_PCIHOTPLUG;
519 } else if (strcmp(name, PCIEHPC_PROP_SLOT_CONDITION) == 0) {
520 value = pcie_slot_condition_text(slot_p->hs_condition);
521 } else {
522 /* unsupported property */
523 PCIE_DBG("Unsupported property: %s\n", name);
524
525 ret = DDI_ENOTSUP;
526 goto get_prop_cleanup2;
527 }
528 if (nvlist_add_string(prop_rlist, name, value) != 0) {
529 ret = DDI_FAILURE;
530 goto get_prop_cleanup2;
531 }
532 }
533
534 // pack nvlist and copyout
535 if ((ret = pcie_copyout_nvlist(prop_rlist, result.nvlist_buf,
536 &result.buf_size)) != DDI_SUCCESS) {
537 goto get_prop_cleanup2;
538 }
539 if (get_udatamodel() == DATAMODEL_NATIVE) {
540 if (copyout(&result, rval, sizeof (ddi_hp_property_t))) {
541 ret = DDI_FAILURE;
542 goto get_prop_cleanup2;
543 }
544 }
545 #ifdef _SYSCALL32_IMPL
546 else {
547 if (result.buf_size > UINT32_MAX) {
548 ret = DDI_FAILURE;
549 } else {
550 result32.buf_size = (uint32_t)result.buf_size;
551 if (copyout(&result32, rval,
552 sizeof (ddi_hp_property32_t)))
553 ret = DDI_FAILURE;
554 }
555 }
556 #endif
557
558 get_prop_cleanup2:
559 mutex_exit(&ctrl_p->hc_mutex);
560 get_prop_cleanup1:
561 nvlist_free(prop_rlist);
562 get_prop_cleanup:
563 nvlist_free(prop_list);
564 return (ret);
565 }
566
567 int
pcishpc_slot_set_property(pcie_hp_slot_t * slot_p,ddi_hp_property_t * arg,ddi_hp_property_t * rval)568 pcishpc_slot_set_property(pcie_hp_slot_t *slot_p, ddi_hp_property_t *arg,
569 ddi_hp_property_t *rval)
570 {
571 ddi_hp_property_t request, result;
572 #ifdef _SYSCALL32_IMPL
573 ddi_hp_property32_t request32, result32;
574 #endif
575 pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
576 nvlist_t *prop_list;
577 nvlist_t *prop_rlist;
578 nvpair_t *prop_pair;
579 char *name, *value;
580 pcie_hp_led_state_t led_state;
581 int ret = DDI_SUCCESS;
582
583 if (get_udatamodel() == DATAMODEL_NATIVE) {
584 if (copyin(arg, &request, sizeof (ddi_hp_property_t)))
585 return (DDI_FAILURE);
586 if (rval &&
587 copyin(rval, &result, sizeof (ddi_hp_property_t)))
588 return (DDI_FAILURE);
589 }
590 #ifdef _SYSCALL32_IMPL
591 else {
592 bzero(&request, sizeof (request));
593 bzero(&result, sizeof (result));
594 if (copyin(arg, &request32, sizeof (ddi_hp_property32_t)))
595 return (DDI_FAILURE);
596 if (rval &&
597 copyin(rval, &result32, sizeof (ddi_hp_property32_t)))
598 return (DDI_FAILURE);
599 request.nvlist_buf = (char *)(uintptr_t)request32.nvlist_buf;
600 request.buf_size = request32.buf_size;
601 if (rval) {
602 result.nvlist_buf =
603 (char *)(uintptr_t)result32.nvlist_buf;
604 result.buf_size = result32.buf_size;
605 }
606 }
607 #endif
608
609 if ((ret = pcie_copyin_nvlist(request.nvlist_buf, request.buf_size,
610 &prop_list)) != DDI_SUCCESS)
611 return (ret);
612
613 /* check whether the requested property is "help" */
614 prop_pair = nvlist_next_nvpair(prop_list, NULL);
615 if (prop_pair && !nvlist_next_nvpair(prop_list, prop_pair) &&
616 (strcmp(nvpair_name(prop_pair), PCIEHPC_PROP_HELP) == 0)) {
617 if (!rval) {
618 ret = DDI_ENOTSUP;
619 goto set_prop_cleanup;
620 }
621
622 if (nvlist_alloc(&prop_rlist, NV_UNIQUE_NAME, 0)) {
623 ret = DDI_ENOMEM;
624 goto set_prop_cleanup;
625 }
626 if (nvlist_add_string(prop_rlist, PCIEHPC_PROP_LED_ATTN,
627 PCIEHPC_PROP_VALUE_LED) != 0) {
628 ret = DDI_FAILURE;
629 goto set_prop_cleanup1;
630 }
631
632 if ((ret = pcie_copyout_nvlist(prop_rlist, result.nvlist_buf,
633 &result.buf_size)) != DDI_SUCCESS) {
634 goto set_prop_cleanup1;
635 }
636 if (get_udatamodel() == DATAMODEL_NATIVE) {
637 if (copyout(&result, rval,
638 sizeof (ddi_hp_property_t))) {
639 ret = DDI_FAILURE;
640 goto set_prop_cleanup1;
641 }
642 }
643 #ifdef _SYSCALL32_IMPL
644 else {
645 if (result.buf_size > UINT32_MAX) {
646 ret = DDI_FAILURE;
647 goto set_prop_cleanup1;
648 } else {
649 result32.buf_size = (uint32_t)result.buf_size;
650 if (copyout(&result32, rval,
651 sizeof (ddi_hp_property32_t))) {
652 ret = DDI_FAILURE;
653 goto set_prop_cleanup1;
654 }
655 }
656 }
657 #endif
658 set_prop_cleanup1:
659 nvlist_free(prop_rlist);
660 nvlist_free(prop_list);
661 return (ret);
662 }
663
664 /* Validate the request */
665 prop_pair = NULL;
666 while ((prop_pair = nvlist_next_nvpair(prop_list, prop_pair)) != NULL) {
667 name = nvpair_name(prop_pair);
668 if (nvpair_type(prop_pair) != DATA_TYPE_STRING) {
669 PCIE_DBG("Unexpected data type of setting "
670 "property %s.\n", name);
671 ret = DDI_EINVAL;
672 goto set_prop_cleanup;
673 }
674 if (nvpair_value_string(prop_pair, &value)) {
675 PCIE_DBG("Get string value failed for property %s.\n",
676 name);
677 ret = DDI_FAILURE;
678 goto set_prop_cleanup;
679 }
680
681 if (strcmp(name, PCIEHPC_PROP_LED_ATTN) == 0) {
682 if ((strcmp(value, PCIEHPC_PROP_VALUE_ON) != 0) &&
683 (strcmp(value, PCIEHPC_PROP_VALUE_OFF) != 0) &&
684 (strcmp(value, PCIEHPC_PROP_VALUE_BLINK) != 0)) {
685 PCIE_DBG("Unsupported value of setting "
686 "property %s\n", name);
687 ret = DDI_ENOTSUP;
688 goto set_prop_cleanup;
689 }
690 } else {
691 PCIE_DBG("Unsupported property: %s\n", name);
692 ret = DDI_ENOTSUP;
693 goto set_prop_cleanup;
694 }
695 }
696
697 mutex_enter(&ctrl_p->hc_mutex);
698
699 /* get the current slot state */
700 pcishpc_get_slot_state(slot_p);
701
702 // set each property
703 prop_pair = NULL;
704 while ((prop_pair = nvlist_next_nvpair(prop_list, prop_pair)) != NULL) {
705 name = nvpair_name(prop_pair);
706
707 if (strcmp(name, PCIEHPC_PROP_LED_ATTN) == 0) {
708 if (strcmp(value, PCIEHPC_PROP_VALUE_ON) == 0)
709 led_state = PCIE_HP_LED_ON;
710 else if (strcmp(value, PCIEHPC_PROP_VALUE_OFF) == 0)
711 led_state = PCIE_HP_LED_OFF;
712 else if (strcmp(value, PCIEHPC_PROP_VALUE_BLINK) == 0)
713 led_state = PCIE_HP_LED_BLINK;
714 else
715 continue;
716
717 (void) pcishpc_setled(slot_p, PCIE_HP_ATTN_LED,
718 led_state);
719 }
720 }
721 if (rval) {
722 if (get_udatamodel() == DATAMODEL_NATIVE) {
723 result.buf_size = 0;
724 if (copyout(&result, rval, sizeof (ddi_hp_property_t)))
725 ret = DDI_FAILURE;
726 }
727 #ifdef _SYSCALL32_IMPL
728 else {
729 result32.buf_size = 0;
730 if (copyout(&result32, rval,
731 sizeof (ddi_hp_property32_t)))
732 ret = DDI_FAILURE;
733 }
734 #endif
735 }
736
737 mutex_exit(&ctrl_p->hc_mutex);
738 set_prop_cleanup:
739 nvlist_free(prop_list);
740 return (ret);
741 }
742
743 /*
744 * pcishpc_hp_ops()
745 *
746 * Handle hotplug commands
747 *
748 * Note: This function is called by DDI HP framework at kernel context only
749 */
750 /* ARGSUSED */
751 int
pcishpc_hp_ops(dev_info_t * dip,char * cn_name,ddi_hp_op_t op,void * arg,void * result)752 pcishpc_hp_ops(dev_info_t *dip, char *cn_name, ddi_hp_op_t op,
753 void *arg, void *result)
754 {
755 pcie_hp_slot_t *slot_p = NULL;
756 pcie_hp_ctrl_t *ctrl_p;
757 int ret = DDI_SUCCESS, i;
758
759 PCIE_DBG("pcishpc_hp_ops: dip=%p cn_name=%s op=%x arg=%p\n",
760 dip, cn_name, op, arg);
761
762 if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL)
763 return (DDI_FAILURE);
764
765 for (i = 0; i < PCIE_HP_MAX_SLOTS && ctrl_p->hc_slots[i]; i++) {
766 if (strcmp(ctrl_p->hc_slots[i]->hs_info.cn_name, cn_name)
767 == 0) {
768 /* Match with a physical slot, found */
769 slot_p = ctrl_p->hc_slots[i];
770 break;
771 }
772 }
773 if (!slot_p) {
774 PCIE_DBG("pcishpc_hp_ops: Failed to find the slot under"
775 "dip %p with name: %s; op=%x arg=%p\n",
776 dip, cn_name, op, arg);
777 return (DDI_EINVAL);
778 }
779 switch (op) {
780 case DDI_HPOP_CN_GET_STATE:
781 {
782 mutex_enter(&ctrl_p->hc_mutex);
783
784 /* get the current slot state */
785 pcishpc_get_slot_state(slot_p);
786
787 *((ddi_hp_cn_state_t *)result) = slot_p->hs_info.cn_state;
788
789 mutex_exit(&ctrl_p->hc_mutex);
790 break;
791 }
792 case DDI_HPOP_CN_CHANGE_STATE:
793 {
794 ddi_hp_cn_state_t target_state = *(ddi_hp_cn_state_t *)arg;
795
796 mutex_enter(&slot_p->hs_ctrl->hc_mutex);
797
798 ret = pcishpc_change_slot_state(slot_p, target_state);
799 *((ddi_hp_cn_state_t *)result) = slot_p->hs_info.cn_state;
800
801 mutex_exit(&slot_p->hs_ctrl->hc_mutex);
802 break;
803 }
804 case DDI_HPOP_CN_PROBE:
805 ret = pcishpc_slot_probe(slot_p);
806
807 break;
808 case DDI_HPOP_CN_UNPROBE:
809 ret = pcishpc_slot_unprobe(slot_p);
810
811 break;
812 case DDI_HPOP_CN_GET_PROPERTY:
813 ret = pcishpc_slot_get_property(slot_p,
814 (ddi_hp_property_t *)arg, (ddi_hp_property_t *)result);
815 break;
816 case DDI_HPOP_CN_SET_PROPERTY:
817 ret = pcishpc_slot_set_property(slot_p,
818 (ddi_hp_property_t *)arg, (ddi_hp_property_t *)result);
819 break;
820 default:
821 ret = DDI_ENOTSUP;
822 break;
823 }
824
825 return (ret);
826 }
827
828 /*
829 * Local functions (called within this file)
830 */
831
832 /*
833 * pcishpc_create_controller()
834 *
835 * This function allocates and creates an SHPC controller state structure
836 * and adds it to the linked list of controllers.
837 */
838 static pcie_hp_ctrl_t *
pcishpc_create_controller(dev_info_t * dip)839 pcishpc_create_controller(dev_info_t *dip)
840 {
841 pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
842 pcie_hp_ctrl_t *ctrl_p;
843
844 PCIE_DBG("pcishpc: create controller for %s#%d\n",
845 ddi_driver_name(dip), ddi_get_instance(dip));
846
847 ctrl_p = kmem_zalloc(sizeof (pcie_hp_ctrl_t), KM_SLEEP);
848 ctrl_p->hc_dip = dip;
849
850 cv_init(&ctrl_p->hc_cmd_comp_cv, NULL, CV_DRIVER, NULL);
851
852 /* Init the shpc controller's mutex. */
853 mutex_init(&ctrl_p->hc_mutex, NULL, MUTEX_DRIVER, NULL);
854
855 /* HPC initialization is complete now */
856 ctrl_p->hc_flags = PCIE_HP_INITIALIZED_FLAG;
857 bus_p->bus_hp_curr_mode = PCIE_PCI_HP_MODE;
858
859 PCIE_SET_HP_CTRL(dip, ctrl_p);
860
861 PCIE_DBG("pcishpc_create_controller() success\n");
862
863 return (ctrl_p);
864 }
865
866
867 /*
868 * pcishpc_setup_controller()
869 *
870 * Get the number of HotPlug Slots, and the PCI device information
871 * for this HotPlug controller.
872 */
873 static int
pcishpc_setup_controller(pcie_hp_ctrl_t * ctrl_p)874 pcishpc_setup_controller(pcie_hp_ctrl_t *ctrl_p)
875 {
876 uint32_t config;
877 dev_info_t *ppdip;
878
879 config = pcishpc_read_reg(ctrl_p, PCI_HP_SLOT_CONFIGURATION_REG);
880
881 /* Get the number of HotPlug slots implemented */
882 ctrl_p->hc_num_slots_impl = ((config)&31);
883
884 /*
885 * Initilize the current bus speed and number of hotplug slots
886 * currently connected.
887 */
888 ctrl_p->hc_curr_bus_speed = -1;
889 ctrl_p->hc_num_slots_connected = 0;
890
891 /*
892 * Get the first PCI device Number used.
893 *
894 * PCI-X I/O boat workaround.
895 * The register doesn't set up the correct value.
896 */
897 ppdip = ddi_get_parent(ddi_get_parent(ctrl_p->hc_dip));
898 if ((ddi_prop_get_int(DDI_DEV_T_ANY, ppdip, DDI_PROP_DONTPASS,
899 "vendor-id", -1) == 0x108e) &&
900 (ddi_prop_get_int(DDI_DEV_T_ANY, ppdip, DDI_PROP_DONTPASS,
901 "device-id", -1) == 0x9010))
902 ctrl_p->hc_device_start = 4;
903 else
904 ctrl_p->hc_device_start = ((config>>8)&31);
905
906 /* Get the first Physical device number. */
907 ctrl_p->hc_phys_start = ((config>>16)&0x7ff);
908
909 /* Check if the device numbers increase or decrease. */
910 ctrl_p->hc_device_increases = ((config>>29)&0x1);
911
912 ctrl_p->hc_has_attn =
913 (config & PCI_HP_SLOT_CONFIG_ATTN_BUTTON) ? B_TRUE : B_FALSE;
914 ctrl_p->hc_has_mrl =
915 (config & PCI_HP_SLOT_CONFIG_MRL_SENSOR) ? B_TRUE : B_FALSE;
916
917 ctrl_p->hc_cmd_pending = B_FALSE;
918 ctrl_p->hc_arbiter_timeout = B_FALSE;
919
920 if (ctrl_p->hc_num_slots_impl > PCIE_HP_MAX_SLOTS) {
921 PCIE_DBG("pcishpc_setup_controller() too many SHPC "
922 "slots error\n");
923 return (DDI_FAILURE);
924 }
925
926 return (DDI_SUCCESS);
927 }
928
929
930 /*
931 * pcishpc_destroy_controller()
932 *
933 * This function deallocates all of the SHPC controller resources.
934 */
935 static int
pcishpc_destroy_controller(dev_info_t * dip)936 pcishpc_destroy_controller(dev_info_t *dip)
937 {
938 pcie_hp_ctrl_t *ctrl_p;
939 pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
940
941 PCIE_DBG("pcishpc_destroy_controller() called(dip=%p)\n", dip);
942
943 /* get the soft state structure for this dip */
944 if ((ctrl_p = PCIE_GET_HP_CTRL(dip)) == NULL) {
945 PCIE_DBG("pcishpc_destroy_controller() not found\n");
946 return (DDI_FAILURE);
947 }
948
949 /*
950 * Deallocate the slot state structures for this controller.
951 */
952 PCIE_SET_HP_CTRL(dip, NULL);
953 bus_p->bus_hp_curr_mode = PCIE_NONE_HP_MODE;
954
955 (void) pcishpc_destroy_slots(ctrl_p);
956 cv_destroy(&ctrl_p->hc_cmd_comp_cv);
957 mutex_destroy(&ctrl_p->hc_mutex);
958 kmem_free(ctrl_p, sizeof (pcie_hp_ctrl_t));
959
960 PCIE_DBG("pcishpc_destroy_controller() success\n");
961 return (DDI_SUCCESS);
962 }
963
964 /*
965 * pcishpc_create_slot()
966 *
967 * Allocate and add a new HotPlug slot state structure to the linked list.
968 */
969 static pcie_hp_slot_t *
pcishpc_create_slot(pcie_hp_ctrl_t * ctrl_p)970 pcishpc_create_slot(pcie_hp_ctrl_t *ctrl_p)
971 {
972 pcie_hp_slot_t *slot_p;
973
974 PCIE_DBG("pcishpc_create_slot() called(ctrl_p=%x)\n", ctrl_p);
975
976 /* Allocate a new slot structure. */
977 slot_p = kmem_zalloc(sizeof (pcie_hp_slot_t), KM_SLEEP);
978 slot_p->hs_ctrl = ctrl_p;
979
980 /* Assign an initial value */
981 slot_p->hs_info.cn_state = DDI_HP_CN_STATE_EMPTY;
982
983 PCIE_DBG("pcishpc_create_slot() success\n");
984 return (slot_p);
985 }
986
987 /*
988 * pcishpc_register_slot()
989 *
990 * Create and register a slot with the Solaris HotPlug framework.
991 */
992 static int
pcishpc_register_slot(pcie_hp_ctrl_t * ctrl_p,int slot)993 pcishpc_register_slot(pcie_hp_ctrl_t *ctrl_p, int slot)
994 {
995 dev_info_t *dip = ctrl_p->hc_dip;
996 pcie_hp_slot_t *slot_p;
997
998 slot_p = pcishpc_create_slot(ctrl_p);
999 ctrl_p->hc_slots[slot] = slot_p;
1000 slot_p->hs_num = slot;
1001
1002 /* Setup the PCI device # for this SHPC slot. */
1003 if (ctrl_p->hc_device_increases)
1004 slot_p->hs_device_num = ctrl_p->hc_device_start +
1005 slot_p->hs_num;
1006 else
1007 slot_p->hs_device_num = ctrl_p->hc_device_start -
1008 slot_p->hs_num;
1009
1010 /* Setup the DDI HP framework slot information. */
1011 slot_p->hs_info.cn_type = DDI_HP_CN_TYPE_PCI;
1012 slot_p->hs_info.cn_type_str = PCIE_PCI_HP_TYPE;
1013 slot_p->hs_info.cn_child = NULL;
1014
1015 slot_p->hs_minor = PCI_MINOR_NUM(
1016 ddi_get_instance(dip), slot_p->hs_device_num);
1017 slot_p->hs_condition = AP_COND_UNKNOWN;
1018
1019 /* setup thread for handling ATTN button events */
1020 if (ctrl_p->hc_has_attn) {
1021 PCIE_DBG("pcishpc_register_slot: "
1022 "setting up ATTN button event "
1023 "handler thread for slot %d\n", slot);
1024
1025 cv_init(&slot_p->hs_attn_btn_cv, NULL, CV_DRIVER, NULL);
1026 slot_p->hs_attn_btn_pending = B_FALSE;
1027 slot_p->hs_attn_btn_threadp = thread_create(NULL, 0,
1028 pcishpc_attn_btn_handler,
1029 (void *)slot_p, 0, &p0, TS_RUN, minclsyspri);
1030 slot_p->hs_attn_btn_thread_exit = B_FALSE;
1031 }
1032
1033 /* setup the slot name (used for ap-id) */
1034 pcishpc_set_slot_name(ctrl_p, slot);
1035
1036 pcishpc_get_slot_state(slot_p);
1037 if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_ENABLED)
1038 slot_p->hs_condition = AP_COND_OK;
1039
1040 /* register the slot with DDI HP framework */
1041 if (ndi_hp_register(dip, &slot_p->hs_info) != NDI_SUCCESS) {
1042 PCIE_DBG("pciehpc_register_slot() failed to register slot %d\n",
1043 slot_p->hs_phy_slot_num);
1044 return (DDI_FAILURE);
1045 }
1046
1047 pcie_hp_create_occupant_props(dip, makedevice(ddi_driver_major(dip),
1048 slot_p->hs_minor), slot_p->hs_device_num);
1049
1050 PCIE_DBG("pcishpc_register_slot() success for slot %d\n", slot);
1051
1052 return (DDI_SUCCESS);
1053 }
1054
1055 /*
1056 * pcishpc_destroy_slots()
1057 *
1058 * Free up all of the slot resources for this controller.
1059 */
1060 static int
pcishpc_destroy_slots(pcie_hp_ctrl_t * ctrl_p)1061 pcishpc_destroy_slots(pcie_hp_ctrl_t *ctrl_p)
1062 {
1063 dev_info_t *dip = ctrl_p->hc_dip;
1064 pcie_hp_slot_t *slot_p;
1065 int i;
1066
1067 PCIE_DBG("pcishpc_destroy_slots() called(ctrl_p=%p)\n", ctrl_p);
1068
1069 for (i = 0; i < PCIE_HP_MAX_SLOTS; i++) {
1070 if ((slot_p = ctrl_p->hc_slots[i]) == NULL)
1071 continue;
1072
1073 if (slot_p->hs_attn_btn_threadp != NULL) {
1074 mutex_enter(&ctrl_p->hc_mutex);
1075 slot_p->hs_attn_btn_thread_exit = B_TRUE;
1076 cv_signal(&slot_p->hs_attn_btn_cv);
1077 PCIE_DBG("pcishpc_destroy_slots: "
1078 "waiting for ATTN thread exit\n");
1079 cv_wait(&slot_p->hs_attn_btn_cv, &ctrl_p->hc_mutex);
1080 PCIE_DBG("pcishpc_destroy_slots: "
1081 "ATTN thread exit\n");
1082 cv_destroy(&slot_p->hs_attn_btn_cv);
1083 slot_p->hs_attn_btn_threadp = NULL;
1084 mutex_exit(&ctrl_p->hc_mutex);
1085 }
1086
1087 PCIE_DBG("pcishpc_destroy_slots() (shpc_p=%p)\n"
1088 "destroyed", slot_p);
1089
1090 pcie_hp_delete_occupant_props(dip,
1091 makedevice(ddi_driver_major(dip),
1092 slot_p->hs_minor));
1093
1094 /* unregister the slot with DDI HP framework */
1095 if (ndi_hp_unregister(dip, slot_p->hs_info.cn_name) !=
1096 NDI_SUCCESS) {
1097 PCIE_DBG("pcishpc_destroy_slots() "
1098 "failed to unregister slot %d\n",
1099 slot_p->hs_phy_slot_num);
1100 return (DDI_FAILURE);
1101 }
1102 kmem_free(slot_p->hs_info.cn_name,
1103 strlen(slot_p->hs_info.cn_name) + 1);
1104 kmem_free(slot_p, sizeof (pcie_hp_slot_t));
1105 }
1106
1107 return (DDI_SUCCESS);
1108 }
1109
1110 /*
1111 * pcishpc_enable_irqs()
1112 *
1113 * Enable/unmask the different IRQ's we support from the SHPC controller.
1114 */
1115 int
pcishpc_enable_irqs(pcie_hp_ctrl_t * ctrl_p)1116 pcishpc_enable_irqs(pcie_hp_ctrl_t *ctrl_p)
1117 {
1118 uint32_t reg;
1119 int slot;
1120
1121 reg = pcishpc_read_reg(ctrl_p, PCI_HP_CTRL_SERR_INT_REG);
1122
1123 /* Enable all interrupts. */
1124 reg &= ~PCI_HP_SERR_INT_MASK_ALL;
1125
1126 pcishpc_write_reg(ctrl_p, PCI_HP_CTRL_SERR_INT_REG, reg);
1127
1128 /* Unmask the interrupts for each slot. */
1129 for (slot = 0; slot < ctrl_p->hc_num_slots_impl; slot++) {
1130 reg = pcishpc_read_reg(ctrl_p, PCI_HP_LOGICAL_SLOT_REGS+slot);
1131 if ((reg & PCI_HP_SLOT_STATE_MASK) == PCI_HP_SLOT_ENABLED) {
1132 reg &= ~(PCI_HP_SLOT_MASK_ALL |
1133 PCI_HP_SLOT_MRL_SERR_MASK);
1134 ctrl_p->hc_num_slots_connected++;
1135 if (ctrl_p->hc_curr_bus_speed == -1)
1136 ctrl_p->hc_curr_bus_speed =
1137 pcishpc_read_reg(ctrl_p,
1138 PCI_HP_PROF_IF_SBCR_REG) &
1139 PCI_HP_SBCR_SPEED_MASK;
1140 } else {
1141 reg &= ~(PCI_HP_SLOT_MASK_ALL);
1142 }
1143
1144 /* Enable/Unmask all slot interrupts. */
1145 pcishpc_write_reg(ctrl_p, PCI_HP_LOGICAL_SLOT_REGS+slot, reg);
1146 }
1147
1148 PCIE_DBG("pcishpc_enable_irqs: ctrl_p 0x%p, "
1149 "current bus speed 0x%x, slots connected 0x%x\n", ctrl_p,
1150 ctrl_p->hc_curr_bus_speed, ctrl_p->hc_num_slots_connected);
1151
1152 return (DDI_SUCCESS);
1153 }
1154
1155
1156 /*
1157 * pcishpc_disable_irqs()
1158 *
1159 * Disable/Mask the different IRQ's we support from the SHPC controller.
1160 */
1161 int
pcishpc_disable_irqs(pcie_hp_ctrl_t * ctrl_p)1162 pcishpc_disable_irqs(pcie_hp_ctrl_t *ctrl_p)
1163 {
1164 uint32_t reg;
1165 int slot;
1166
1167 reg = pcishpc_read_reg(ctrl_p, PCI_HP_CTRL_SERR_INT_REG);
1168
1169 /* Mask all interrupts. */
1170 reg |= PCI_HP_SERR_INT_MASK_ALL;
1171
1172 pcishpc_write_reg(ctrl_p, PCI_HP_CTRL_SERR_INT_REG, reg);
1173
1174 /* Unmask the interrupts for each slot. */
1175 for (slot = 0; slot < ctrl_p->hc_num_slots_impl; slot++) {
1176 reg = pcishpc_read_reg(ctrl_p, PCI_HP_LOGICAL_SLOT_REGS+slot);
1177
1178 /* Disable/Mask all slot interrupts. */
1179 reg |= PCI_HP_SLOT_MASK_ALL;
1180
1181 pcishpc_write_reg(ctrl_p, PCI_HP_LOGICAL_SLOT_REGS+slot, reg);
1182 }
1183
1184 PCIE_DBG("pcishpc_disable_irqs: ctrl_p 0x%p, "
1185 "current bus speed 0x%x, slots connected 0x%x\n", ctrl_p,
1186 ctrl_p->hc_curr_bus_speed, ctrl_p->hc_num_slots_connected);
1187
1188 return (DDI_SUCCESS);
1189 }
1190
1191 /*
1192 * pcishpc_slot_poweron()
1193 *
1194 * Poweron/Enable the slot.
1195 *
1196 * Note: This function is called by DDI HP framework at kernel context only
1197 */
1198 /*ARGSUSED*/
1199 static int
pcishpc_slot_poweron(pcie_hp_slot_t * slot_p,ddi_hp_cn_state_t * result_state)1200 pcishpc_slot_poweron(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result_state)
1201 {
1202 uint32_t status;
1203
1204 PCIE_DBG("pcishpc_slot_poweron called()\n");
1205
1206 ASSERT(MUTEX_HELD(&slot_p->hs_ctrl->hc_mutex));
1207
1208 /* get the current slot state */
1209 pcishpc_get_slot_state(slot_p);
1210
1211 /* check if the slot is already in the 'enabled' state */
1212 if (slot_p->hs_info.cn_state >= DDI_HP_CN_STATE_POWERED) {
1213 /* slot is already in the 'enabled' state */
1214 PCIE_DBG("pcishpc_slot_poweron() slot %d already enabled\n",
1215 slot_p->hs_phy_slot_num);
1216
1217 *result_state = slot_p->hs_info.cn_state;
1218 return (DDI_SUCCESS);
1219 }
1220
1221 if (slot_p->hs_info.cn_state == DDI_HP_CN_STATE_EMPTY) {
1222 PCIE_DBG("pcishpc_slot_poweron() slot in empty state\n");
1223 goto cleanup;
1224 }
1225
1226 /* make sure the MRL sensor is closed */
1227 status = pcishpc_read_reg(slot_p->hs_ctrl,
1228 PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num);
1229
1230 if (status & PCI_HP_SLOT_MRL_STATE_MASK) {
1231 PCIE_DBG("pcishpc_slot_poweron() failed: MRL open\n");
1232 goto cleanup;
1233 }
1234
1235 /* Set the Power LED to blink */
1236 (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED, PCIE_HP_LED_BLINK);
1237
1238 /* Turn all other LEDS off */
1239 (void) pcishpc_setled(slot_p, PCIE_HP_FAULT_LED, PCIE_HP_LED_OFF);
1240 (void) pcishpc_setled(slot_p, PCIE_HP_ATTN_LED, PCIE_HP_LED_OFF);
1241 (void) pcishpc_setled(slot_p, PCIE_HP_ACTIVE_LED, PCIE_HP_LED_OFF);
1242
1243 /* Set the bus speed only if the bus segment is not running */
1244 if (pcishpc_set_bus_speed(slot_p) != DDI_SUCCESS) {
1245 PCIE_DBG("pcishpc_slot_poweron() setting speed failed\n");
1246 goto cleanup;
1247 }
1248
1249 slot_p->hs_ctrl->hc_num_slots_connected++;
1250
1251 PCIE_DBG("pcishpc_slot_poweron(): slot_p 0x%p, slot state 0x%x, "
1252 "current bus speed 0x%x, slots connected 0x%x\n", slot_p,
1253 slot_p->hs_info.cn_state, slot_p->hs_ctrl->hc_curr_bus_speed,
1254 slot_p->hs_ctrl->hc_num_slots_connected);
1255
1256 /* Mask or Unmask MRL Sensor SEER bit based on new slot state */
1257 if (slot_p->hs_ctrl->hc_has_mrl == B_TRUE) {
1258 uint32_t reg;
1259
1260 reg = pcishpc_read_reg(slot_p->hs_ctrl,
1261 PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num);
1262
1263 pcishpc_write_reg(slot_p->hs_ctrl,
1264 PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num,
1265 reg & ~PCI_HP_SLOT_MRL_SERR_MASK);
1266 }
1267
1268 /* Update the hardware slot state. */
1269 if (pcishpc_set_slot_state(slot_p,
1270 DDI_HP_CN_STATE_ENABLED) != DDI_SUCCESS) {
1271 PCIE_DBG("pcishpc_slot_poweron() failed\n");
1272
1273 pcishpc_get_slot_state(slot_p);
1274 goto cleanup;
1275 }
1276 /* Update the current state. It will be used in pcishpc_setled() */
1277 slot_p->hs_info.cn_state = DDI_HP_CN_STATE_ENABLED;
1278
1279 /* Turn the Power LED ON for a enabled slot. */
1280 (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED, PCIE_HP_LED_ON);
1281
1282 /* Turn all other LEDS off. */
1283 (void) pcishpc_setled(slot_p, PCIE_HP_FAULT_LED, PCIE_HP_LED_OFF);
1284 (void) pcishpc_setled(slot_p, PCIE_HP_ATTN_LED, PCIE_HP_LED_OFF);
1285 (void) pcishpc_setled(slot_p, PCIE_HP_ACTIVE_LED, PCIE_HP_LED_OFF);
1286
1287 /* delay after powerON to let the device initialize itself */
1288 delay(drv_usectohz(pcishpc_reset_delay));
1289
1290 PCIE_DBG("pcishpc_slot_poweron() success!\n");
1291
1292 /*
1293 * Want to show up as POWERED state for now. It will be updated to
1294 * ENABLED state when user explicitly enable the slot.
1295 */
1296 slot_p->hs_info.cn_state = DDI_HP_CN_STATE_POWERED;
1297
1298 /* get the current slot state */
1299 pcishpc_get_slot_state(slot_p);
1300 /*
1301 * It should be poweron'ed now. Have a check here in case any
1302 * hardware problems.
1303 */
1304 if (slot_p->hs_info.cn_state < DDI_HP_CN_STATE_POWERED) {
1305 PCIE_DBG("pcishpc_slot_poweron() failed after hardware"
1306 " registers all programmed.\n");
1307
1308 goto cleanup;
1309 }
1310
1311 *result_state = slot_p->hs_info.cn_state;
1312
1313 return (DDI_SUCCESS);
1314
1315 cleanup:
1316 (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED, PCIE_HP_LED_OFF);
1317 return (DDI_FAILURE);
1318 }
1319
1320 /*ARGSUSED*/
1321 static int
pcishpc_slot_poweroff(pcie_hp_slot_t * slot_p,ddi_hp_cn_state_t * result_state)1322 pcishpc_slot_poweroff(pcie_hp_slot_t *slot_p, ddi_hp_cn_state_t *result_state)
1323 {
1324 PCIE_DBG("pcishpc_slot_poweroff called()\n");
1325
1326 ASSERT(MUTEX_HELD(&slot_p->hs_ctrl->hc_mutex));
1327
1328 /* get the current slot state */
1329 pcishpc_get_slot_state(slot_p);
1330
1331 /* check if the slot is not in the "enabled" or "powered" state */
1332 if (slot_p->hs_info.cn_state < DDI_HP_CN_STATE_POWERED) {
1333 /* slot is in the 'disabled' state */
1334 PCIE_DBG("pcishpc_slot_poweroff(): "
1335 "slot %d already disabled\n", slot_p->hs_phy_slot_num);
1336
1337 *result_state = slot_p->hs_info.cn_state;
1338 return (DDI_SUCCESS);
1339 }
1340
1341 /* Set the Power LED to blink */
1342 (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED, PCIE_HP_LED_BLINK);
1343
1344 /* Turn all other LEDS off */
1345 (void) pcishpc_setled(slot_p, PCIE_HP_FAULT_LED, PCIE_HP_LED_OFF);
1346 (void) pcishpc_setled(slot_p, PCIE_HP_ATTN_LED, PCIE_HP_LED_OFF);
1347 (void) pcishpc_setled(slot_p, PCIE_HP_ACTIVE_LED, PCIE_HP_LED_OFF);
1348
1349 if (--slot_p->hs_ctrl->hc_num_slots_connected == 0)
1350 slot_p->hs_ctrl->hc_curr_bus_speed = -1;
1351
1352 PCIE_DBG("pcishpc_slot_poweroff(): slot_p 0x%p, slot state 0x%x, "
1353 "current bus speed 0x%x, slots connected 0x%x\n", slot_p,
1354 slot_p->hs_info.cn_state, slot_p->hs_ctrl->hc_curr_bus_speed,
1355 slot_p->hs_ctrl->hc_num_slots_connected);
1356
1357 /* Mask or Unmask MRL Sensor SEER bit based on new slot state */
1358 if (slot_p->hs_ctrl->hc_has_mrl == B_TRUE) {
1359 uint32_t reg;
1360
1361 reg = pcishpc_read_reg(slot_p->hs_ctrl,
1362 PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num);
1363
1364 pcishpc_write_reg(slot_p->hs_ctrl,
1365 PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num,
1366 reg | PCI_HP_SLOT_MRL_SERR_MASK);
1367 }
1368
1369 /* Update the hardware slot state. */
1370 if (pcishpc_set_slot_state(slot_p, DDI_HP_CN_STATE_PRESENT) !=
1371 DDI_SUCCESS) {
1372 PCIE_DBG("pcishpc_slot_poweroff() failed\n");
1373
1374 pcishpc_get_slot_state(slot_p);
1375 goto cleanup;
1376 }
1377
1378 /* Update the current state. It will be used in pcishpc_setled() */
1379 slot_p->hs_info.cn_state = DDI_HP_CN_STATE_PRESENT;
1380
1381 /* Turn the Power LED OFF for a disabled slot. */
1382 (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED, PCIE_HP_LED_OFF);
1383
1384 /* Turn all other LEDS off. */
1385 (void) pcishpc_setled(slot_p, PCIE_HP_FAULT_LED, PCIE_HP_LED_OFF);
1386 (void) pcishpc_setled(slot_p, PCIE_HP_ATTN_LED, PCIE_HP_LED_OFF);
1387 (void) pcishpc_setled(slot_p, PCIE_HP_ACTIVE_LED, PCIE_HP_LED_OFF);
1388
1389 /* delay after powerON to let the device initialize itself */
1390 delay(drv_usectohz(pcishpc_reset_delay));
1391
1392 pcishpc_get_slot_state(slot_p);
1393 /*
1394 * It should be poweroff'ed now. Have a check here in case any
1395 * hardware problems.
1396 */
1397 if (slot_p->hs_info.cn_state > DDI_HP_CN_STATE_PRESENT) {
1398 PCIE_DBG("pcishpc_slot_poweroff() failed after hardware"
1399 " registers all programmed.\n");
1400
1401 goto cleanup;
1402 }
1403
1404 PCIE_DBG("pcishpc_slot_poweroff() success!\n");
1405
1406 *result_state = slot_p->hs_info.cn_state;
1407 return (DDI_SUCCESS);
1408
1409 cleanup:
1410 (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED, PCIE_HP_LED_OFF);
1411 return (DDI_FAILURE);
1412 }
1413
1414 /*
1415 * pcishpc_slot_probe()
1416 *
1417 * Probe the slot.
1418 *
1419 * Note: This function is called by DDI HP framework at kernel context only
1420 */
1421 /*ARGSUSED*/
1422 static int
pcishpc_slot_probe(pcie_hp_slot_t * slot_p)1423 pcishpc_slot_probe(pcie_hp_slot_t *slot_p)
1424 {
1425 mutex_enter(&slot_p->hs_ctrl->hc_mutex);
1426
1427 PCIE_DBG("pcishpc_slot_probe called()\n");
1428
1429 /* get the current slot state */
1430 pcishpc_get_slot_state(slot_p);
1431
1432 /*
1433 * Probe a given PCI Hotplug Connection (CN).
1434 */
1435 if (pcie_hp_probe(slot_p) != DDI_SUCCESS) {
1436 (void) pcishpc_setled(slot_p, PCIE_HP_ATTN_LED,
1437 PCIE_HP_LED_BLINK);
1438
1439 PCIE_DBG("pcishpc_slot_probe() failed\n");
1440
1441 mutex_exit(&slot_p->hs_ctrl->hc_mutex);
1442 return (DDI_FAILURE);
1443 }
1444
1445 PCIE_DBG("pcishpc_slot_probe() success!\n");
1446
1447 /* get the current slot state */
1448 pcishpc_get_slot_state(slot_p);
1449
1450 mutex_exit(&slot_p->hs_ctrl->hc_mutex);
1451 return (DDI_SUCCESS);
1452 }
1453
1454 /*
1455 * pcishpc_slot_unprobe()
1456 *
1457 * Unprobe the slot.
1458 *
1459 * Note: This function is called by DDI HP framework at kernel context only
1460 */
1461 /*ARGSUSED*/
1462 static int
pcishpc_slot_unprobe(pcie_hp_slot_t * slot_p)1463 pcishpc_slot_unprobe(pcie_hp_slot_t *slot_p)
1464 {
1465 mutex_enter(&slot_p->hs_ctrl->hc_mutex);
1466
1467 PCIE_DBG("pcishpc_slot_unprobe called()\n");
1468
1469 /* get the current slot state */
1470 pcishpc_get_slot_state(slot_p);
1471
1472 /*
1473 * Unprobe a given PCI Hotplug Connection (CN).
1474 */
1475 if (pcie_hp_unprobe(slot_p) != DDI_SUCCESS) {
1476 (void) pcishpc_setled(slot_p, PCIE_HP_ATTN_LED,
1477 PCIE_HP_LED_BLINK);
1478
1479 PCIE_DBG("pcishpc_slot_unprobe() failed\n");
1480
1481 mutex_exit(&slot_p->hs_ctrl->hc_mutex);
1482 return (DDI_FAILURE);
1483 }
1484
1485 PCIE_DBG("pcishpc_slot_unprobe() success!\n");
1486
1487 /* get the current slot state */
1488 pcishpc_get_slot_state(slot_p);
1489
1490 mutex_exit(&slot_p->hs_ctrl->hc_mutex);
1491 return (DDI_SUCCESS);
1492 }
1493
1494 static int
pcishpc_upgrade_slot_state(pcie_hp_slot_t * slot_p,ddi_hp_cn_state_t target_state)1495 pcishpc_upgrade_slot_state(pcie_hp_slot_t *slot_p,
1496 ddi_hp_cn_state_t target_state)
1497 {
1498 ddi_hp_cn_state_t curr_state;
1499 int rv = DDI_SUCCESS;
1500
1501 if (target_state > DDI_HP_CN_STATE_ENABLED) {
1502 return (DDI_EINVAL);
1503 }
1504
1505 curr_state = slot_p->hs_info.cn_state;
1506 while ((curr_state < target_state) && (rv == DDI_SUCCESS)) {
1507
1508 switch (curr_state) {
1509 case DDI_HP_CN_STATE_EMPTY:
1510 /*
1511 * From EMPTY to PRESENT, just check the hardware
1512 * slot state.
1513 */
1514 pcishpc_get_slot_state(slot_p);
1515 curr_state = slot_p->hs_info.cn_state;
1516 if (curr_state < DDI_HP_CN_STATE_PRESENT)
1517 rv = DDI_FAILURE;
1518 break;
1519 case DDI_HP_CN_STATE_PRESENT:
1520 rv = pcishpc_slot_poweron(slot_p, &curr_state);
1521 break;
1522 case DDI_HP_CN_STATE_POWERED:
1523 curr_state = slot_p->hs_info.cn_state =
1524 DDI_HP_CN_STATE_ENABLED;
1525 break;
1526 default:
1527 /* should never reach here */
1528 ASSERT("unknown devinfo state");
1529 }
1530 }
1531
1532 return (rv);
1533 }
1534
1535 static int
pcishpc_downgrade_slot_state(pcie_hp_slot_t * slot_p,ddi_hp_cn_state_t target_state)1536 pcishpc_downgrade_slot_state(pcie_hp_slot_t *slot_p,
1537 ddi_hp_cn_state_t target_state)
1538 {
1539 ddi_hp_cn_state_t curr_state;
1540 int rv = DDI_SUCCESS;
1541
1542
1543 curr_state = slot_p->hs_info.cn_state;
1544 while ((curr_state > target_state) && (rv == DDI_SUCCESS)) {
1545
1546 switch (curr_state) {
1547 case DDI_HP_CN_STATE_PRESENT:
1548 /*
1549 * From PRESENT to EMPTY, just check hardware
1550 * slot state.
1551 */
1552 pcishpc_get_slot_state(slot_p);
1553 curr_state = slot_p->hs_info.cn_state;
1554 if (curr_state >= DDI_HP_CN_STATE_PRESENT)
1555 rv = DDI_FAILURE;
1556 break;
1557 case DDI_HP_CN_STATE_POWERED:
1558 rv = pcishpc_slot_poweroff(slot_p, &curr_state);
1559
1560 break;
1561 case DDI_HP_CN_STATE_ENABLED:
1562 curr_state = slot_p->hs_info.cn_state =
1563 DDI_HP_CN_STATE_POWERED;
1564
1565 break;
1566 default:
1567 /* should never reach here */
1568 ASSERT("unknown devinfo state");
1569 }
1570 }
1571
1572 return (rv);
1573 }
1574
1575 /* Change slot state to a target state */
1576 static int
pcishpc_change_slot_state(pcie_hp_slot_t * slot_p,ddi_hp_cn_state_t target_state)1577 pcishpc_change_slot_state(pcie_hp_slot_t *slot_p,
1578 ddi_hp_cn_state_t target_state)
1579 {
1580 ddi_hp_cn_state_t curr_state;
1581 int rv;
1582
1583 pcishpc_get_slot_state(slot_p);
1584 curr_state = slot_p->hs_info.cn_state;
1585
1586 if (curr_state == target_state) {
1587 return (DDI_SUCCESS);
1588 }
1589 if (curr_state < target_state) {
1590
1591 rv = pcishpc_upgrade_slot_state(slot_p, target_state);
1592 } else {
1593 rv = pcishpc_downgrade_slot_state(slot_p, target_state);
1594 }
1595
1596 return (rv);
1597 }
1598
1599 /*
1600 * pcishpc_issue_command()
1601 *
1602 * Sends a command to the SHPC controller.
1603 */
1604 static int
pcishpc_issue_command(pcie_hp_ctrl_t * ctrl_p,uint32_t cmd_code)1605 pcishpc_issue_command(pcie_hp_ctrl_t *ctrl_p, uint32_t cmd_code)
1606 {
1607 int retCode;
1608
1609 ASSERT(MUTEX_HELD(&ctrl_p->hc_mutex));
1610
1611 PCIE_DBG("pcishpc_issue_command() cmd_code=%02x\n", cmd_code);
1612
1613 ctrl_p->hc_cmd_pending = B_TRUE;
1614
1615 /* Write the command to the SHPC controller. */
1616 pcishpc_write_reg(ctrl_p, PCI_HP_COMMAND_STATUS_REG, cmd_code);
1617
1618 while (ctrl_p->hc_cmd_pending == B_TRUE)
1619 cv_wait(&ctrl_p->hc_cmd_comp_cv, &ctrl_p->hc_mutex);
1620
1621 /* Wait until the SHPC controller processes the command. */
1622 retCode = pcishpc_wait_busy(ctrl_p);
1623
1624 /* Make sure the command completed. */
1625 if (retCode == DDI_SUCCESS) {
1626 /* Did the command fail to generate the command complete IRQ? */
1627 if (ctrl_p->hc_cmd_pending != B_FALSE) {
1628 PCIE_DBG("pcishpc_issue_command() Failed on "
1629 "generate cmd complete IRQ\n");
1630 retCode = DDI_FAILURE;
1631 }
1632 }
1633
1634 if (retCode == DDI_FAILURE)
1635 PCIE_DBG("pcishpc_issue_command() Failed on cmd_code=%02x\n",
1636 cmd_code);
1637 else
1638 PCIE_DBG("pcishpc_issue_command() Success on "
1639 "cmd_code=%02x\n", cmd_code);
1640
1641 return (retCode);
1642 }
1643
1644 /*
1645 * pcishpc_wait_busy()
1646 *
1647 * Wait until the SHPC controller is not busy.
1648 */
1649 static int
pcishpc_wait_busy(pcie_hp_ctrl_t * ctrl_p)1650 pcishpc_wait_busy(pcie_hp_ctrl_t *ctrl_p)
1651 {
1652 uint32_t status;
1653
1654 /* Wait until SHPC controller is NOT busy */
1655 for (;;) {
1656 status = pcishpc_read_reg(ctrl_p, PCI_HP_COMMAND_STATUS_REG);
1657
1658 /* Is there an MRL Sensor error? */
1659 if ((status & PCI_HP_COMM_STS_ERR_MASK) ==
1660 PCI_HP_COMM_STS_ERR_MRL_OPEN) {
1661 PCIE_DBG("pcishpc_wait_busy() ERROR: "
1662 "MRL Sensor error\n");
1663 break;
1664 }
1665
1666 /* Is there an Invalid command error? */
1667 if ((status & PCI_HP_COMM_STS_ERR_MASK) ==
1668 PCI_HP_COMM_STS_ERR_INVALID_COMMAND) {
1669 PCIE_DBG("pcishpc_wait_busy() ERROR: Invalid "
1670 "command error\n");
1671 break;
1672 }
1673
1674 /* Is there an Invalid Speed/Mode error? */
1675 if ((status & PCI_HP_COMM_STS_ERR_MASK) ==
1676 PCI_HP_COMM_STS_ERR_INVALID_SPEED) {
1677 PCIE_DBG("pcishpc_wait_busy() ERROR: Invalid "
1678 "Speed/Mode error\n");
1679 break;
1680 }
1681
1682 /* Is the SHPC controller not BUSY? */
1683 if (!(status & PCI_HP_COMM_STS_CTRL_BUSY)) {
1684 /* Return Success. */
1685 return (DDI_SUCCESS);
1686 }
1687
1688 PCIE_DBG("pcishpc_wait_busy() SHPC controller busy. Waiting\n");
1689
1690 /* Wait before polling the status register again. */
1691 delay(drv_usectohz(PCIE_HP_CMD_WAIT_TIME));
1692 }
1693
1694 return (DDI_FAILURE);
1695 }
1696
1697 static void
pcishpc_attn_btn_handler(pcie_hp_slot_t * slot_p)1698 pcishpc_attn_btn_handler(pcie_hp_slot_t *slot_p)
1699 {
1700 pcie_hp_led_state_t hs_power_led_state;
1701 callb_cpr_t cprinfo;
1702
1703 PCIE_DBG("pcishpc_attn_btn_handler: thread started\n");
1704
1705 CALLB_CPR_INIT(&cprinfo, &slot_p->hs_ctrl->hc_mutex,
1706 callb_generic_cpr, "pcishpc_attn_btn_handler");
1707
1708 mutex_enter(&slot_p->hs_ctrl->hc_mutex);
1709
1710 /* wait for ATTN button event */
1711 cv_wait(&slot_p->hs_attn_btn_cv, &slot_p->hs_ctrl->hc_mutex);
1712
1713 while (slot_p->hs_attn_btn_thread_exit == B_FALSE) {
1714 if (slot_p->hs_attn_btn_pending == B_TRUE) {
1715 /* get the current state of power LED */
1716 hs_power_led_state = slot_p->hs_power_led_state;
1717
1718 /* Blink the Power LED while we wait for 5 seconds */
1719 (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED,
1720 PCIE_HP_LED_BLINK);
1721
1722 /* wait for 5 seconds before taking any action */
1723 if (cv_reltimedwait(&slot_p->hs_attn_btn_cv,
1724 &slot_p->hs_ctrl->hc_mutex,
1725 SEC_TO_TICK(5), TR_CLOCK_TICK) == -1) {
1726 /*
1727 * It is a time out;
1728 * make sure the ATTN pending flag is
1729 * still ON before sending the event
1730 * to DDI HP framework.
1731 */
1732 if (slot_p->hs_attn_btn_pending == B_TRUE) {
1733 int hint;
1734
1735 /* restore the power LED state */
1736 (void) pcishpc_setled(slot_p,
1737 PCIE_HP_POWER_LED,
1738 hs_power_led_state);
1739 /*
1740 * send the ATTN button event
1741 * to DDI HP framework
1742 */
1743 slot_p->hs_attn_btn_pending = B_FALSE;
1744
1745 pcishpc_get_slot_state(slot_p);
1746
1747 if (slot_p->hs_info.cn_state <=
1748 DDI_HP_CN_STATE_PRESENT) {
1749 /*
1750 * Insertion.
1751 */
1752 hint = SE_INCOMING_RES;
1753 } else {
1754 /*
1755 * Want to remove;
1756 */
1757 hint = SE_OUTGOING_RES;
1758 }
1759 pcie_hp_gen_sysevent_req(
1760 slot_p->hs_info.cn_name,
1761 hint,
1762 slot_p->hs_ctrl->hc_dip,
1763 KM_SLEEP);
1764
1765 continue;
1766 }
1767 }
1768
1769 /* restore the power LED state */
1770 (void) pcishpc_setled(slot_p, PCIE_HP_POWER_LED,
1771 hs_power_led_state);
1772 continue;
1773 }
1774
1775 /* wait for another ATTN button event */
1776 cv_wait(&slot_p->hs_attn_btn_cv, &slot_p->hs_ctrl->hc_mutex);
1777 }
1778
1779 PCIE_DBG("pcishpc_attn_btn_handler: thread exit\n");
1780 cv_signal(&slot_p->hs_attn_btn_cv);
1781 CALLB_CPR_EXIT(&cprinfo);
1782 thread_exit();
1783 }
1784
1785 /*
1786 * pcishpc_get_slot_state()
1787 *
1788 * Get the state of the slot.
1789 * The slot state should have been initialized before this function gets called.
1790 */
1791 static void
pcishpc_get_slot_state(pcie_hp_slot_t * slot_p)1792 pcishpc_get_slot_state(pcie_hp_slot_t *slot_p)
1793 {
1794 uint32_t reg;
1795 ddi_hp_cn_state_t curr_state = slot_p->hs_info.cn_state;
1796
1797 /* Read the logical slot register for this Slot. */
1798 reg = pcishpc_read_reg(slot_p->hs_ctrl,
1799 PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num);
1800
1801 /* Convert from the SHPC slot state to the HPC slot state. */
1802 slot_p->hs_info.cn_state = pcishpc_slot_shpc_to_hpc(reg);
1803 if (curr_state == DDI_HP_CN_STATE_POWERED &&
1804 slot_p->hs_info.cn_state > DDI_HP_CN_STATE_POWERED) {
1805 /*
1806 * Keep POWERED state if it is currently POWERED state because
1807 * this driver does not really implement enable/disable
1808 * slot operations. That is, when poweron, it actually enables
1809 * the slot also.
1810 * So, from hardware view, POWERED == ENABLED.
1811 * But, when user explicitly change to POWERED state, it should
1812 * be kept until user explicitly change to other states later.
1813 */
1814 slot_p->hs_info.cn_state = DDI_HP_CN_STATE_POWERED;
1815 }
1816
1817 /* Convert from the SHPC Power LED state to the HPC Power LED state. */
1818 slot_p->hs_power_led_state = pcishpc_led_shpc_to_hpc((reg>>2)&3);
1819
1820 /* Convert from the SHPC Attn LED state to the HPC Attn LED state. */
1821 slot_p->hs_attn_led_state = pcishpc_led_shpc_to_hpc((reg>>4)&3);
1822
1823 /* We don't have a fault LED so just default it to OFF. */
1824 slot_p->hs_fault_led_state = PCIE_HP_LED_OFF;
1825
1826 /* We don't have an active LED so just default it to OFF. */
1827 slot_p->hs_active_led_state = PCIE_HP_LED_OFF;
1828 }
1829
1830 /*
1831 * pcishpc_set_slot_state()
1832 *
1833 * Updates the slot's state and leds.
1834 */
1835 static int
pcishpc_set_slot_state(pcie_hp_slot_t * slot_p,ddi_hp_cn_state_t new_slot_state)1836 pcishpc_set_slot_state(pcie_hp_slot_t *slot_p,
1837 ddi_hp_cn_state_t new_slot_state)
1838 {
1839 uint32_t reg, cmd_code;
1840 ddi_hp_cn_state_t curr_state;
1841
1842 ASSERT(MUTEX_HELD(&slot_p->hs_ctrl->hc_mutex));
1843
1844 reg = pcishpc_read_reg(slot_p->hs_ctrl,
1845 PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num);
1846
1847 /* Default all states to unchanged. */
1848 cmd_code = ((1 + slot_p->hs_num) << 8);
1849
1850 /* Has the slot state changed? */
1851 curr_state = pcishpc_slot_shpc_to_hpc(reg);
1852 if (curr_state != new_slot_state) {
1853 PCIE_DBG("pcishpc_set_slot_state() Slot State changed");
1854
1855 /* Set the new slot state in the Slot operation command. */
1856 cmd_code |= pcishpc_slot_hpc_to_shpc(new_slot_state);
1857 }
1858
1859 /* Has the Power LED state changed? */
1860 if (slot_p->hs_power_led_state != pcishpc_led_shpc_to_hpc((reg>>2)&3)) {
1861 PCIE_DBG("pcishpc_set_slot_state() Power LED State changed\n");
1862
1863 /* Set the new power led state in the Slot operation command. */
1864 cmd_code |=
1865 (pcishpc_led_hpc_to_shpc(slot_p->hs_power_led_state) << 2);
1866 }
1867
1868 /* Has the Attn LED state changed? */
1869 if (slot_p->hs_attn_led_state != pcishpc_led_shpc_to_hpc((reg>>4)&3)) {
1870 PCIE_DBG("pcishpc_set_slot_state() Attn LED State changed\n");
1871
1872 /* Set the new attn led state in the Slot operation command. */
1873 cmd_code |=
1874 (pcishpc_led_hpc_to_shpc(slot_p->hs_attn_led_state) << 4);
1875 }
1876
1877 return (pcishpc_issue_command(slot_p->hs_ctrl, cmd_code));
1878 }
1879
1880 /*
1881 * setup slot name/slot-number info.
1882 */
1883 static void
pcishpc_set_slot_name(pcie_hp_ctrl_t * ctrl_p,int slot)1884 pcishpc_set_slot_name(pcie_hp_ctrl_t *ctrl_p, int slot)
1885 {
1886 pcie_hp_slot_t *slot_p = ctrl_p->hc_slots[slot];
1887 pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
1888 uchar_t *slotname_data;
1889 int *slotnum;
1890 uint_t count;
1891 int len;
1892 uchar_t *s;
1893 uint32_t bit_mask;
1894 int pci_id_cnt, pci_id_bit;
1895 int slots_before, found;
1896 int invalid_slotnum = 0;
1897
1898 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, ctrl_p->hc_dip,
1899 DDI_PROP_DONTPASS, "physical-slot#", &slotnum, &count) ==
1900 DDI_PROP_SUCCESS) {
1901 slot_p->hs_phy_slot_num = slotnum[0];
1902 ddi_prop_free(slotnum);
1903 } else {
1904 if (ctrl_p->hc_device_increases)
1905 slot_p->hs_phy_slot_num = ctrl_p->hc_phys_start + slot;
1906 else
1907 slot_p->hs_phy_slot_num = ctrl_p->hc_phys_start - slot;
1908
1909 if ((ndi_prop_update_int(DDI_DEV_T_NONE, ctrl_p->hc_dip,
1910 "physical-slot#", slot_p->hs_phy_slot_num)) != DDI_SUCCESS)
1911 PCIE_DBG("pcishpc_set_slot_name(): failed to "
1912 "create phyical-slot#%d\n",
1913 slot_p->hs_phy_slot_num);
1914 }
1915
1916 /* Platform may not have initialized it */
1917 if (!slot_p->hs_phy_slot_num) {
1918 slot_p->hs_phy_slot_num = pci_config_get8(bus_p->bus_cfg_hdl,
1919 PCI_BCNF_SECBUS);
1920 invalid_slotnum = 1;
1921 }
1922 slot_p->hs_info.cn_num = slot_p->hs_phy_slot_num;
1923 slot_p->hs_info.cn_num_dpd_on = DDI_HP_CN_NUM_NONE;
1924
1925 /*
1926 * construct the slot_name:
1927 * if "slot-names" property exists then use that name
1928 * else if valid slot number exists then it is "pci<slot-num>".
1929 * else it will be "pci<sec-bus-number>dev<dev-number>"
1930 */
1931 if (ddi_getlongprop(DDI_DEV_T_ANY, ctrl_p->hc_dip, DDI_PROP_DONTPASS,
1932 "slot-names", (caddr_t)&slotname_data, &len) == DDI_PROP_SUCCESS) {
1933 bit_mask = slotname_data[3] | (slotname_data[2] << 8) |
1934 (slotname_data[1] << 16) | (slotname_data[0] << 24);
1935
1936 pci_id_bit = 1;
1937 pci_id_cnt = slots_before = found = 0;
1938
1939 /*
1940 * Walk the bit mask until we find the bit that corresponds
1941 * to our slots device number. We count how many bits
1942 * we find before we find our slot's bit.
1943 */
1944 while (!found && (pci_id_cnt < 32)) {
1945 while (slot_p->hs_device_num != pci_id_cnt) {
1946
1947 /*
1948 * Find the next bit set.
1949 */
1950 while (!(bit_mask & pci_id_bit) &&
1951 (pci_id_cnt < 32)) {
1952 pci_id_bit = pci_id_bit << 1;
1953 pci_id_cnt++;
1954 }
1955
1956 if (slot_p->hs_device_num != pci_id_cnt)
1957 slots_before++;
1958 else
1959 found = 1;
1960 }
1961 }
1962
1963 if (pci_id_cnt < 32) {
1964
1965 /*
1966 * Set ptr to first string.
1967 */
1968 s = slotname_data + 4;
1969
1970 /*
1971 * Increment past all the strings for the slots
1972 * before ours.
1973 */
1974 while (slots_before) {
1975 while (*s != '\0')
1976 s++;
1977 s++;
1978 slots_before--;
1979 }
1980
1981 slot_p->hs_info.cn_name = i_ddi_strdup((char *)s,
1982 KM_SLEEP);
1983 kmem_free(slotname_data, len);
1984 return;
1985 }
1986
1987 /* slot-names entry not found */
1988 PCIE_DBG("pcishpc_set_slot_name(): "
1989 "No slot-names entry found for slot #%d\n",
1990 slot_p->hs_phy_slot_num);
1991 kmem_free(slotname_data, len);
1992 }
1993
1994 if (invalid_slotnum) {
1995 char tmp_name[256];
1996
1997 (void) snprintf(tmp_name, sizeof (tmp_name), "pci%d",
1998 slot_p->hs_device_num);
1999 slot_p->hs_info.cn_name = i_ddi_strdup(tmp_name, KM_SLEEP);
2000 } else {
2001 char tmp_name[256];
2002
2003 (void) snprintf(tmp_name, sizeof (tmp_name), "pci%d",
2004 slot_p->hs_phy_slot_num);
2005 slot_p->hs_info.cn_name = i_ddi_strdup(tmp_name, KM_SLEEP);
2006 }
2007 }
2008
2009 /*
2010 * pcishpc_set_bus_speed()
2011 *
2012 * Set the bus speed and mode.
2013 */
2014 static int
pcishpc_set_bus_speed(pcie_hp_slot_t * slot_p)2015 pcishpc_set_bus_speed(pcie_hp_slot_t *slot_p)
2016 {
2017 pcie_hp_ctrl_t *ctrl_p = slot_p->hs_ctrl;
2018 int curr_speed = ctrl_p->hc_curr_bus_speed;
2019 int speed = -1;
2020 int avail_slots;
2021 uint32_t status, slots_avail1_reg, slots_avail2_reg;
2022
2023 ASSERT(MUTEX_HELD(&slot_p->hs_ctrl->hc_mutex));
2024
2025 /* Make sure that the slot is in a correct state */
2026 status = pcishpc_read_reg(ctrl_p,
2027 PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num);
2028
2029 /* Return failure if the slot is empty */
2030 if ((status & PCI_HP_SLOT_CARD_EMPTY_MASK) ==
2031 PCI_HP_SLOT_CARD_EMPTY_MASK) {
2032 PCIE_DBG("pcishpc_set_bus_speed() failed: "
2033 "the slot is empty\n");
2034 return (DDI_FAILURE);
2035 }
2036
2037 /* Return failure if the slot is not in disabled state */
2038 if ((status & PCI_HP_SLOT_STATE_MASK) != PCI_HP_SLOT_DISABLED) {
2039 PCIE_DBG("pcishpc_set_bus_speed() failed: "
2040 "incorrect slot state\n");
2041 return (DDI_FAILURE);
2042 }
2043
2044 /* Set the "power-only" mode for the slot */
2045 if (pcishpc_issue_command(ctrl_p, ((1+slot_p->hs_num)<<8) |
2046 PCI_HP_SLOT_POWER_ONLY) != DDI_SUCCESS) {
2047 PCIE_DBG("pcishpc_set_bus_speed() failed to set "
2048 "the slot %d in the power-only mode\n", slot_p->hs_num);
2049 return (DDI_FAILURE);
2050 }
2051
2052 /* Wait for power good */
2053 delay(drv_usectohz(PCIE_HP_POWER_GOOD_WAIT_TIME));
2054
2055 /* Make sure that the slot is in "power-only" state */
2056 status = pcishpc_read_reg(ctrl_p,
2057 PCI_HP_LOGICAL_SLOT_REGS+slot_p->hs_num);
2058
2059 if ((status & PCI_HP_SLOT_STATE_MASK) != PCI_HP_SLOT_POWER_ONLY) {
2060 PCIE_DBG("pcishpc_set_bus_speed() "
2061 "power-only failed: incorrect slot state\n");
2062 return (DDI_FAILURE);
2063 }
2064
2065 slots_avail1_reg = pcishpc_read_reg(ctrl_p,
2066 PCI_HP_SLOTS_AVAIL_I_REG);
2067 slots_avail2_reg = pcishpc_read_reg(ctrl_p,
2068 PCI_HP_SLOTS_AVAIL_II_REG);
2069
2070 /*
2071 * Check if SHPC has available slots and select the highest
2072 * available bus speed for the slot.
2073 *
2074 * The bus speed codes are:
2075 * 100 - 133Mhz; <--+
2076 * 011 - 100Mhz; <--+ PCI-X
2077 * 010 - 66Mhz; <--+
2078 *
2079 * 001 - 66Mhz; <--+
2080 * 000 - 33Mhz <--+ Conv PCI
2081 */
2082 switch (status & PCI_HP_SLOT_PCIX_CAPABLE_MASK) {
2083 case PCI_HP_SLOT_133MHZ_PCIX_CAPABLE:
2084 avail_slots = (slots_avail1_reg >>
2085 PCI_HP_AVAIL_133MHZ_PCIX_SPEED_SHIFT) &
2086 PCI_HP_AVAIL_SPEED_MASK;
2087
2088 if (((curr_speed == -1) && avail_slots) ||
2089 (curr_speed == PCI_HP_SBCR_133MHZ_PCIX_SPEED)) {
2090 speed = PCI_HP_SBCR_133MHZ_PCIX_SPEED;
2091 break;
2092 }
2093 /* FALLTHROUGH */
2094 case PCI_HP_SLOT_100MHZ_PCIX_CAPABLE:
2095 avail_slots = (slots_avail1_reg >>
2096 PCI_HP_AVAIL_100MHZ_PCIX_SPEED_SHIFT) &
2097 PCI_HP_AVAIL_SPEED_MASK;
2098
2099 if (((curr_speed == -1) && avail_slots) ||
2100 (curr_speed == PCI_HP_SBCR_100MHZ_PCIX_SPEED)) {
2101 speed = PCI_HP_SBCR_100MHZ_PCIX_SPEED;
2102 break;
2103 }
2104 /* FALLTHROUGH */
2105 case PCI_HP_SLOT_66MHZ_PCIX_CAPABLE:
2106 avail_slots = (slots_avail1_reg >>
2107 PCI_HP_AVAIL_66MHZ_PCIX_SPEED_SHIFT) &
2108 PCI_HP_AVAIL_SPEED_MASK;
2109
2110 if (((curr_speed == -1) && avail_slots) ||
2111 (curr_speed == PCI_HP_SBCR_66MHZ_PCIX_SPEED)) {
2112 speed = PCI_HP_SBCR_66MHZ_PCIX_SPEED;
2113 break;
2114 }
2115 /* FALLTHROUGH */
2116 default:
2117 avail_slots = (slots_avail2_reg >>
2118 PCI_HP_AVAIL_66MHZ_CONV_SPEED_SHIFT) &
2119 PCI_HP_AVAIL_SPEED_MASK;
2120
2121 if ((status & PCI_HP_SLOT_66MHZ_CONV_CAPABLE) &&
2122 (((curr_speed == -1) && avail_slots) ||
2123 (curr_speed == PCI_HP_SBCR_66MHZ_CONV_SPEED))) {
2124 speed = PCI_HP_SBCR_66MHZ_CONV_SPEED;
2125 } else {
2126 avail_slots = (slots_avail1_reg >>
2127 PCI_HP_AVAIL_33MHZ_CONV_SPEED_SHIFT) &
2128 PCI_HP_AVAIL_SPEED_MASK;
2129
2130 if (((curr_speed == -1) && (avail_slots)) ||
2131 (curr_speed == PCI_HP_SBCR_33MHZ_CONV_SPEED)) {
2132 speed = PCI_HP_SBCR_33MHZ_CONV_SPEED;
2133 } else {
2134 PCIE_DBG("pcishpc_set_bus_speed() "
2135 " failed to set the bus speed, slot# %d\n",
2136 slot_p->hs_num);
2137 return (DDI_FAILURE);
2138 }
2139 }
2140 break;
2141 }
2142
2143 /*
2144 * If the bus segment is already running, check to see the card
2145 * in the slot can support the current bus speed.
2146 */
2147 if (curr_speed == speed) {
2148 /*
2149 * Check to see there is any slot available for the current
2150 * bus speed. Otherwise, we need fail the current slot connect
2151 * request.
2152 */
2153 return ((avail_slots <= ctrl_p->hc_num_slots_connected) ?
2154 DDI_FAILURE : DDI_SUCCESS);
2155 }
2156
2157 /* Set the bus speed */
2158 if (pcishpc_issue_command(ctrl_p, PCI_HP_COMM_STS_SET_SPEED |
2159 speed) == DDI_FAILURE) {
2160 PCIE_DBG("pcishpc_set_bus_speed() failed "
2161 "to set bus %d speed\n", slot_p->hs_num);
2162 return (DDI_FAILURE);
2163 }
2164
2165 /* Check the current bus speed */
2166 status = pcishpc_read_reg(ctrl_p, PCI_HP_PROF_IF_SBCR_REG) &
2167 PCI_HP_SBCR_SPEED_MASK;
2168 if ((status & PCI_HP_SBCR_SPEED_MASK) != speed) {
2169 PCIE_DBG("pcishpc_set_bus_speed() an incorrect "
2170 "bus speed, slot = 0x%x, speed = 0x%x\n",
2171 slot_p->hs_num, status & PCI_HP_SBCR_SPEED_MASK);
2172 return (DDI_FAILURE);
2173 }
2174
2175
2176 /* Save the current bus speed */
2177 ctrl_p->hc_curr_bus_speed = speed;
2178
2179 return (DDI_SUCCESS);
2180 }
2181
2182 /*
2183 * pcishpc_setled()
2184 *
2185 * Change the state of a slot's LED.
2186 */
2187 static int
pcishpc_setled(pcie_hp_slot_t * slot_p,pcie_hp_led_t led,pcie_hp_led_state_t state)2188 pcishpc_setled(pcie_hp_slot_t *slot_p, pcie_hp_led_t led,
2189 pcie_hp_led_state_t state)
2190 {
2191 ASSERT(MUTEX_HELD(&slot_p->hs_ctrl->hc_mutex));
2192
2193 switch (led) {
2194 case PCIE_HP_FAULT_LED:
2195 PCIE_DBG("pcishpc_setled() - PCIE_HP_FAULT_LED "
2196 "(set %s)\n", pcishpc_slot_textledstate(state));
2197 slot_p->hs_fault_led_state = state;
2198 break;
2199
2200 case PCIE_HP_POWER_LED:
2201 PCIE_DBG("pcishpc_setled() - PCIE_HP_POWER_LED "
2202 "(set %s)\n", pcishpc_slot_textledstate(state));
2203 slot_p->hs_power_led_state = state;
2204 break;
2205
2206 case PCIE_HP_ATTN_LED:
2207 PCIE_DBG("pcishpc_setled() - PCIE_HP_ATTN_LED "
2208 "(set %s)\n", pcishpc_slot_textledstate(state));
2209 slot_p->hs_attn_led_state = state;
2210 break;
2211
2212 case PCIE_HP_ACTIVE_LED:
2213 PCIE_DBG("pcishpc_setled() - PCIE_HP_ACTIVE_LED "
2214 "(set %s)\n", pcishpc_slot_textledstate(state));
2215 slot_p->hs_active_led_state = state;
2216 break;
2217 }
2218
2219 return (pcishpc_set_slot_state(slot_p, slot_p->hs_info.cn_state));
2220 }
2221
2222 /*
2223 * pcishpc_led_shpc_to_hpc()
2224 *
2225 * Convert from SHPC indicator status to HPC indicator status.
2226 */
2227 static int
pcishpc_led_shpc_to_hpc(int state)2228 pcishpc_led_shpc_to_hpc(int state)
2229 {
2230 switch (state) {
2231 case 1: /* SHPC On bits b01 */
2232 return (PCIE_HP_LED_ON);
2233 case 2: /* SHPC Blink bits b10 */
2234 return (PCIE_HP_LED_BLINK);
2235 case 3: /* SHPC Off bits b11 */
2236 return (PCIE_HP_LED_OFF);
2237 }
2238
2239 return (PCIE_HP_LED_OFF);
2240 }
2241
2242
2243 /*
2244 * pcishpc_led_hpc_to_shpc()
2245 *
2246 * Convert from HPC indicator status to SHPC indicator status.
2247 */
2248 static int
pcishpc_led_hpc_to_shpc(int state)2249 pcishpc_led_hpc_to_shpc(int state)
2250 {
2251 switch (state) {
2252 case PCIE_HP_LED_ON:
2253 return (1); /* SHPC On bits b01 */
2254 case PCIE_HP_LED_BLINK:
2255 return (2); /* SHPC Blink bits b10 */
2256 case PCIE_HP_LED_OFF:
2257 return (3); /* SHPC Off bits b11 */
2258 }
2259
2260 return (3); /* SHPC Off bits b11 */
2261 }
2262
2263 /*
2264 * pcishpc_slot_shpc_to_hpc()
2265 *
2266 * Convert from SHPC slot state to HPC slot state.
2267 * The argument shpc_state is expected to be read from the slot register.
2268 */
2269 static int
pcishpc_slot_shpc_to_hpc(int shpc_state)2270 pcishpc_slot_shpc_to_hpc(int shpc_state)
2271 {
2272 if ((shpc_state & PCI_HP_SLOT_CARD_EMPTY_MASK) ==
2273 PCI_HP_SLOT_CARD_EMPTY_MASK)
2274 return (DDI_HP_CN_STATE_EMPTY);
2275
2276 switch (shpc_state & PCI_HP_SLOT_STATE_MASK) {
2277 case PCI_HP_SLOT_POWER_ONLY: /* SHPC Powered Only */
2278 return (DDI_HP_CN_STATE_POWERED);
2279
2280 case PCI_HP_SLOT_ENABLED: /* SHPC Enabled */
2281 return (DDI_HP_CN_STATE_ENABLED);
2282
2283 case PCI_HP_SLOT_DISABLED: /* SHPC Disabled */
2284 default : /* SHPC Reserved */
2285 return (DDI_HP_CN_STATE_PRESENT);
2286 }
2287 }
2288
2289 /*
2290 * pcishpc_slot_hpc_to_shpc()
2291 *
2292 * Convert from HPC slot state to SHPC slot state.
2293 */
2294 static int
pcishpc_slot_hpc_to_shpc(int state)2295 pcishpc_slot_hpc_to_shpc(int state)
2296 {
2297 switch (state) {
2298 case DDI_HP_CN_STATE_EMPTY:
2299 return (0);
2300
2301 case DDI_HP_CN_STATE_POWERED:
2302 return (PCI_HP_SLOT_POWER_ONLY);
2303
2304 case DDI_HP_CN_STATE_ENABLED:
2305 return (PCI_HP_SLOT_ENABLED);
2306
2307 default:
2308 return (PCI_HP_SLOT_DISABLED);
2309 }
2310 }
2311
2312 /*
2313 * pcishpc_slot_textslotstate()
2314 *
2315 * Convert the request into a text message.
2316 */
2317 static char *
pcishpc_slot_textslotstate(ddi_hp_cn_state_t state)2318 pcishpc_slot_textslotstate(ddi_hp_cn_state_t state)
2319 {
2320 /* Convert an HPC slot state into a textual string. */
2321 if (state == DDI_HP_CN_STATE_EMPTY)
2322 return ("HPC_SLOT_EMPTY");
2323 else if (state == DDI_HP_CN_STATE_ENABLED)
2324 return ("HPC_SLOT_ENABLED");
2325 else if (state == DDI_HP_CN_STATE_POWERED)
2326 return ("HPC_SLOT_POWERED_ONLY");
2327 else
2328 return ("HPC_SLOT_DISABLED");
2329 }
2330
2331
2332 /*
2333 * pcishpc_slot_textledstate()
2334 *
2335 * Convert the led state into a text message.
2336 */
2337 static char *
pcishpc_slot_textledstate(pcie_hp_led_state_t state)2338 pcishpc_slot_textledstate(pcie_hp_led_state_t state)
2339 {
2340 /* Convert an HPC led state into a textual string. */
2341 switch (state) {
2342 case PCIE_HP_LED_OFF:
2343 return ("off");
2344
2345 case PCIE_HP_LED_ON:
2346 return ("on");
2347
2348 case PCIE_HP_LED_BLINK:
2349 return ("blink");
2350 }
2351 return ("unknown");
2352 }
2353
2354
2355 /*
2356 * pcishpc_read_reg()
2357 *
2358 * Read from a SHPC controller register.
2359 */
2360 static uint32_t
pcishpc_read_reg(pcie_hp_ctrl_t * ctrl_p,int reg)2361 pcishpc_read_reg(pcie_hp_ctrl_t *ctrl_p, int reg)
2362 {
2363 pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
2364
2365 /* Setup the SHPC dword select register. */
2366 pci_config_put8(bus_p->bus_cfg_hdl,
2367 bus_p->bus_pci_hp_off + PCI_HP_DWORD_SELECT_OFF, (uint8_t)reg);
2368
2369 /* Read back the SHPC dword select register and verify. */
2370 if (pci_config_get8(bus_p->bus_cfg_hdl, bus_p->bus_pci_hp_off +
2371 PCI_HP_DWORD_SELECT_OFF) != (uint8_t)reg) {
2372 PCIE_DBG("pcishpc_read_reg() - Failed writing DWORD "
2373 "select reg\n");
2374 return (0xFFFFFFFF);
2375 }
2376
2377 /* Read from the SHPC dword data register. */
2378 return (pci_config_get32(bus_p->bus_cfg_hdl,
2379 bus_p->bus_pci_hp_off + PCI_HP_DWORD_DATA_OFF));
2380 }
2381
2382
2383 /*
2384 * pcishpc_write_reg()
2385 *
2386 * Write to a SHPC controller register.
2387 */
2388 static void
pcishpc_write_reg(pcie_hp_ctrl_t * ctrl_p,int reg,uint32_t data)2389 pcishpc_write_reg(pcie_hp_ctrl_t *ctrl_p, int reg, uint32_t data)
2390 {
2391 pcie_bus_t *bus_p = PCIE_DIP2BUS(ctrl_p->hc_dip);
2392
2393 /* Setup the SHPC dword select register. */
2394 pci_config_put8(bus_p->bus_cfg_hdl,
2395 bus_p->bus_pci_hp_off + PCI_HP_DWORD_SELECT_OFF, (uint8_t)reg);
2396
2397 /* Read back the SHPC dword select register and verify. */
2398 if (pci_config_get8(bus_p->bus_cfg_hdl, bus_p->bus_pci_hp_off +
2399 PCI_HP_DWORD_SELECT_OFF) != (uint8_t)reg) {
2400 PCIE_DBG("pcishpc_write_reg() - Failed writing "
2401 "DWORD select reg\n");
2402 return;
2403 }
2404
2405 /* Write to the SHPC dword data register. */
2406 pci_config_put32(bus_p->bus_cfg_hdl,
2407 bus_p->bus_pci_hp_off + PCI_HP_DWORD_DATA_OFF, data);
2408
2409 /*
2410 * Issue a read of the VendorID/DeviceID just to force the previous
2411 * write to complete. This is probably not necessary, but it does
2412 * help enforce ordering if there is an issue.
2413 */
2414 (void) pci_config_get16(bus_p->bus_cfg_hdl, PCI_CONF_VENID);
2415 }
2416
2417
2418 #ifdef DEBUG
2419 /*
2420 * pcishpc_dump_regs()
2421 *
2422 * Dumps all of the SHPC controller registers.
2423 */
2424 static void
pcishpc_dump_regs(pcie_hp_ctrl_t * ctrl_p)2425 pcishpc_dump_regs(pcie_hp_ctrl_t *ctrl_p)
2426 {
2427 int slot, numSlots;
2428 uint32_t reg;
2429 char *state;
2430
2431 if (!pcie_debug_flags)
2432 return;
2433
2434 PCIE_DBG("pcishpc_dump_regs() called:\n");
2435 PCIE_DBG("==========================================================");
2436
2437 PCIE_DBG("SHPC Base Offset "
2438 ": 0x%08x\n", pcishpc_read_reg(ctrl_p, PCI_HP_BASE_OFFSET_REG));
2439
2440 reg = pcishpc_read_reg(ctrl_p, PCI_HP_SLOTS_AVAIL_I_REG);
2441
2442 PCIE_DBG("Number of PCIX slots avail (33 Mhz) : %d\n",
2443 (reg & 31));
2444
2445 PCIE_DBG("Number of PCIX slots avail (66 Mhz) : %d\n",
2446 ((reg>>8) & 31));
2447
2448 PCIE_DBG("Number of PCIX slots avail (100 Mhz) : %d\n",
2449 ((reg>>16) & 31));
2450
2451 PCIE_DBG("Number of PCIX slots avail (133 Mhz) : %d\n",
2452 ((reg>>24) & 31));
2453
2454 reg = pcishpc_read_reg(ctrl_p, PCI_HP_SLOTS_AVAIL_II_REG);
2455
2456 PCIE_DBG("Number of conventional PCI slots (66 Mhz) : %d\n",
2457 (reg & 31));
2458
2459 reg = pcishpc_read_reg(ctrl_p, PCI_HP_SLOT_CONFIGURATION_REG);
2460
2461 numSlots = (reg & 31);
2462
2463 PCIE_DBG("Number of Slots connected to this port : %d\n",
2464 numSlots);
2465
2466 PCIE_DBG("PCI Device # for First HotPlug Slot : %d\n",
2467 ((reg>>8) & 31));
2468
2469 PCIE_DBG("Physical Slot # for First PCI Device # : %d\n",
2470 ((reg>>16) & 0x7ff));
2471
2472 PCIE_DBG("Physical Slot Number Up/Down : %d\n",
2473 ((reg>>29) & 0x1));
2474
2475 PCIE_DBG("MRL Sensor Implemented : %s\n",
2476 (reg & PCI_HP_SLOT_CONFIG_MRL_SENSOR) ? "Yes" : "No");
2477
2478 PCIE_DBG("Attention Button Implemented : %s\n",
2479 (reg & PCI_HP_SLOT_CONFIG_ATTN_BUTTON) ? "Yes" : "No");
2480
2481 reg = pcishpc_read_reg(ctrl_p, PCI_HP_PROF_IF_SBCR_REG);
2482
2483 switch (reg & 7) {
2484 case 0:
2485 state = "33Mhz Conventional PCI";
2486 break;
2487 case 1:
2488 state = "66Mhz Conventional PCI";
2489 break;
2490 case 2:
2491 state = "66Mhz PCI-X";
2492 break;
2493 case 3:
2494 state = "100Mhz PCI-X";
2495 break;
2496 case 4:
2497 state = "133Mhz PCI-X";
2498 break;
2499 default:
2500 state = "Reserved (Error)";
2501 break;
2502 }
2503
2504 PCIE_DBG("Current Port Operation Mode : %s\n", state);
2505
2506 PCIE_DBG("SHPC Interrupt Message Number : %d\n",
2507 ((reg>>16) &31));
2508
2509 PCIE_DBG("SHPC Programming Interface : %d\n",
2510 ((reg>>24) & 0xff));
2511
2512 reg = pcishpc_read_reg(ctrl_p, PCI_HP_COMMAND_STATUS_REG);
2513
2514 PCIE_DBG("SHPC Command Code : %d\n",
2515 (reg & 0xff));
2516
2517 PCIE_DBG("SHPC Target Slot : %d\n",
2518 ((reg>>8) & 31));
2519
2520 PCIE_DBG("SHPC Controller Busy : %s\n",
2521 ((reg>>16) & 1) ? "Yes" : "No");
2522
2523 PCIE_DBG("SHPC Controller Err: MRL Sensor : %s\n",
2524 ((reg>>17) & 1) ? "Yes" : "No");
2525
2526 PCIE_DBG("SHPC Controller Err: Invalid Command : %s\n",
2527 ((reg>>18) & 1) ? "Yes" : "No");
2528
2529 PCIE_DBG("SHPC Controller Err: Invalid Speed/Mode : %s\n",
2530 ((reg>>19) & 1) ? "Yes" : "No");
2531
2532 reg = pcishpc_read_reg(ctrl_p, PCI_HP_IRQ_LOCATOR_REG);
2533
2534 PCIE_DBG("Command Completion Interrupt Pending : %s\n",
2535 (reg & PCI_HP_IRQ_CMD_COMPLETE) ? "Yes" : "No");
2536
2537 for (slot = 0; slot < numSlots; slot++) {
2538 PCIE_DBG("Slot %d Interrupt Pending : %s\n", slot+1,
2539 (reg & (PCI_HP_IRQ_SLOT_N_PENDING<<slot)) ? "Yes" : "No");
2540 }
2541
2542 reg = pcishpc_read_reg(ctrl_p, PCI_HP_SERR_LOCATOR_REG);
2543
2544 PCIE_DBG("Arbiter SERR Pending : %s\n",
2545 (reg & PCI_HP_IRQ_SERR_ARBITER_PENDING) ? "Yes" : "No");
2546
2547 for (slot = 0; slot < numSlots; slot++) {
2548 PCIE_DBG("Slot %d SERR Pending : %s\n",
2549 slot+1, (reg &
2550 (PCI_HP_IRQ_SERR_SLOT_N_PENDING<<slot)) ? "Yes" : "No");
2551 }
2552
2553 reg = pcishpc_read_reg(ctrl_p, PCI_HP_CTRL_SERR_INT_REG);
2554
2555 PCIE_DBG("Global Interrupt Mask : %s\n",
2556 (reg & PCI_HP_SERR_INT_GLOBAL_IRQ_MASK) ? "Yes" : "No");
2557
2558 PCIE_DBG("Global SERR Mask : %s\n",
2559 (reg & PCI_HP_SERR_INT_GLOBAL_SERR_MASK) ? "Yes" : "No");
2560
2561 PCIE_DBG("Command Completion Interrupt Mask : %s\n",
2562 (reg & PCI_HP_SERR_INT_CMD_COMPLETE_MASK) ? "Yes" : "No");
2563
2564 PCIE_DBG("Arbiter SERR Mask : %s\n",
2565 (reg & PCI_HP_SERR_INT_ARBITER_SERR_MASK) ? "Yes" : "No");
2566
2567 PCIE_DBG("Command Completion Detected : %s\n",
2568 (reg & PCI_HP_SERR_INT_CMD_COMPLETE_IRQ) ? "Yes" : "No");
2569
2570 PCIE_DBG("Arbiter Timeout Detected : %s\n",
2571 (reg & PCI_HP_SERR_INT_ARBITER_IRQ) ? "Yes" : "No");
2572
2573 for (slot = 0; slot < numSlots; slot++) {
2574 PCIE_DBG("Logical Slot %d Registers:\n", slot+1);
2575 PCIE_DBG("------------------------------------\n");
2576
2577 reg = pcishpc_read_reg(ctrl_p, PCI_HP_LOGICAL_SLOT_REGS+slot);
2578
2579 PCIE_DBG("Slot %d state : %s\n", slot+1,
2580 pcishpc_slot_textslotstate(pcishpc_slot_shpc_to_hpc(reg)));
2581
2582 PCIE_DBG("Slot %d Power Indicator State : %s\n", slot+1,
2583 pcishpc_slot_textledstate(pcishpc_led_shpc_to_hpc(
2584 (reg>>2) &3)));
2585
2586 PCIE_DBG("Slot %d Attention Indicator State : %s\n", slot+1,
2587 pcishpc_slot_textledstate(pcishpc_led_shpc_to_hpc(
2588 (reg>>4)&3)));
2589
2590 PCIE_DBG("Slot %d Power Fault : %s\n", slot+1,
2591 ((reg>>6)&1) ? "Fault Detected" : "No Fault");
2592 PCIE_DBG("Slot %d Attention Button : %s\n", slot+1,
2593 ((reg>>7)&1) ? "Depressed" : "Not Depressed");
2594 PCIE_DBG("Slot %d MRL Sensor : %s\n", slot+1,
2595 ((reg>>8)&1) ? "Not Closed" : "Closed");
2596 PCIE_DBG("Slot %d 66mhz Capable : %s\n", slot+1,
2597 ((reg>>9)&1) ? "66mhz" : "33mgz");
2598
2599 switch ((reg>>10)&3) {
2600 case 0:
2601 state = "Card Present 7.5W";
2602 break;
2603 case 1:
2604 state = "Card Present 15W";
2605 break;
2606 case 2:
2607 state = "Card Present 25W";
2608 break;
2609 case 3:
2610 state = "Slot Empty";
2611 break;
2612 }
2613
2614 PCIE_DBG("Slot %d PRSNT1#/PRSNT2# : %s\n", slot+1,
2615 state);
2616
2617 switch ((reg>>12)&3) {
2618 case 0:
2619 state = "Non PCI-X";
2620 break;
2621 case 1:
2622 state = "66mhz PCI-X";
2623 break;
2624 case 2:
2625 state = "Reserved";
2626 break;
2627 case 3:
2628 state = "133mhz PCI-X";
2629 break;
2630 }
2631
2632 PCIE_DBG("Slot %d Card Presence Change Detected : %s\n",
2633 slot+1, (reg & PCI_HP_SLOT_PRESENCE_DETECTED) ? "Yes" :
2634 "No");
2635 PCIE_DBG("Slot %d Isolated Power Fault Detected : %s\n",
2636 slot+1, (reg & PCI_HP_SLOT_ISO_PWR_DETECTED) ? "Yes" :
2637 "No");
2638 PCIE_DBG("Slot %d Attention Button Press Detected : %s\n",
2639 slot+1, (reg & PCI_HP_SLOT_ATTN_DETECTED) ? "Yes" : "No");
2640 PCIE_DBG("Slot %d MRL Sensor Change Detected : %s\n",
2641 slot+1, (reg & PCI_HP_SLOT_MRL_DETECTED) ? "Yes" : "No");
2642 PCIE_DBG("Slot %d Connected Power Fault Detected : %s\n",
2643 slot+1, (reg & PCI_HP_SLOT_POWER_DETECTED) ? "Yes" : "No");
2644
2645 PCIE_DBG("Slot %d Card Presence IRQ Masked : %s\n",
2646 slot+1, (reg & PCI_HP_SLOT_PRESENCE_MASK) ? "Yes" : "No");
2647 PCIE_DBG("Slot %d Isolated Power Fault IRQ Masked : %s\n",
2648 slot+1, (reg & PCI_HP_SLOT_ISO_PWR_MASK) ? "Yes" : "No");
2649 PCIE_DBG("Slot %d Attention Button IRQ Masked : %s\n",
2650 slot+1, (reg & PCI_HP_SLOT_ATTN_MASK) ? "Yes" : "No");
2651 PCIE_DBG("Slot %d MRL Sensor IRQ Masked : %s\n",
2652 slot+1, (reg & PCI_HP_SLOT_MRL_MASK) ? "Yes" : "No");
2653 PCIE_DBG("Slot %d Connected Power Fault IRQ Masked : %s\n",
2654 slot+1, (reg & PCI_HP_SLOT_POWER_MASK) ? "Yes" : "No");
2655 PCIE_DBG("Slot %d MRL Sensor SERR Masked : %s\n",
2656 slot+1, (reg & PCI_HP_SLOT_MRL_SERR_MASK) ? "Yes" : "No");
2657 PCIE_DBG("Slot %d Connected Power Fault SERR Masked : %s\n",
2658 slot+1, (reg & PCI_HP_SLOT_POWER_SERR_MASK) ? "Yes" : "No");
2659 }
2660 }
2661 #endif /* DEBUG */
2662