diff options
Diffstat (limited to 'drivers/char/ipmi')
-rw-r--r-- | drivers/char/ipmi/ipmi_si_intf.c | 117 |
1 files changed, 105 insertions, 12 deletions
diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 60f2f968b689..4fb36d4142fb 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c | |||
@@ -110,6 +110,21 @@ enum si_type { | |||
110 | SI_KCS, SI_SMIC, SI_BT | 110 | SI_KCS, SI_SMIC, SI_BT |
111 | }; | 111 | }; |
112 | 112 | ||
113 | struct ipmi_device_id { | ||
114 | unsigned char device_id; | ||
115 | unsigned char device_revision; | ||
116 | unsigned char firmware_revision_1; | ||
117 | unsigned char firmware_revision_2; | ||
118 | unsigned char ipmi_version; | ||
119 | unsigned char additional_device_support; | ||
120 | unsigned char manufacturer_id[3]; | ||
121 | unsigned char product_id[2]; | ||
122 | unsigned char aux_firmware_revision[4]; | ||
123 | } __attribute__((packed)); | ||
124 | |||
125 | #define ipmi_version_major(v) ((v)->ipmi_version & 0xf) | ||
126 | #define ipmi_version_minor(v) ((v)->ipmi_version >> 4) | ||
127 | |||
113 | struct smi_info | 128 | struct smi_info |
114 | { | 129 | { |
115 | ipmi_smi_t intf; | 130 | ipmi_smi_t intf; |
@@ -132,12 +147,24 @@ struct smi_info | |||
132 | void (*irq_cleanup)(struct smi_info *info); | 147 | void (*irq_cleanup)(struct smi_info *info); |
133 | unsigned int io_size; | 148 | unsigned int io_size; |
134 | 149 | ||
150 | /* Per-OEM handler, called from handle_flags(). | ||
151 | Returns 1 when handle_flags() needs to be re-run | ||
152 | or 0 indicating it set si_state itself. | ||
153 | */ | ||
154 | int (*oem_data_avail_handler)(struct smi_info *smi_info); | ||
155 | |||
135 | /* Flags from the last GET_MSG_FLAGS command, used when an ATTN | 156 | /* Flags from the last GET_MSG_FLAGS command, used when an ATTN |
136 | is set to hold the flags until we are done handling everything | 157 | is set to hold the flags until we are done handling everything |
137 | from the flags. */ | 158 | from the flags. */ |
138 | #define RECEIVE_MSG_AVAIL 0x01 | 159 | #define RECEIVE_MSG_AVAIL 0x01 |
139 | #define EVENT_MSG_BUFFER_FULL 0x02 | 160 | #define EVENT_MSG_BUFFER_FULL 0x02 |
140 | #define WDT_PRE_TIMEOUT_INT 0x08 | 161 | #define WDT_PRE_TIMEOUT_INT 0x08 |
162 | #define OEM0_DATA_AVAIL 0x20 | ||
163 | #define OEM1_DATA_AVAIL 0x40 | ||
164 | #define OEM2_DATA_AVAIL 0x80 | ||
165 | #define OEM_DATA_AVAIL (OEM0_DATA_AVAIL | \ | ||
166 | OEM1_DATA_AVAIL | \ | ||
167 | OEM2_DATA_AVAIL) | ||
141 | unsigned char msg_flags; | 168 | unsigned char msg_flags; |
142 | 169 | ||
143 | /* If set to true, this will request events the next time the | 170 | /* If set to true, this will request events the next time the |
@@ -176,11 +203,7 @@ struct smi_info | |||
176 | interrupts. */ | 203 | interrupts. */ |
177 | int interrupt_disabled; | 204 | int interrupt_disabled; |
178 | 205 | ||
179 | unsigned char ipmi_si_dev_rev; | 206 | struct ipmi_device_id device_id; |
180 | unsigned char ipmi_si_fw_rev_major; | ||
181 | unsigned char ipmi_si_fw_rev_minor; | ||
182 | unsigned char ipmi_version_major; | ||
183 | unsigned char ipmi_version_minor; | ||
184 | 207 | ||
185 | /* Slave address, could be reported from DMI. */ | 208 | /* Slave address, could be reported from DMI. */ |
186 | unsigned char slave_addr; | 209 | unsigned char slave_addr; |
@@ -323,6 +346,7 @@ static inline void enable_si_irq(struct smi_info *smi_info) | |||
323 | 346 | ||
324 | static void handle_flags(struct smi_info *smi_info) | 347 | static void handle_flags(struct smi_info *smi_info) |
325 | { | 348 | { |
349 | retry: | ||
326 | if (smi_info->msg_flags & WDT_PRE_TIMEOUT_INT) { | 350 | if (smi_info->msg_flags & WDT_PRE_TIMEOUT_INT) { |
327 | /* Watchdog pre-timeout */ | 351 | /* Watchdog pre-timeout */ |
328 | spin_lock(&smi_info->count_lock); | 352 | spin_lock(&smi_info->count_lock); |
@@ -372,6 +396,10 @@ static void handle_flags(struct smi_info *smi_info) | |||
372 | smi_info->curr_msg->data, | 396 | smi_info->curr_msg->data, |
373 | smi_info->curr_msg->data_size); | 397 | smi_info->curr_msg->data_size); |
374 | smi_info->si_state = SI_GETTING_EVENTS; | 398 | smi_info->si_state = SI_GETTING_EVENTS; |
399 | } else if (smi_info->msg_flags & OEM_DATA_AVAIL) { | ||
400 | if (smi_info->oem_data_avail_handler) | ||
401 | if (smi_info->oem_data_avail_handler(smi_info)) | ||
402 | goto retry; | ||
375 | } else { | 403 | } else { |
376 | smi_info->si_state = SI_NORMAL; | 404 | smi_info->si_state = SI_NORMAL; |
377 | } | 405 | } |
@@ -1927,11 +1955,8 @@ static int try_get_dev_id(struct smi_info *smi_info) | |||
1927 | } | 1955 | } |
1928 | 1956 | ||
1929 | /* Record info from the get device id, in case we need it. */ | 1957 | /* Record info from the get device id, in case we need it. */ |
1930 | smi_info->ipmi_si_dev_rev = resp[4] & 0xf; | 1958 | memcpy(&smi_info->device_id, &resp[3], |
1931 | smi_info->ipmi_si_fw_rev_major = resp[5] & 0x7f; | 1959 | min_t(unsigned long, resp_len-3, sizeof(smi_info->device_id))); |
1932 | smi_info->ipmi_si_fw_rev_minor = resp[6]; | ||
1933 | smi_info->ipmi_version_major = resp[7] & 0xf; | ||
1934 | smi_info->ipmi_version_minor = resp[7] >> 4; | ||
1935 | 1960 | ||
1936 | out: | 1961 | out: |
1937 | kfree(resp); | 1962 | kfree(resp); |
@@ -1992,6 +2017,72 @@ static int stat_file_read_proc(char *page, char **start, off_t off, | |||
1992 | return (out - ((char *) page)); | 2017 | return (out - ((char *) page)); |
1993 | } | 2018 | } |
1994 | 2019 | ||
2020 | /* | ||
2021 | * oem_data_avail_to_receive_msg_avail | ||
2022 | * @info - smi_info structure with msg_flags set | ||
2023 | * | ||
2024 | * Converts flags from OEM_DATA_AVAIL to RECEIVE_MSG_AVAIL | ||
2025 | * Returns 1 indicating need to re-run handle_flags(). | ||
2026 | */ | ||
2027 | static int oem_data_avail_to_receive_msg_avail(struct smi_info *smi_info) | ||
2028 | { | ||
2029 | smi_info->msg_flags = (smi_info->msg_flags & ~OEM_DATA_AVAIL) | | ||
2030 | RECEIVE_MSG_AVAIL; | ||
2031 | return 1; | ||
2032 | } | ||
2033 | |||
2034 | /* | ||
2035 | * setup_dell_poweredge_oem_data_handler | ||
2036 | * @info - smi_info.device_id must be populated | ||
2037 | * | ||
2038 | * Systems that match, but have firmware version < 1.40 may assert | ||
2039 | * OEM0_DATA_AVAIL on their own, without being told via Set Flags that | ||
2040 | * it's safe to do so. Such systems will de-assert OEM1_DATA_AVAIL | ||
2041 | * upon receipt of IPMI_GET_MSG_CMD, so we should treat these flags | ||
2042 | * as RECEIVE_MSG_AVAIL instead. | ||
2043 | * | ||
2044 | * As Dell has no plans to release IPMI 1.5 firmware that *ever* | ||
2045 | * assert the OEM[012] bits, and if it did, the driver would have to | ||
2046 | * change to handle that properly, we don't actually check for the | ||
2047 | * firmware version. | ||
2048 | * Device ID = 0x20 BMC on PowerEdge 8G servers | ||
2049 | * Device Revision = 0x80 | ||
2050 | * Firmware Revision1 = 0x01 BMC version 1.40 | ||
2051 | * Firmware Revision2 = 0x40 BCD encoded | ||
2052 | * IPMI Version = 0x51 IPMI 1.5 | ||
2053 | * Manufacturer ID = A2 02 00 Dell IANA | ||
2054 | * | ||
2055 | */ | ||
2056 | #define DELL_POWEREDGE_8G_BMC_DEVICE_ID 0x20 | ||
2057 | #define DELL_POWEREDGE_8G_BMC_DEVICE_REV 0x80 | ||
2058 | #define DELL_POWEREDGE_8G_BMC_IPMI_VERSION 0x51 | ||
2059 | #define DELL_IANA_MFR_ID {0xA2, 0x02, 0x00} | ||
2060 | static void setup_dell_poweredge_oem_data_handler(struct smi_info *smi_info) | ||
2061 | { | ||
2062 | struct ipmi_device_id *id = &smi_info->device_id; | ||
2063 | const char mfr[3]=DELL_IANA_MFR_ID; | ||
2064 | if (!memcmp(mfr, id->manufacturer_id, sizeof(mfr)) && | ||
2065 | id->device_id == DELL_POWEREDGE_8G_BMC_DEVICE_ID && | ||
2066 | id->device_revision == DELL_POWEREDGE_8G_BMC_DEVICE_REV && | ||
2067 | id->ipmi_version == DELL_POWEREDGE_8G_BMC_IPMI_VERSION) { | ||
2068 | smi_info->oem_data_avail_handler = | ||
2069 | oem_data_avail_to_receive_msg_avail; | ||
2070 | } | ||
2071 | } | ||
2072 | |||
2073 | /* | ||
2074 | * setup_oem_data_handler | ||
2075 | * @info - smi_info.device_id must be filled in already | ||
2076 | * | ||
2077 | * Fills in smi_info.device_id.oem_data_available_handler | ||
2078 | * when we know what function to use there. | ||
2079 | */ | ||
2080 | |||
2081 | static void setup_oem_data_handler(struct smi_info *smi_info) | ||
2082 | { | ||
2083 | setup_dell_poweredge_oem_data_handler(smi_info); | ||
2084 | } | ||
2085 | |||
1995 | /* Returns 0 if initialized, or negative on an error. */ | 2086 | /* Returns 0 if initialized, or negative on an error. */ |
1996 | static int init_one_smi(int intf_num, struct smi_info **smi) | 2087 | static int init_one_smi(int intf_num, struct smi_info **smi) |
1997 | { | 2088 | { |
@@ -2090,6 +2181,8 @@ static int init_one_smi(int intf_num, struct smi_info **smi) | |||
2090 | if (rv) | 2181 | if (rv) |
2091 | goto out_err; | 2182 | goto out_err; |
2092 | 2183 | ||
2184 | setup_oem_data_handler(new_smi); | ||
2185 | |||
2093 | /* Try to claim any interrupts. */ | 2186 | /* Try to claim any interrupts. */ |
2094 | new_smi->irq_setup(new_smi); | 2187 | new_smi->irq_setup(new_smi); |
2095 | 2188 | ||
@@ -2123,8 +2216,8 @@ static int init_one_smi(int intf_num, struct smi_info **smi) | |||
2123 | 2216 | ||
2124 | rv = ipmi_register_smi(&handlers, | 2217 | rv = ipmi_register_smi(&handlers, |
2125 | new_smi, | 2218 | new_smi, |
2126 | new_smi->ipmi_version_major, | 2219 | ipmi_version_major(&new_smi->device_id), |
2127 | new_smi->ipmi_version_minor, | 2220 | ipmi_version_minor(&new_smi->device_id), |
2128 | new_smi->slave_addr, | 2221 | new_smi->slave_addr, |
2129 | &(new_smi->intf)); | 2222 | &(new_smi->intf)); |
2130 | if (rv) { | 2223 | if (rv) { |