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 0 : } 72 : 73 0 : return SPDK_VMD_LED_STATE_UNKNOWN; 74 0 : } 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 0 : } 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 0 : } 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 0 : }