Commit 3ae0e0f9b15b95a2c3e64088d2a85e3f4a707681

Authored by Corey Minyard
Committed by Linus Torvalds
1 parent 07766f241b

[PATCH] ipmi: OEM flag handling and hacks for some Dell machines

The ipmi driver does not have a way to handle firmware-generated events
which have the OEM[012] Data Available flags set.  In such a case, the
SMS_ATN bit may never get cleared by firmware, leaving the driver looping
infinitely but never able to make any progress.

This patch first simplifies storage and use of the data returned from an
IPMI Get Device ID command.

It then creates a new per-OEM handler hook, which should know how to handle
events with the OEM[012] Data Available flags set.  It then uses this to
implement a workaround for IPMI 1.5-capable Dell PowerEdge servers which
are susceptable to setting the OEM[012] Data Available flags when the
driver can't handle it.

Signed-off-by: Matt Domsch <Matt_Domsch@dell.com>
Signed-off-by: Corey Minyard <minyard@acm.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

Showing 1 changed file with 105 additions and 12 deletions Side-by-side Diff

drivers/char/ipmi/ipmi_si_intf.c
... ... @@ -110,6 +110,21 @@
110 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 128 struct smi_info
114 129 {
115 130 ipmi_smi_t intf;
116 131  
... ... @@ -132,12 +147,24 @@
132 147 void (*irq_cleanup)(struct smi_info *info);
133 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 156 /* Flags from the last GET_MSG_FLAGS command, used when an ATTN
136 157 is set to hold the flags until we are done handling everything
137 158 from the flags. */
138 159 #define RECEIVE_MSG_AVAIL 0x01
139 160 #define EVENT_MSG_BUFFER_FULL 0x02
140 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 168 unsigned char msg_flags;
142 169  
143 170 /* If set to true, this will request events the next time the
... ... @@ -176,11 +203,7 @@
176 203 interrupts. */
177 204 int interrupt_disabled;
178 205  
179   - unsigned char ipmi_si_dev_rev;
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;
  206 + struct ipmi_device_id device_id;
184 207  
185 208 /* Slave address, could be reported from DMI. */
186 209 unsigned char slave_addr;
... ... @@ -323,6 +346,7 @@
323 346  
324 347 static void handle_flags(struct smi_info *smi_info)
325 348 {
  349 + retry:
326 350 if (smi_info->msg_flags & WDT_PRE_TIMEOUT_INT) {
327 351 /* Watchdog pre-timeout */
328 352 spin_lock(&smi_info->count_lock);
... ... @@ -372,6 +396,10 @@
372 396 smi_info->curr_msg->data,
373 397 smi_info->curr_msg->data_size);
374 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 403 } else {
376 404 smi_info->si_state = SI_NORMAL;
377 405 }
... ... @@ -1927,11 +1955,8 @@
1927 1955 }
1928 1956  
1929 1957 /* Record info from the get device id, in case we need it. */
1930   - smi_info->ipmi_si_dev_rev = resp[4] & 0xf;
1931   - smi_info->ipmi_si_fw_rev_major = resp[5] & 0x7f;
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;
  1958 + memcpy(&smi_info->device_id, &resp[3],
  1959 + min_t(unsigned long, resp_len-3, sizeof(smi_info->device_id)));
1935 1960  
1936 1961 out:
1937 1962 kfree(resp);
... ... @@ -1992,6 +2017,72 @@
1992 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 2086 /* Returns 0 if initialized, or negative on an error. */
1996 2087 static int init_one_smi(int intf_num, struct smi_info **smi)
1997 2088 {
... ... @@ -2090,6 +2181,8 @@
2090 2181 if (rv)
2091 2182 goto out_err;
2092 2183  
  2184 + setup_oem_data_handler(new_smi);
  2185 +
2093 2186 /* Try to claim any interrupts. */
2094 2187 new_smi->irq_setup(new_smi);
2095 2188  
... ... @@ -2123,8 +2216,8 @@
2123 2216  
2124 2217 rv = ipmi_register_smi(&handlers,
2125 2218 new_smi,
2126   - new_smi->ipmi_version_major,
2127   - new_smi->ipmi_version_minor,
  2219 + ipmi_version_major(&new_smi->device_id),
  2220 + ipmi_version_minor(&new_smi->device_id),
2128 2221 new_smi->slave_addr,
2129 2222 &(new_smi->intf));
2130 2223 if (rv) {