Commit | Line | Data |
---|---|---|
bb9f8692 ZY |
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> | |
5a0e3ad6 | 40 | #include <linux/slab.h> |
bb9f8692 ZY |
41 | |
42 | #include "iwm.h" | |
43 | #include "umac.h" | |
44 | #include "commands.h" | |
45 | #include "eeprom.h" | |
46 | ||
47 | static 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 | ||
902b6667 SO |
70 | [IWM_EEPROM_FAT_CHANNELS_CAP] = |
71 | {"HT channels capabilities", IWM_EEPROM_FAT_CHANNELS_CAP_OFF, | |
72 | IWM_EEPROM_FAT_CHANNELS_CAP_LEN}, | |
73 | ||
bb9f8692 ZY |
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 | ||
82 | static 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 | ||
146 | u8 *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 | ||
902b6667 SO |
154 | int 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 | ||
0bed08de SO |
180 | u32 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 | ||
bb9f8692 ZY |
200 | int 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++) { | |
bb9f8692 ZY |
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 | ||
231 | void iwm_eeprom_exit(struct iwm_priv *iwm) | |
232 | { | |
233 | kfree(iwm->eeprom); | |
234 | } |