Branch data Line data Source code
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 : 0 : 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 [ # # ]: 0 : assert(state >= SPDK_VMD_LED_STATE_OFF && state <= SPDK_VMD_LED_STATE_REBUILD); 41 : 0 : config = &g_led_config[state]; 42 : : 43 : 0 : slot_control = vmd_device->pcie_cap->slot_control; 44 : 0 : slot_control.bit_field.attention_indicator_control = config->attention_indicator; 45 : 0 : 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 : 0 : vmd_device->pcie_cap->slot_control = slot_control; 53 : 0 : vmd_device->cached_slot_control = vmd_device->pcie_cap->slot_control; 54 : 0 : } 55 : : 56 : : static unsigned int 57 : 0 : 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 : 0 : slot_control = vmd_device->cached_slot_control; 64 [ # # ]: 0 : for (state = SPDK_VMD_LED_STATE_OFF; state <= SPDK_VMD_LED_STATE_REBUILD; ++state) { 65 : 0 : config = &g_led_config[state]; 66 : : 67 [ # # ]: 0 : if (slot_control.bit_field.attention_indicator_control == config->attention_indicator && 68 [ # # ]: 0 : slot_control.bit_field.power_indicator_control == config->power_indicator) { 69 : 0 : return state; 70 : : } 71 : : } 72 : : 73 : 0 : 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 : 0 : vmd_get_led_device(const struct spdk_pci_device *pci_device) 84 : : { 85 : : struct vmd_pci_device *vmd_device; 86 : : 87 [ # # # # ]: 0 : assert(strcmp(spdk_pci_device_get_type(pci_device), "vmd") == 0); 88 : : 89 : 0 : vmd_device = vmd_find_device(&pci_device->addr); 90 [ # # ]: 0 : if (spdk_unlikely(vmd_device == NULL)) { 91 : 0 : return NULL; 92 : : } 93 : : 94 [ # # ]: 0 : if (vmd_device->header_type == PCI_HEADER_TYPE_NORMAL) { 95 [ # # ]: 0 : if (spdk_unlikely(vmd_device->parent == NULL)) { 96 : 0 : return NULL; 97 : : } 98 : : 99 : 0 : return vmd_device->parent->self; 100 : : } 101 : : 102 : 0 : return vmd_device; 103 : : } 104 : : 105 : : int 106 : 0 : 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 [ # # ]: 0 : if (state < SPDK_VMD_LED_STATE_OFF || state > SPDK_VMD_LED_STATE_REBUILD) { 111 : 0 : SPDK_ERRLOG("Invalid LED state\n"); 112 : 0 : return -EINVAL; 113 : : } 114 : : 115 : 0 : vmd_device = vmd_get_led_device(pci_device); 116 [ # # ]: 0 : if (spdk_unlikely(vmd_device == NULL)) { 117 : 0 : SPDK_ERRLOG("The PCI device is not behind the VMD\n"); 118 : 0 : return -ENODEV; 119 : : } 120 : : 121 : 0 : vmd_led_set_indicator_control(vmd_device, state); 122 : 0 : return 0; 123 : : } 124 : : 125 : : int 126 : 0 : 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 : 0 : vmd_device = vmd_get_led_device(pci_device); 131 [ # # ]: 0 : if (spdk_unlikely(vmd_device == NULL)) { 132 : 0 : SPDK_ERRLOG("The PCI device is not behind the VMD\n"); 133 : 0 : return -ENODEV; 134 : : } 135 : : 136 : 0 : *state = (enum spdk_vmd_led_state)vmd_led_get_state(vmd_device); 137 : 0 : return 0; 138 : : }