]>
Commit | Line | Data |
---|---|---|
77241056 MM |
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) 2015 Intel Corporation. | |
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 | * BSD LICENSE | |
20 | * | |
21 | * Copyright(c) 2015 Intel Corporation. | |
22 | * | |
23 | * Redistribution and use in source and binary forms, with or without | |
24 | * modification, are permitted provided that the following conditions | |
25 | * are met: | |
26 | * | |
27 | * - Redistributions of source code must retain the above copyright | |
28 | * notice, this list of conditions and the following disclaimer. | |
29 | * - Redistributions in binary form must reproduce the above copyright | |
30 | * notice, this list of conditions and the following disclaimer in | |
31 | * the documentation and/or other materials provided with the | |
32 | * distribution. | |
33 | * - Neither the name of Intel Corporation nor the names of its | |
34 | * contributors may be used to endorse or promote products derived | |
35 | * from this software without specific prior written permission. | |
36 | * | |
37 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
38 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
39 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
40 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
41 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
42 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
43 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
44 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
45 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
46 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
47 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
48 | * | |
49 | */ | |
50 | #include <linux/delay.h> | |
51 | #include "hfi.h" | |
52 | #include "common.h" | |
53 | #include "eprom.h" | |
54 | ||
55 | /* | |
cd371e09 | 56 | * The EPROM is logically divided into three partitions: |
77241056 | 57 | * partition 0: the first 128K, visible from PCI ROM BAR |
cd371e09 DL |
58 | * partition 1: 4K config file (sector size) |
59 | * partition 2: the rest | |
77241056 MM |
60 | */ |
61 | #define P0_SIZE (128 * 1024) | |
cd371e09 | 62 | #define P1_SIZE (4 * 1024) |
77241056 | 63 | #define P1_START P0_SIZE |
cd371e09 DL |
64 | #define P2_START (P0_SIZE + P1_SIZE) |
65 | ||
66 | /* erase sizes supported by the controller */ | |
67 | #define SIZE_4KB (4 * 1024) | |
68 | #define MASK_4KB (SIZE_4KB - 1) | |
77241056 | 69 | |
77241056 MM |
70 | #define SIZE_32KB (32 * 1024) |
71 | #define MASK_32KB (SIZE_32KB - 1) | |
72 | ||
cd371e09 DL |
73 | #define SIZE_64KB (64 * 1024) |
74 | #define MASK_64KB (SIZE_64KB - 1) | |
75 | ||
77241056 MM |
76 | /* controller page size, in bytes */ |
77 | #define EP_PAGE_SIZE 256 | |
78 | #define EEP_PAGE_MASK (EP_PAGE_SIZE - 1) | |
79 | ||
80 | /* controller commands */ | |
81 | #define CMD_SHIFT 24 | |
82 | #define CMD_NOP (0) | |
83 | #define CMD_PAGE_PROGRAM(addr) ((0x02 << CMD_SHIFT) | addr) | |
84 | #define CMD_READ_DATA(addr) ((0x03 << CMD_SHIFT) | addr) | |
85 | #define CMD_READ_SR1 ((0x05 << CMD_SHIFT)) | |
86 | #define CMD_WRITE_ENABLE ((0x06 << CMD_SHIFT)) | |
cd371e09 | 87 | #define CMD_SECTOR_ERASE_4KB(addr) ((0x20 << CMD_SHIFT) | addr) |
77241056 MM |
88 | #define CMD_SECTOR_ERASE_32KB(addr) ((0x52 << CMD_SHIFT) | addr) |
89 | #define CMD_CHIP_ERASE ((0x60 << CMD_SHIFT)) | |
90 | #define CMD_READ_MANUF_DEV_ID ((0x90 << CMD_SHIFT)) | |
91 | #define CMD_RELEASE_POWERDOWN_NOID ((0xab << CMD_SHIFT)) | |
cd371e09 | 92 | #define CMD_SECTOR_ERASE_64KB(addr) ((0xd8 << CMD_SHIFT) | addr) |
77241056 MM |
93 | |
94 | /* controller interface speeds */ | |
95 | #define EP_SPEED_FULL 0x2 /* full speed */ | |
96 | ||
97 | /* controller status register 1 bits */ | |
98 | #define SR1_BUSY 0x1ull /* the BUSY bit in SR1 */ | |
99 | ||
100 | /* sleep length while waiting for controller */ | |
101 | #define WAIT_SLEEP_US 100 /* must be larger than 5 (see usage) */ | |
8638b77f | 102 | #define COUNT_DELAY_SEC(n) ((n) * (1000000 / WAIT_SLEEP_US)) |
77241056 MM |
103 | |
104 | /* GPIO pins */ | |
3f34d958 | 105 | #define EPROM_WP_N BIT_ULL(14) /* EPROM write line */ |
77241056 MM |
106 | |
107 | /* | |
108 | * Use the EP mutex to guard against other callers from within the driver. | |
77241056 MM |
109 | */ |
110 | static DEFINE_MUTEX(eprom_mutex); | |
77241056 MM |
111 | |
112 | /* | |
113 | * Turn on external enable line that allows writing on the flash. | |
114 | */ | |
115 | static void write_enable(struct hfi1_devdata *dd) | |
116 | { | |
117 | /* raise signal */ | |
118 | write_csr(dd, ASIC_GPIO_OUT, | |
119 | read_csr(dd, ASIC_GPIO_OUT) | EPROM_WP_N); | |
120 | /* raise enable */ | |
121 | write_csr(dd, ASIC_GPIO_OE, | |
122 | read_csr(dd, ASIC_GPIO_OE) | EPROM_WP_N); | |
123 | } | |
124 | ||
125 | /* | |
126 | * Turn off external enable line that allows writing on the flash. | |
127 | */ | |
128 | static void write_disable(struct hfi1_devdata *dd) | |
129 | { | |
130 | /* lower signal */ | |
131 | write_csr(dd, ASIC_GPIO_OUT, | |
132 | read_csr(dd, ASIC_GPIO_OUT) & ~EPROM_WP_N); | |
133 | /* lower enable */ | |
134 | write_csr(dd, ASIC_GPIO_OE, | |
135 | read_csr(dd, ASIC_GPIO_OE) & ~EPROM_WP_N); | |
136 | } | |
137 | ||
138 | /* | |
139 | * Wait for the device to become not busy. Must be called after all | |
140 | * write or erase operations. | |
141 | */ | |
142 | static int wait_for_not_busy(struct hfi1_devdata *dd) | |
143 | { | |
144 | unsigned long count = 0; | |
145 | u64 reg; | |
146 | int ret = 0; | |
147 | ||
148 | /* starts page mode */ | |
149 | write_csr(dd, ASIC_EEP_ADDR_CMD, CMD_READ_SR1); | |
150 | while (1) { | |
151 | udelay(WAIT_SLEEP_US); | |
152 | usleep_range(WAIT_SLEEP_US - 5, WAIT_SLEEP_US + 5); | |
153 | count++; | |
154 | reg = read_csr(dd, ASIC_EEP_DATA); | |
155 | if ((reg & SR1_BUSY) == 0) | |
156 | break; | |
157 | /* 200s is the largest time for a 128Mb device */ | |
158 | if (count > COUNT_DELAY_SEC(200)) { | |
159 | dd_dev_err(dd, "waited too long for SPI FLASH busy to clear - failing\n"); | |
160 | ret = -ETIMEDOUT; | |
161 | break; /* break, not goto - must stop page mode */ | |
162 | } | |
163 | } | |
164 | ||
165 | /* stop page mode with a NOP */ | |
166 | write_csr(dd, ASIC_EEP_ADDR_CMD, CMD_NOP); | |
167 | ||
168 | return ret; | |
169 | } | |
170 | ||
171 | /* | |
172 | * Read the device ID from the SPI controller. | |
173 | */ | |
174 | static u32 read_device_id(struct hfi1_devdata *dd) | |
175 | { | |
176 | /* read the Manufacture Device ID */ | |
177 | write_csr(dd, ASIC_EEP_ADDR_CMD, CMD_READ_MANUF_DEV_ID); | |
178 | return (u32)read_csr(dd, ASIC_EEP_DATA); | |
179 | } | |
180 | ||
181 | /* | |
182 | * Erase the whole flash. | |
183 | */ | |
184 | static int erase_chip(struct hfi1_devdata *dd) | |
185 | { | |
186 | int ret; | |
187 | ||
188 | write_enable(dd); | |
189 | ||
190 | write_csr(dd, ASIC_EEP_ADDR_CMD, CMD_WRITE_ENABLE); | |
191 | write_csr(dd, ASIC_EEP_ADDR_CMD, CMD_CHIP_ERASE); | |
192 | ret = wait_for_not_busy(dd); | |
193 | ||
194 | write_disable(dd); | |
195 | ||
196 | return ret; | |
197 | } | |
198 | ||
199 | /* | |
cd371e09 | 200 | * Erase a range. |
77241056 | 201 | */ |
cd371e09 | 202 | static int erase_range(struct hfi1_devdata *dd, u32 start, u32 len) |
77241056 | 203 | { |
cd371e09 | 204 | u32 end = start + len; |
77241056 MM |
205 | int ret = 0; |
206 | ||
207 | if (end < start) | |
208 | return -EINVAL; | |
209 | ||
cd371e09 DL |
210 | /* check the end points for the minimum erase */ |
211 | if ((start & MASK_4KB) || (end & MASK_4KB)) { | |
77241056 | 212 | dd_dev_err(dd, |
cd371e09 | 213 | "%s: non-aligned range (0x%x,0x%x) for a 4KB erase\n", |
77241056 MM |
214 | __func__, start, end); |
215 | return -EINVAL; | |
216 | } | |
217 | ||
218 | write_enable(dd); | |
219 | ||
cd371e09 | 220 | while (start < end) { |
77241056 | 221 | write_csr(dd, ASIC_EEP_ADDR_CMD, CMD_WRITE_ENABLE); |
cd371e09 DL |
222 | /* check in order of largest to smallest */ |
223 | if (((start & MASK_64KB) == 0) && (start + SIZE_64KB <= end)) { | |
224 | write_csr(dd, ASIC_EEP_ADDR_CMD, | |
225 | CMD_SECTOR_ERASE_64KB(start)); | |
226 | start += SIZE_64KB; | |
227 | } else if (((start & MASK_32KB) == 0) && | |
228 | (start + SIZE_32KB <= end)) { | |
229 | write_csr(dd, ASIC_EEP_ADDR_CMD, | |
230 | CMD_SECTOR_ERASE_32KB(start)); | |
231 | start += SIZE_32KB; | |
232 | } else { /* 4KB will work */ | |
233 | write_csr(dd, ASIC_EEP_ADDR_CMD, | |
234 | CMD_SECTOR_ERASE_4KB(start)); | |
235 | start += SIZE_4KB; | |
236 | } | |
77241056 MM |
237 | ret = wait_for_not_busy(dd); |
238 | if (ret) | |
239 | goto done; | |
240 | } | |
241 | ||
242 | done: | |
243 | write_disable(dd); | |
244 | ||
245 | return ret; | |
246 | } | |
247 | ||
248 | /* | |
249 | * Read a 256 byte (64 dword) EPROM page. | |
250 | * All callers have verified the offset is at a page boundary. | |
251 | */ | |
252 | static void read_page(struct hfi1_devdata *dd, u32 offset, u32 *result) | |
253 | { | |
254 | int i; | |
255 | ||
256 | write_csr(dd, ASIC_EEP_ADDR_CMD, CMD_READ_DATA(offset)); | |
8638b77f | 257 | for (i = 0; i < EP_PAGE_SIZE / sizeof(u32); i++) |
77241056 MM |
258 | result[i] = (u32)read_csr(dd, ASIC_EEP_DATA); |
259 | write_csr(dd, ASIC_EEP_ADDR_CMD, CMD_NOP); /* close open page */ | |
260 | } | |
261 | ||
262 | /* | |
263 | * Read length bytes starting at offset. Copy to user address addr. | |
264 | */ | |
265 | static int read_length(struct hfi1_devdata *dd, u32 start, u32 len, u64 addr) | |
266 | { | |
267 | u32 offset; | |
8638b77f | 268 | u32 buffer[EP_PAGE_SIZE / sizeof(u32)]; |
77241056 MM |
269 | int ret = 0; |
270 | ||
271 | /* reject anything not on an EPROM page boundary */ | |
272 | if ((start & EEP_PAGE_MASK) || (len & EEP_PAGE_MASK)) | |
273 | return -EINVAL; | |
274 | ||
275 | for (offset = 0; offset < len; offset += EP_PAGE_SIZE) { | |
276 | read_page(dd, start + offset, buffer); | |
277 | if (copy_to_user((void __user *)(addr + offset), | |
278 | buffer, EP_PAGE_SIZE)) { | |
279 | ret = -EFAULT; | |
280 | goto done; | |
281 | } | |
282 | } | |
283 | ||
284 | done: | |
285 | return ret; | |
286 | } | |
287 | ||
288 | /* | |
289 | * Write a 256 byte (64 dword) EPROM page. | |
290 | * All callers have verified the offset is at a page boundary. | |
291 | */ | |
292 | static int write_page(struct hfi1_devdata *dd, u32 offset, u32 *data) | |
293 | { | |
294 | int i; | |
295 | ||
296 | write_csr(dd, ASIC_EEP_ADDR_CMD, CMD_WRITE_ENABLE); | |
297 | write_csr(dd, ASIC_EEP_DATA, data[0]); | |
298 | write_csr(dd, ASIC_EEP_ADDR_CMD, CMD_PAGE_PROGRAM(offset)); | |
8638b77f | 299 | for (i = 1; i < EP_PAGE_SIZE / sizeof(u32); i++) |
77241056 MM |
300 | write_csr(dd, ASIC_EEP_DATA, data[i]); |
301 | /* will close the open page */ | |
302 | return wait_for_not_busy(dd); | |
303 | } | |
304 | ||
305 | /* | |
306 | * Write length bytes starting at offset. Read from user address addr. | |
307 | */ | |
308 | static int write_length(struct hfi1_devdata *dd, u32 start, u32 len, u64 addr) | |
309 | { | |
310 | u32 offset; | |
8638b77f | 311 | u32 buffer[EP_PAGE_SIZE / sizeof(u32)]; |
77241056 MM |
312 | int ret = 0; |
313 | ||
314 | /* reject anything not on an EPROM page boundary */ | |
315 | if ((start & EEP_PAGE_MASK) || (len & EEP_PAGE_MASK)) | |
316 | return -EINVAL; | |
317 | ||
318 | write_enable(dd); | |
319 | ||
320 | for (offset = 0; offset < len; offset += EP_PAGE_SIZE) { | |
321 | if (copy_from_user(buffer, (void __user *)(addr + offset), | |
322 | EP_PAGE_SIZE)) { | |
323 | ret = -EFAULT; | |
324 | goto done; | |
325 | } | |
326 | ret = write_page(dd, start + offset, buffer); | |
327 | if (ret) | |
328 | goto done; | |
329 | } | |
330 | ||
331 | done: | |
332 | write_disable(dd); | |
333 | return ret; | |
334 | } | |
335 | ||
cd371e09 DL |
336 | /* convert an range composite to a length, in bytes */ |
337 | static inline u32 extract_rlen(u32 composite) | |
338 | { | |
339 | return (composite & 0xffff) * EP_PAGE_SIZE; | |
340 | } | |
341 | ||
342 | /* convert an range composite to a start, in bytes */ | |
343 | static inline u32 extract_rstart(u32 composite) | |
344 | { | |
345 | return (composite >> 16) * EP_PAGE_SIZE; | |
346 | } | |
347 | ||
77241056 MM |
348 | /* |
349 | * Perform the given operation on the EPROM. Called from user space. The | |
350 | * user credentials have already been checked. | |
351 | * | |
352 | * Return 0 on success, -ERRNO on error | |
353 | */ | |
d24bc648 | 354 | int handle_eprom_command(struct file *fp, const struct hfi1_cmd *cmd) |
77241056 MM |
355 | { |
356 | struct hfi1_devdata *dd; | |
357 | u32 dev_id; | |
cd371e09 DL |
358 | u32 rlen; /* range length */ |
359 | u32 rstart; /* range start */ | |
d24bc648 | 360 | int i_minor; |
77241056 MM |
361 | int ret = 0; |
362 | ||
363 | /* | |
d24bc648 DL |
364 | * Map the device file to device data using the relative minor. |
365 | * The device file minor number is the unit number + 1. 0 is | |
366 | * the generic device file - reject it. | |
77241056 | 367 | */ |
d24bc648 DL |
368 | i_minor = iminor(file_inode(fp)) - HFI1_USER_MINOR_BASE; |
369 | if (i_minor <= 0) | |
370 | return -EINVAL; | |
371 | dd = hfi1_lookup(i_minor - 1); | |
77241056 | 372 | if (!dd) { |
d24bc648 | 373 | pr_err("%s: cannot find unit %d!\n", __func__, i_minor); |
77241056 MM |
374 | return -EINVAL; |
375 | } | |
376 | ||
e154f127 DL |
377 | /* some devices do not have an EPROM */ |
378 | if (!dd->eprom_available) | |
379 | return -EOPNOTSUPP; | |
380 | ||
77241056 MM |
381 | /* lock against other callers touching the ASIC block */ |
382 | mutex_lock(&eprom_mutex); | |
383 | ||
77241056 MM |
384 | /* lock against the other HFI on another OS */ |
385 | ret = acquire_hw_mutex(dd); | |
386 | if (ret) { | |
387 | dd_dev_err(dd, | |
388 | "%s: unable to acquire hw mutex, no EPROM support\n", | |
389 | __func__); | |
390 | goto done_asic; | |
391 | } | |
392 | ||
393 | dd_dev_info(dd, "%s: cmd: type %d, len 0x%x, addr 0x%016llx\n", | |
394 | __func__, cmd->type, cmd->len, cmd->addr); | |
395 | ||
396 | switch (cmd->type) { | |
397 | case HFI1_CMD_EP_INFO: | |
398 | if (cmd->len != sizeof(u32)) { | |
399 | ret = -ERANGE; | |
400 | break; | |
401 | } | |
402 | dev_id = read_device_id(dd); | |
403 | /* addr points to a u32 user buffer */ | |
404 | if (copy_to_user((void __user *)cmd->addr, &dev_id, | |
405 | sizeof(u32))) | |
406 | ret = -EFAULT; | |
407 | break; | |
cd371e09 | 408 | |
77241056 MM |
409 | case HFI1_CMD_EP_ERASE_CHIP: |
410 | ret = erase_chip(dd); | |
411 | break; | |
cd371e09 DL |
412 | |
413 | case HFI1_CMD_EP_ERASE_RANGE: | |
414 | rlen = extract_rlen(cmd->len); | |
415 | rstart = extract_rstart(cmd->len); | |
416 | ret = erase_range(dd, rstart, rlen); | |
77241056 | 417 | break; |
cd371e09 DL |
418 | |
419 | case HFI1_CMD_EP_READ_RANGE: | |
420 | rlen = extract_rlen(cmd->len); | |
421 | rstart = extract_rstart(cmd->len); | |
422 | ret = read_length(dd, rstart, rlen, cmd->addr); | |
77241056 | 423 | break; |
cd371e09 DL |
424 | |
425 | case HFI1_CMD_EP_WRITE_RANGE: | |
426 | rlen = extract_rlen(cmd->len); | |
427 | rstart = extract_rstart(cmd->len); | |
428 | ret = write_length(dd, rstart, rlen, cmd->addr); | |
77241056 | 429 | break; |
cd371e09 | 430 | |
77241056 MM |
431 | default: |
432 | dd_dev_err(dd, "%s: unexpected command %d\n", | |
433 | __func__, cmd->type); | |
434 | ret = -EINVAL; | |
435 | break; | |
436 | } | |
437 | ||
438 | release_hw_mutex(dd); | |
439 | done_asic: | |
440 | mutex_unlock(&eprom_mutex); | |
441 | return ret; | |
442 | } | |
443 | ||
444 | /* | |
445 | * Initialize the EPROM handler. | |
446 | */ | |
447 | int eprom_init(struct hfi1_devdata *dd) | |
448 | { | |
449 | int ret = 0; | |
450 | ||
451 | /* only the discrete chip has an EPROM, nothing to do */ | |
452 | if (dd->pcidev->device != PCI_DEVICE_ID_INTEL0) | |
453 | return 0; | |
454 | ||
455 | /* lock against other callers */ | |
456 | mutex_lock(&eprom_mutex); | |
77241056 MM |
457 | |
458 | /* | |
459 | * Lock against the other HFI on another OS - the mutex above | |
460 | * would have caught anything in this driver. It is OK if | |
461 | * both OSes reset the EPROM - as long as they don't do it at | |
462 | * the same time. | |
463 | */ | |
464 | ret = acquire_hw_mutex(dd); | |
465 | if (ret) { | |
466 | dd_dev_err(dd, | |
467 | "%s: unable to acquire hw mutex, no EPROM support\n", | |
468 | __func__); | |
469 | goto done_asic; | |
470 | } | |
471 | ||
472 | /* reset EPROM to be sure it is in a good state */ | |
473 | ||
474 | /* set reset */ | |
475 | write_csr(dd, ASIC_EEP_CTL_STAT, | |
476 | ASIC_EEP_CTL_STAT_EP_RESET_SMASK); | |
477 | /* clear reset, set speed */ | |
478 | write_csr(dd, ASIC_EEP_CTL_STAT, | |
479 | EP_SPEED_FULL << ASIC_EEP_CTL_STAT_RATE_SPI_SHIFT); | |
480 | ||
481 | /* wake the device with command "release powerdown NoID" */ | |
482 | write_csr(dd, ASIC_EEP_ADDR_CMD, CMD_RELEASE_POWERDOWN_NOID); | |
483 | ||
e154f127 | 484 | dd->eprom_available = true; |
77241056 MM |
485 | release_hw_mutex(dd); |
486 | done_asic: | |
487 | mutex_unlock(&eprom_mutex); | |
488 | return ret; | |
489 | } |