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