Commit | Line | Data |
---|---|---|
8ca151b5 JB |
1 | /****************************************************************************** |
2 | * | |
3 | * This file is provided under a dual BSD/GPLv2 license. When using or | |
4 | * redistributing this file, you may do so under either license. | |
5 | * | |
6 | * GPL LICENSE SUMMARY | |
7 | * | |
8 | * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify | |
11 | * it under the terms of version 2 of the GNU General Public License as | |
12 | * published by the Free Software Foundation. | |
13 | * | |
14 | * This program is distributed in the hope that it will be useful, but | |
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
17 | * General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU General Public License | |
20 | * along with this program; if not, write to the Free Software | |
21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, | |
22 | * USA | |
23 | * | |
24 | * The full GNU General Public License is included in this distribution | |
410dc5aa | 25 | * in the file called COPYING. |
8ca151b5 JB |
26 | * |
27 | * Contact Information: | |
28 | * Intel Linux Wireless <ilw@linux.intel.com> | |
29 | * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 | |
30 | * | |
31 | * BSD LICENSE | |
32 | * | |
33 | * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. | |
34 | * All rights reserved. | |
35 | * | |
36 | * Redistribution and use in source and binary forms, with or without | |
37 | * modification, are permitted provided that the following conditions | |
38 | * are met: | |
39 | * | |
40 | * * Redistributions of source code must retain the above copyright | |
41 | * notice, this list of conditions and the following disclaimer. | |
42 | * * Redistributions in binary form must reproduce the above copyright | |
43 | * notice, this list of conditions and the following disclaimer in | |
44 | * the documentation and/or other materials provided with the | |
45 | * distribution. | |
46 | * * Neither the name Intel Corporation nor the names of its | |
47 | * contributors may be used to endorse or promote products derived | |
48 | * from this software without specific prior written permission. | |
49 | * | |
50 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
51 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
52 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
53 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
54 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
55 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
56 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
57 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
58 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
59 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
60 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
61 | * | |
62 | *****************************************************************************/ | |
63 | #include "iwl-trans.h" | |
64 | #include "mvm.h" | |
65 | #include "iwl-eeprom-parse.h" | |
66 | #include "iwl-eeprom-read.h" | |
67 | #include "iwl-nvm-parse.h" | |
68 | ||
69 | /* list of NVM sections we are allowed/need to read */ | |
70 | static const int nvm_to_read[] = { | |
71 | NVM_SECTION_TYPE_HW, | |
72 | NVM_SECTION_TYPE_SW, | |
73 | NVM_SECTION_TYPE_CALIBRATION, | |
74 | NVM_SECTION_TYPE_PRODUCTION, | |
75 | }; | |
76 | ||
1fd4afe2 DS |
77 | /* Default NVM size to read */ |
78 | #define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024); | |
79 | ||
8ca151b5 JB |
80 | /* used to simplify the shared operations on NCM_ACCESS_CMD versions */ |
81 | union iwl_nvm_access_cmd { | |
82 | struct iwl_nvm_access_cmd_ver1 ver1; | |
83 | struct iwl_nvm_access_cmd_ver2 ver2; | |
84 | }; | |
85 | union iwl_nvm_access_resp { | |
86 | struct iwl_nvm_access_resp_ver1 ver1; | |
87 | struct iwl_nvm_access_resp_ver2 ver2; | |
88 | }; | |
89 | ||
90 | static inline void iwl_nvm_fill_read_ver1(struct iwl_nvm_access_cmd_ver1 *cmd, | |
91 | u16 offset, u16 length) | |
92 | { | |
93 | cmd->offset = cpu_to_le16(offset); | |
94 | cmd->length = cpu_to_le16(length); | |
95 | cmd->cache_refresh = 1; | |
96 | } | |
97 | ||
98 | static inline void iwl_nvm_fill_read_ver2(struct iwl_nvm_access_cmd_ver2 *cmd, | |
99 | u16 offset, u16 length, u16 section) | |
100 | { | |
101 | cmd->offset = cpu_to_le16(offset); | |
102 | cmd->length = cpu_to_le16(length); | |
103 | cmd->type = cpu_to_le16(section); | |
104 | } | |
105 | ||
106 | static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section, | |
107 | u16 offset, u16 length, u8 *data) | |
108 | { | |
109 | union iwl_nvm_access_cmd nvm_access_cmd; | |
110 | union iwl_nvm_access_resp *nvm_resp; | |
111 | struct iwl_rx_packet *pkt; | |
112 | struct iwl_host_cmd cmd = { | |
113 | .id = NVM_ACCESS_CMD, | |
114 | .flags = CMD_SYNC | CMD_WANT_SKB, | |
115 | .data = { &nvm_access_cmd, }, | |
116 | }; | |
117 | int ret, bytes_read, offset_read; | |
118 | u8 *resp_data; | |
119 | ||
120 | memset(&nvm_access_cmd, 0, sizeof(nvm_access_cmd)); | |
121 | ||
122 | /* TODO: not sure family should be the decider, maybe FW version? */ | |
123 | if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) { | |
124 | iwl_nvm_fill_read_ver2(&(nvm_access_cmd.ver2), | |
125 | offset, length, section); | |
126 | cmd.len[0] = sizeof(struct iwl_nvm_access_cmd_ver2); | |
127 | } else { | |
128 | iwl_nvm_fill_read_ver1(&(nvm_access_cmd.ver1), | |
129 | offset, length); | |
130 | cmd.len[0] = sizeof(struct iwl_nvm_access_cmd_ver1); | |
131 | } | |
132 | ||
133 | ret = iwl_mvm_send_cmd(mvm, &cmd); | |
134 | if (ret) | |
135 | return ret; | |
136 | ||
137 | pkt = cmd.resp_pkt; | |
138 | if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { | |
139 | IWL_ERR(mvm, "Bad return from NVM_ACCES_COMMAND (0x%08X)\n", | |
140 | pkt->hdr.flags); | |
141 | ret = -EIO; | |
142 | goto exit; | |
143 | } | |
144 | ||
145 | /* Extract NVM response */ | |
146 | nvm_resp = (void *)pkt->data; | |
147 | if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) { | |
148 | ret = le16_to_cpu(nvm_resp->ver2.status); | |
149 | bytes_read = le16_to_cpu(nvm_resp->ver2.length); | |
150 | offset_read = le16_to_cpu(nvm_resp->ver2.offset); | |
151 | resp_data = nvm_resp->ver2.data; | |
152 | } else { | |
153 | ret = le16_to_cpu(nvm_resp->ver1.length) <= 0; | |
154 | bytes_read = le16_to_cpu(nvm_resp->ver1.length); | |
155 | offset_read = le16_to_cpu(nvm_resp->ver1.offset); | |
156 | resp_data = nvm_resp->ver1.data; | |
157 | } | |
158 | if (ret) { | |
159 | IWL_ERR(mvm, | |
160 | "NVM access command failed with status %d (device: %s)\n", | |
161 | ret, mvm->cfg->name); | |
162 | ret = -EINVAL; | |
163 | goto exit; | |
164 | } | |
165 | ||
166 | if (offset_read != offset) { | |
167 | IWL_ERR(mvm, "NVM ACCESS response with invalid offset %d\n", | |
168 | offset_read); | |
169 | ret = -EINVAL; | |
170 | goto exit; | |
171 | } | |
172 | ||
173 | /* Write data to NVM */ | |
174 | memcpy(data + offset, resp_data, bytes_read); | |
175 | ret = bytes_read; | |
176 | ||
177 | exit: | |
178 | iwl_free_resp(&cmd); | |
179 | return ret; | |
180 | } | |
181 | ||
182 | /* | |
183 | * Reads an NVM section completely. | |
184 | * NICs prior to 7000 family doesn't have a real NVM, but just read | |
185 | * section 0 which is the EEPROM. Because the EEPROM reading is unlimited | |
186 | * by uCode, we need to manually check in this case that we don't | |
187 | * overflow and try to read more than the EEPROM size. | |
188 | * For 7000 family NICs, we supply the maximal size we can read, and | |
189 | * the uCode fills the response with as much data as we can, | |
190 | * without overflowing, so no check is needed. | |
191 | */ | |
192 | static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section, | |
193 | u8 *data) | |
194 | { | |
195 | u16 length, offset = 0; | |
196 | int ret; | |
197 | bool old_eeprom = mvm->cfg->device_family != IWL_DEVICE_FAMILY_7000; | |
198 | ||
1fd4afe2 DS |
199 | /* Set nvm section read length */ |
200 | length = IWL_NVM_DEFAULT_CHUNK_SIZE; | |
201 | ||
8ca151b5 JB |
202 | /* |
203 | * if length is greater than EEPROM size, truncate it because uCode | |
204 | * doesn't check it by itself, and exit the loop when reached. | |
205 | */ | |
206 | if (old_eeprom && length > mvm->cfg->base_params->eeprom_size) | |
207 | length = mvm->cfg->base_params->eeprom_size; | |
208 | ret = length; | |
209 | ||
210 | /* Read the NVM until exhausted (reading less than requested) */ | |
211 | while (ret == length) { | |
212 | ret = iwl_nvm_read_chunk(mvm, section, offset, length, data); | |
213 | if (ret < 0) { | |
214 | IWL_ERR(mvm, | |
215 | "Cannot read NVM from section %d offset %d, length %d\n", | |
216 | section, offset, length); | |
217 | return ret; | |
218 | } | |
219 | offset += ret; | |
220 | if (old_eeprom && offset == mvm->cfg->base_params->eeprom_size) | |
221 | break; | |
222 | } | |
223 | ||
224 | IWL_INFO(mvm, "NVM section %d read completed\n", section); | |
225 | return offset; | |
226 | } | |
227 | ||
228 | static struct iwl_nvm_data * | |
229 | iwl_parse_nvm_sections(struct iwl_mvm *mvm) | |
230 | { | |
231 | struct iwl_nvm_section *sections = mvm->nvm_sections; | |
232 | const __le16 *hw, *sw, *calib; | |
233 | ||
234 | /* Checking for required sections */ | |
235 | if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data || | |
236 | !mvm->nvm_sections[NVM_SECTION_TYPE_HW].data) { | |
237 | IWL_ERR(mvm, "Can't parse empty NVM sections\n"); | |
238 | return NULL; | |
239 | } | |
240 | ||
241 | if (WARN_ON(!mvm->cfg)) | |
242 | return NULL; | |
243 | ||
244 | hw = (const __le16 *)sections[NVM_SECTION_TYPE_HW].data; | |
245 | sw = (const __le16 *)sections[NVM_SECTION_TYPE_SW].data; | |
246 | calib = (const __le16 *)sections[NVM_SECTION_TYPE_CALIBRATION].data; | |
247 | return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib); | |
248 | } | |
249 | ||
250 | int iwl_nvm_init(struct iwl_mvm *mvm) | |
251 | { | |
252 | int ret, i, section; | |
253 | u8 *nvm_buffer, *temp; | |
254 | ||
255 | if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) { | |
256 | /* TODO: find correct NVM max size for a section */ | |
257 | nvm_buffer = kmalloc(mvm->cfg->base_params->eeprom_size, | |
258 | GFP_KERNEL); | |
259 | if (!nvm_buffer) | |
260 | return -ENOMEM; | |
261 | for (i = 0; i < ARRAY_SIZE(nvm_to_read); i++) { | |
262 | section = nvm_to_read[i]; | |
263 | /* we override the constness for initial read */ | |
264 | ret = iwl_nvm_read_section(mvm, section, nvm_buffer); | |
265 | if (ret < 0) | |
266 | break; | |
267 | temp = kmemdup(nvm_buffer, ret, GFP_KERNEL); | |
268 | if (!temp) { | |
269 | ret = -ENOMEM; | |
270 | break; | |
271 | } | |
272 | mvm->nvm_sections[section].data = temp; | |
273 | mvm->nvm_sections[section].length = ret; | |
274 | } | |
275 | kfree(nvm_buffer); | |
276 | if (ret < 0) | |
277 | return ret; | |
278 | } else { | |
279 | /* allocate eeprom */ | |
280 | mvm->eeprom_blob_size = mvm->cfg->base_params->eeprom_size; | |
281 | IWL_DEBUG_EEPROM(mvm->trans->dev, "NVM size = %zd\n", | |
282 | mvm->eeprom_blob_size); | |
283 | mvm->eeprom_blob = kzalloc(mvm->eeprom_blob_size, GFP_KERNEL); | |
284 | if (!mvm->eeprom_blob) | |
285 | return -ENOMEM; | |
286 | ||
287 | ret = iwl_nvm_read_section(mvm, 0, mvm->eeprom_blob); | |
288 | if (ret != mvm->eeprom_blob_size) { | |
289 | IWL_ERR(mvm, "Read partial NVM %d/%zd\n", | |
290 | ret, mvm->eeprom_blob_size); | |
291 | kfree(mvm->eeprom_blob); | |
292 | mvm->eeprom_blob = NULL; | |
293 | return -EINVAL; | |
294 | } | |
295 | } | |
296 | ||
297 | ret = 0; | |
298 | if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) | |
299 | mvm->nvm_data = iwl_parse_nvm_sections(mvm); | |
300 | else | |
301 | mvm->nvm_data = | |
302 | iwl_parse_eeprom_data(mvm->trans->dev, | |
303 | mvm->cfg, | |
304 | mvm->eeprom_blob, | |
305 | mvm->eeprom_blob_size); | |
306 | ||
307 | if (!mvm->nvm_data) { | |
308 | kfree(mvm->eeprom_blob); | |
309 | mvm->eeprom_blob = NULL; | |
310 | ret = -ENOMEM; | |
311 | } | |
312 | ||
313 | return ret; | |
314 | } |