1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright (C) 2019 Intel Corporation. 3 * All rights reserved. 4 */ 5 6 #include "spdk/stdinc.h" 7 #include "spdk/likely.h" 8 #include "spdk/log.h" 9 #include "vmd_internal.h" 10 11 struct vmd_led_indicator_config { 12 uint8_t attention_indicator : 2; 13 uint8_t power_indicator : 2; 14 uint8_t reserved : 4; 15 }; 16 17 /* 18 * VMD LED Attn Power LED Amber 19 * State Indicator Indicator 20 * Control Control 21 * ------------------------------------------------ 22 * Off 11b 11b Off 23 * Ident 11b 01b Blink 4Hz 24 * Fault 01b 11b On 25 * Rebuild 01b 01b Blink 1Hz 26 */ 27 static const struct vmd_led_indicator_config g_led_config[] = { 28 [SPDK_VMD_LED_STATE_OFF] = { .attention_indicator = 3, .power_indicator = 3 }, 29 [SPDK_VMD_LED_STATE_IDENTIFY] = { .attention_indicator = 3, .power_indicator = 1 }, 30 [SPDK_VMD_LED_STATE_FAULT] = { .attention_indicator = 1, .power_indicator = 3 }, 31 [SPDK_VMD_LED_STATE_REBUILD] = { .attention_indicator = 1, .power_indicator = 1 }, 32 }; 33 34 static void 35 vmd_led_set_indicator_control(struct vmd_pci_device *vmd_device, enum spdk_vmd_led_state state) 36 { 37 const struct vmd_led_indicator_config *config; 38 union express_slot_control_register slot_control; 39 40 assert(state >= SPDK_VMD_LED_STATE_OFF && state <= SPDK_VMD_LED_STATE_REBUILD); 41 config = &g_led_config[state]; 42 43 slot_control = vmd_device->pcie_cap->slot_control; 44 slot_control.bit_field.attention_indicator_control = config->attention_indicator; 45 slot_control.bit_field.power_indicator_control = config->power_indicator; 46 47 /* 48 * Due to the fact that writes to the PCI config space are posted writes, we need to issue 49 * a read to the register we've just written to ensure it reached its destination. 50 * TODO: wrap all register writes with a function taking care of that. 51 */ 52 vmd_device->pcie_cap->slot_control = slot_control; 53 vmd_device->cached_slot_control = vmd_device->pcie_cap->slot_control; 54 } 55 56 static unsigned int 57 vmd_led_get_state(struct vmd_pci_device *vmd_device) 58 { 59 const struct vmd_led_indicator_config *config; 60 union express_slot_control_register slot_control; 61 unsigned int state; 62 63 slot_control = vmd_device->cached_slot_control; 64 for (state = SPDK_VMD_LED_STATE_OFF; state <= SPDK_VMD_LED_STATE_REBUILD; ++state) { 65 config = &g_led_config[state]; 66 67 if (slot_control.bit_field.attention_indicator_control == config->attention_indicator && 68 slot_control.bit_field.power_indicator_control == config->power_indicator) { 69 return state; 70 } 71 } 72 73 return SPDK_VMD_LED_STATE_UNKNOWN; 74 } 75 76 /* 77 * The identifying device under VMD is located in the global list of VMD controllers. If the BDF 78 * identifies an endpoint, then the LED is attached to the endpoint's parent. If the BDF identifies 79 * a type 1 header, then this device has the corresponding LED. This may arise when a user wants to 80 * identify a given empty slot under VMD. 81 */ 82 static struct vmd_pci_device * 83 vmd_get_led_device(const struct spdk_pci_device *pci_device) 84 { 85 struct vmd_pci_device *vmd_device; 86 87 assert(strcmp(spdk_pci_device_get_type(pci_device), "vmd") == 0); 88 89 vmd_device = vmd_find_device(&pci_device->addr); 90 if (spdk_unlikely(vmd_device == NULL)) { 91 return NULL; 92 } 93 94 if (vmd_device->header_type == PCI_HEADER_TYPE_NORMAL) { 95 if (spdk_unlikely(vmd_device->parent == NULL)) { 96 return NULL; 97 } 98 99 return vmd_device->parent->self; 100 } 101 102 return vmd_device; 103 } 104 105 int 106 spdk_vmd_set_led_state(struct spdk_pci_device *pci_device, enum spdk_vmd_led_state state) 107 { 108 struct vmd_pci_device *vmd_device; 109 110 if (state < SPDK_VMD_LED_STATE_OFF || state > SPDK_VMD_LED_STATE_REBUILD) { 111 SPDK_ERRLOG("Invalid LED state\n"); 112 return -EINVAL; 113 } 114 115 vmd_device = vmd_get_led_device(pci_device); 116 if (spdk_unlikely(vmd_device == NULL)) { 117 SPDK_ERRLOG("The PCI device is not behind the VMD\n"); 118 return -ENODEV; 119 } 120 121 vmd_led_set_indicator_control(vmd_device, state); 122 return 0; 123 } 124 125 int 126 spdk_vmd_get_led_state(struct spdk_pci_device *pci_device, enum spdk_vmd_led_state *state) 127 { 128 struct vmd_pci_device *vmd_device; 129 130 vmd_device = vmd_get_led_device(pci_device); 131 if (spdk_unlikely(vmd_device == NULL)) { 132 SPDK_ERRLOG("The PCI device is not behind the VMD\n"); 133 return -ENODEV; 134 } 135 136 *state = (enum spdk_vmd_led_state)vmd_led_get_state(vmd_device); 137 return 0; 138 } 139