1 /*- 2 * BSD LICENSE 3 * 4 * Copyright (c) Intel Corporation. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * * Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * * Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * * Neither the name of Intel Corporation nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #include "spdk/stdinc.h" 35 #include "spdk/likely.h" 36 #include "spdk/log.h" 37 #include "vmd.h" 38 39 struct vmd_led_indicator_config { 40 uint8_t attention_indicator : 2; 41 uint8_t power_indicator : 2; 42 uint8_t reserved : 4; 43 }; 44 45 /* 46 * VMD LED Attn Power LED Amber 47 * State Indicator Indicator 48 * Control Control 49 * ------------------------------------------------ 50 * Off 11b 11b Off 51 * Ident 11b 01b Blink 4Hz 52 * Fault 01b 11b On 53 * Rebuild 01b 01b Blink 1Hz 54 */ 55 static const struct vmd_led_indicator_config g_led_config[] = { 56 [SPDK_VMD_LED_STATE_OFF] = { .attention_indicator = 3, .power_indicator = 3 }, 57 [SPDK_VMD_LED_STATE_IDENTIFY] = { .attention_indicator = 3, .power_indicator = 1 }, 58 [SPDK_VMD_LED_STATE_FAULT] = { .attention_indicator = 1, .power_indicator = 3 }, 59 [SPDK_VMD_LED_STATE_REBUILD] = { .attention_indicator = 1, .power_indicator = 1 }, 60 }; 61 62 static void 63 vmd_led_set_indicator_control(struct vmd_pci_device *vmd_device, enum spdk_vmd_led_state state) 64 { 65 const struct vmd_led_indicator_config *config; 66 union express_slot_control_register slot_control; 67 68 assert(state >= SPDK_VMD_LED_STATE_OFF && state <= SPDK_VMD_LED_STATE_REBUILD); 69 config = &g_led_config[state]; 70 71 slot_control = vmd_device->pcie_cap->slot_control; 72 slot_control.bit_field.attention_indicator_control = config->attention_indicator; 73 slot_control.bit_field.power_indicator_control = config->power_indicator; 74 75 /* 76 * Due to the fact that writes to the PCI config space are posted writes, we need to issue 77 * a read to the register we've just written to ensure it reached its destination. 78 * TODO: wrap all register writes with a function taking care of that. 79 */ 80 vmd_device->pcie_cap->slot_control = slot_control; 81 vmd_device->cached_slot_control = vmd_device->pcie_cap->slot_control; 82 } 83 84 static unsigned int 85 vmd_led_get_state(struct vmd_pci_device *vmd_device) 86 { 87 const struct vmd_led_indicator_config *config; 88 union express_slot_control_register slot_control; 89 unsigned int state; 90 91 slot_control = vmd_device->cached_slot_control; 92 for (state = SPDK_VMD_LED_STATE_OFF; state <= SPDK_VMD_LED_STATE_REBUILD; ++state) { 93 config = &g_led_config[state]; 94 95 if (slot_control.bit_field.attention_indicator_control == config->attention_indicator && 96 slot_control.bit_field.power_indicator_control == config->power_indicator) { 97 return state; 98 } 99 } 100 101 return SPDK_VMD_LED_STATE_UNKNOWN; 102 } 103 104 /* 105 * The identifying device under VMD is located in the global list of VMD controllers. If the BDF 106 * identifies an endpoint, then the LED is attached to the endpoint's parent. If the BDF identifies 107 * a type 1 header, then this device has the corresponding LED. This may arise when a user wants to 108 * identify a given empty slot under VMD. 109 */ 110 static struct vmd_pci_device * 111 vmd_get_led_device(const struct spdk_pci_device *pci_device) 112 { 113 struct vmd_pci_device *vmd_device; 114 115 assert(strcmp(spdk_pci_device_get_type(pci_device), "vmd") == 0); 116 117 vmd_device = vmd_find_device(&pci_device->addr); 118 if (spdk_unlikely(vmd_device == NULL)) { 119 return NULL; 120 } 121 122 if (vmd_device->header_type == PCI_HEADER_TYPE_NORMAL) { 123 if (spdk_unlikely(vmd_device->parent == NULL)) { 124 return NULL; 125 } 126 127 return vmd_device->parent->self; 128 } 129 130 return vmd_device; 131 } 132 133 int 134 spdk_vmd_set_led_state(struct spdk_pci_device *pci_device, enum spdk_vmd_led_state state) 135 { 136 struct vmd_pci_device *vmd_device; 137 138 if (state < SPDK_VMD_LED_STATE_OFF || state > SPDK_VMD_LED_STATE_REBUILD) { 139 SPDK_ERRLOG("Invalid LED state\n"); 140 return -EINVAL; 141 } 142 143 vmd_device = vmd_get_led_device(pci_device); 144 if (spdk_unlikely(vmd_device == NULL)) { 145 SPDK_ERRLOG("The PCI device is not behind the VMD\n"); 146 return -ENODEV; 147 } 148 149 vmd_led_set_indicator_control(vmd_device, state); 150 return 0; 151 } 152 153 int 154 spdk_vmd_get_led_state(struct spdk_pci_device *pci_device, enum spdk_vmd_led_state *state) 155 { 156 struct vmd_pci_device *vmd_device; 157 158 vmd_device = vmd_get_led_device(pci_device); 159 if (spdk_unlikely(vmd_device == NULL)) { 160 SPDK_ERRLOG("The PCI device is not behind the VMD\n"); 161 return -ENODEV; 162 } 163 164 *state = (enum spdk_vmd_led_state)vmd_led_get_state(vmd_device); 165 return 0; 166 } 167