1/* 2 * Intel Wireless Multicomm 3200 WiFi driver 3 * 4 * Copyright (C) 2009 Intel Corporation. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * * Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * * Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in 14 * the documentation and/or other materials provided with the 15 * distribution. 16 * * Neither the name of Intel Corporation nor the names of its 17 * contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 * 32 * 33 * Intel Corporation <ilw@linux.intel.com> 34 * Samuel Ortiz <samuel.ortiz@intel.com> 35 * Zhu Yi <yi.zhu@intel.com> 36 * 37 */ 38 39#include <linux/kernel.h> 40#include <linux/slab.h> 41 42#include "iwm.h" 43#include "umac.h" 44#include "commands.h" 45#include "eeprom.h" 46 47static struct iwm_eeprom_entry eeprom_map[] = { 48 [IWM_EEPROM_SIG] = 49 {"Signature", IWM_EEPROM_SIG_OFF, IWM_EEPROM_SIG_LEN}, 50 51 [IWM_EEPROM_VERSION] = 52 {"Version", IWM_EEPROM_VERSION_OFF, IWM_EEPROM_VERSION_LEN}, 53 54 [IWM_EEPROM_OEM_HW_VERSION] = 55 {"OEM HW version", IWM_EEPROM_OEM_HW_VERSION_OFF, 56 IWM_EEPROM_OEM_HW_VERSION_LEN}, 57 58 [IWM_EEPROM_MAC_VERSION] = 59 {"MAC version", IWM_EEPROM_MAC_VERSION_OFF, IWM_EEPROM_MAC_VERSION_LEN}, 60 61 [IWM_EEPROM_CARD_ID] = 62 {"Card ID", IWM_EEPROM_CARD_ID_OFF, IWM_EEPROM_CARD_ID_LEN}, 63 64 [IWM_EEPROM_RADIO_CONF] = 65 {"Radio config", IWM_EEPROM_RADIO_CONF_OFF, IWM_EEPROM_RADIO_CONF_LEN}, 66 67 [IWM_EEPROM_SKU_CAP] = 68 {"SKU capabilities", IWM_EEPROM_SKU_CAP_OFF, IWM_EEPROM_SKU_CAP_LEN}, 69 70 [IWM_EEPROM_FAT_CHANNELS_CAP] = 71 {"HT channels capabilities", IWM_EEPROM_FAT_CHANNELS_CAP_OFF, 72 IWM_EEPROM_FAT_CHANNELS_CAP_LEN}, 73 74 [IWM_EEPROM_CALIB_RXIQ_OFFSET] = 75 {"RX IQ offset", IWM_EEPROM_CALIB_RXIQ_OFF, IWM_EEPROM_INDIRECT_LEN}, 76 77 [IWM_EEPROM_CALIB_RXIQ] = 78 {"Calib RX IQ", 0, IWM_EEPROM_CALIB_RXIQ_LEN}, 79}; 80 81 82static int iwm_eeprom_read(struct iwm_priv *iwm, u8 eeprom_id) 83{ 84 int ret; 85 u32 entry_size, chunk_size, data_offset = 0, addr_offset = 0; 86 u32 addr; 87 struct iwm_udma_wifi_cmd udma_cmd; 88 struct iwm_umac_cmd umac_cmd; 89 struct iwm_umac_cmd_eeprom_proxy eeprom_cmd; 90 91 if (eeprom_id > (IWM_EEPROM_LAST - 1)) 92 return -EINVAL; 93 94 entry_size = eeprom_map[eeprom_id].length; 95 96 if (eeprom_id >= IWM_EEPROM_INDIRECT_DATA) { 97 /* indirect data */ 98 u32 off_id = eeprom_id - IWM_EEPROM_INDIRECT_DATA + 99 IWM_EEPROM_INDIRECT_OFFSET; 100 101 eeprom_map[eeprom_id].offset = 102 *(u16 *)(iwm->eeprom + eeprom_map[off_id].offset) << 1; 103 } 104 105 addr = eeprom_map[eeprom_id].offset; 106 107 udma_cmd.eop = 1; 108 udma_cmd.credit_group = 0x4; 109 udma_cmd.ra_tid = UMAC_HDI_ACT_TBL_IDX_HOST_CMD; 110 udma_cmd.lmac_offset = 0; 111 112 umac_cmd.id = UMAC_CMD_OPCODE_EEPROM_PROXY; 113 umac_cmd.resp = 1; 114 115 while (entry_size > 0) { 116 chunk_size = min_t(u32, entry_size, IWM_MAX_EEPROM_DATA_LEN); 117 118 eeprom_cmd.hdr.type = 119 cpu_to_le32(IWM_UMAC_CMD_EEPROM_TYPE_READ); 120 eeprom_cmd.hdr.offset = cpu_to_le32(addr + addr_offset); 121 eeprom_cmd.hdr.len = cpu_to_le32(chunk_size); 122 123 ret = iwm_hal_send_umac_cmd(iwm, &udma_cmd, 124 &umac_cmd, &eeprom_cmd, 125 sizeof(struct iwm_umac_cmd_eeprom_proxy)); 126 if (ret < 0) { 127 IWM_ERR(iwm, "Couldn't read eeprom\n"); 128 return ret; 129 } 130 131 ret = iwm_notif_handle(iwm, UMAC_CMD_OPCODE_EEPROM_PROXY, 132 IWM_SRC_UMAC, 2*HZ); 133 if (ret < 0) { 134 IWM_ERR(iwm, "Did not get any eeprom answer\n"); 135 return ret; 136 } 137 138 data_offset += chunk_size; 139 addr_offset += chunk_size; 140 entry_size -= chunk_size; 141 } 142 143 return 0; 144} 145 146u8 *iwm_eeprom_access(struct iwm_priv *iwm, u8 eeprom_id) 147{ 148 if (!iwm->eeprom) 149 return ERR_PTR(-ENODEV); 150 151 return iwm->eeprom + eeprom_map[eeprom_id].offset; 152} 153 154int iwm_eeprom_fat_channels(struct iwm_priv *iwm) 155{ 156 struct wiphy *wiphy = iwm_to_wiphy(iwm); 157 struct ieee80211_supported_band *band; 158 u16 *channels, i; 159 160 channels = (u16 *)iwm_eeprom_access(iwm, IWM_EEPROM_FAT_CHANNELS_CAP); 161 if (IS_ERR(channels)) 162 return PTR_ERR(channels); 163 164 band = wiphy->bands[IEEE80211_BAND_2GHZ]; 165 band->ht_cap.ht_supported = true; 166 167 for (i = 0; i < IWM_EEPROM_FAT_CHANNELS_24; i++) 168 if (!(channels[i] & IWM_EEPROM_FAT_CHANNEL_ENABLED)) 169 band->ht_cap.ht_supported = false; 170 171 band = wiphy->bands[IEEE80211_BAND_5GHZ]; 172 band->ht_cap.ht_supported = true; 173 for (i = IWM_EEPROM_FAT_CHANNELS_24; i < IWM_EEPROM_FAT_CHANNELS; i++) 174 if (!(channels[i] & IWM_EEPROM_FAT_CHANNEL_ENABLED)) 175 band->ht_cap.ht_supported = false; 176 177 return 0; 178} 179 180u32 iwm_eeprom_wireless_mode(struct iwm_priv *iwm) 181{ 182 u16 sku_cap; 183 u32 wireless_mode = 0; 184 185 sku_cap = *((u16 *)iwm_eeprom_access(iwm, IWM_EEPROM_SKU_CAP)); 186 187 if (sku_cap & IWM_EEPROM_SKU_CAP_BAND_24GHZ) 188 wireless_mode |= WIRELESS_MODE_11G; 189 190 if (sku_cap & IWM_EEPROM_SKU_CAP_BAND_52GHZ) 191 wireless_mode |= WIRELESS_MODE_11A; 192 193 if (sku_cap & IWM_EEPROM_SKU_CAP_11N_ENABLE) 194 wireless_mode |= WIRELESS_MODE_11N; 195 196 return wireless_mode; 197} 198 199 200int iwm_eeprom_init(struct iwm_priv *iwm) 201{ 202 int i, ret = 0; 203 char name[32]; 204 205 iwm->eeprom = kzalloc(IWM_EEPROM_LEN, GFP_KERNEL); 206 if (!iwm->eeprom) 207 return -ENOMEM; 208 209 for (i = IWM_EEPROM_FIRST; i < IWM_EEPROM_LAST; i++) { 210 ret = iwm_eeprom_read(iwm, i); 211 if (ret < 0) { 212 IWM_ERR(iwm, "Couldn't read eeprom entry #%d: %s\n", 213 i, eeprom_map[i].name); 214 break; 215 } 216 } 217 218 IWM_DBG_BOOT(iwm, DBG, "EEPROM dump:\n"); 219 for (i = IWM_EEPROM_FIRST; i < IWM_EEPROM_LAST; i++) { 220 memset(name, 0, 32); 221 sprintf(name, "%s: ", eeprom_map[i].name); 222 223 IWM_HEXDUMP(iwm, DBG, BOOT, name, 224 iwm->eeprom + eeprom_map[i].offset, 225 eeprom_map[i].length); 226 } 227 228 return ret; 229} 230 231void iwm_eeprom_exit(struct iwm_priv *iwm) 232{ 233 kfree(iwm->eeprom); 234} 235