|
1 /** |
|
2 Network Driver for Beckhoff CCAT communication controller |
|
3 Copyright (C) 2014 Beckhoff Automation GmbH |
|
4 Author: Patrick Bruenn <p.bruenn@beckhoff.com> |
|
5 |
|
6 This program is free software; you can redistribute it and/or modify |
|
7 it under the terms of the GNU General Public License as published by |
|
8 the Free Software Foundation; either version 2 of the License, or |
|
9 (at your option) any later version. |
|
10 |
|
11 This program is distributed in the hope that it will be useful, |
|
12 but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
14 GNU General Public License for more details. |
|
15 |
|
16 You should have received a copy of the GNU General Public License along |
|
17 with this program; if not, write to the Free Software Foundation, Inc., |
|
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
|
19 */ |
|
20 |
|
21 #include <linux/fs.h> |
|
22 #include <linux/kernel.h> |
|
23 #include <linux/module.h> |
|
24 #include <linux/sched.h> |
|
25 #include <linux/uaccess.h> |
|
26 #include "compat.h" |
|
27 #include "module.h" |
|
28 #include "print.h" |
|
29 #include "update.h" |
|
30 |
|
31 #define CCAT_DATA_IN_4 0x038 |
|
32 #define CCAT_DATA_IN_N 0x7F0 |
|
33 #define CCAT_DATA_OUT_4 0x030 |
|
34 #define CCAT_DATA_BLOCK_SIZE (size_t)((CCAT_DATA_IN_N - CCAT_DATA_IN_4)/8) |
|
35 #define CCAT_WRITE_BLOCK_SIZE 128 |
|
36 #define CCAT_FLASH_SIZE (size_t)0xE0000 |
|
37 |
|
38 /** FUNCTION_NAME CMD, CLOCKS */ |
|
39 #define CCAT_BULK_ERASE 0xE3, 8 |
|
40 #define CCAT_GET_PROM_ID 0xD5, 40 |
|
41 #define CCAT_READ_FLASH 0xC0, 32 |
|
42 #define CCAT_READ_STATUS 0xA0, 16 |
|
43 #define CCAT_WRITE_ENABLE 0x60, 8 |
|
44 #define CCAT_WRITE_FLASH 0x40, 32 |
|
45 |
|
46 /* from http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith32Bits */ |
|
47 #define SWAP_BITS(B) \ |
|
48 ((((B) * 0x0802LU & 0x22110LU) | ((B) * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16) |
|
49 |
|
50 /** |
|
51 * struct update_buffer - keep track of a CCAT FPGA update |
|
52 * @update: pointer to a valid ccat_update object |
|
53 * @data: buffer used for write operations |
|
54 * @size: number of bytes written to the data buffer, if 0 on ccat_update_release() no data will be written to FPGA |
|
55 */ |
|
56 struct update_buffer { |
|
57 struct ccat_update *update; |
|
58 char data[CCAT_FLASH_SIZE]; |
|
59 size_t size; |
|
60 }; |
|
61 |
|
62 static void ccat_wait_status_cleared(void __iomem * const ioaddr); |
|
63 static int ccat_read_flash(void __iomem * const ioaddr, char __user * buf, |
|
64 uint32_t len, loff_t * off); |
|
65 static void ccat_write_flash(const struct update_buffer *const buf); |
|
66 static void ccat_update_cmd(void __iomem * const ioaddr, uint8_t cmd, |
|
67 uint16_t clocks); |
|
68 static void ccat_update_destroy(struct kref *ref); |
|
69 |
|
70 /** |
|
71 * wait_until_busy_reset() - wait until the busy flag was reset |
|
72 * @ioaddr: address of the CCAT Update function in PCI config space |
|
73 */ |
|
74 static inline void wait_until_busy_reset(void __iomem * const ioaddr) |
|
75 { |
|
76 wmb(); |
|
77 while (ioread8(ioaddr + 1)) { |
|
78 schedule(); |
|
79 } |
|
80 } |
|
81 |
|
82 static int ccat_update_open(struct inode *const i, struct file *const f) |
|
83 { |
|
84 struct ccat_update *update = |
|
85 container_of(i->i_cdev, struct ccat_update, cdev); |
|
86 struct update_buffer *buf; |
|
87 kref_get(&update->refcount); |
|
88 if (atomic_read(&update->refcount.refcount) > 2) { |
|
89 kref_put(&update->refcount, ccat_update_destroy); |
|
90 return -EBUSY; |
|
91 } |
|
92 |
|
93 buf = kzalloc(sizeof(*buf), GFP_KERNEL); |
|
94 if (!buf) { |
|
95 kref_put(&update->refcount, ccat_update_destroy); |
|
96 return -ENOMEM; |
|
97 } |
|
98 |
|
99 buf->update = update; |
|
100 f->private_data = buf; |
|
101 return 0; |
|
102 } |
|
103 |
|
104 static int ccat_update_release(struct inode *const i, struct file *const f) |
|
105 { |
|
106 const struct update_buffer *const buf = f->private_data; |
|
107 struct ccat_update *const update = buf->update; |
|
108 if (buf->size > 0) { |
|
109 ccat_update_cmd(update->ioaddr, CCAT_WRITE_ENABLE); |
|
110 ccat_update_cmd(update->ioaddr, CCAT_BULK_ERASE); |
|
111 ccat_wait_status_cleared(update->ioaddr); |
|
112 ccat_write_flash(buf); |
|
113 } |
|
114 kfree(f->private_data); |
|
115 kref_put(&update->refcount, ccat_update_destroy); |
|
116 return 0; |
|
117 } |
|
118 |
|
119 /** |
|
120 * ccat_update_read() - Read CCAT configuration data from flash |
|
121 * @f: file handle previously initialized with ccat_update_open() |
|
122 * @buf: buffer in user space provided for our data |
|
123 * @len: length of the user space buffer |
|
124 * @off: current offset of our file operation |
|
125 * |
|
126 * Copies data from the CCAT FPGA's configuration flash to user space. |
|
127 * Note that the size of the FPGA's firmware is not known exactly so it |
|
128 * is very possible that the overall buffer ends with a lot of 0xff. |
|
129 * |
|
130 * Return: the number of bytes written, or 0 if EOF reached |
|
131 */ |
|
132 static ssize_t ccat_update_read(struct file *const f, char __user * buf, |
|
133 size_t len, loff_t * off) |
|
134 { |
|
135 struct update_buffer *update = f->private_data; |
|
136 if (!buf || !off) { |
|
137 return -EINVAL; |
|
138 } |
|
139 if (*off >= CCAT_FLASH_SIZE) { |
|
140 return 0; |
|
141 } |
|
142 if (*off + len >= CCAT_FLASH_SIZE) { |
|
143 len = CCAT_FLASH_SIZE - *off; |
|
144 } |
|
145 return ccat_read_flash(update->update->ioaddr, buf, len, off); |
|
146 } |
|
147 |
|
148 /** |
|
149 * ccat_update_write() - Write data to the CCAT FPGA's configuration flash |
|
150 * @f: file handle previously initialized with ccat_update_open() |
|
151 * @buf: buffer in user space providing the new configuration data (from *.rbf) |
|
152 * @len: length of the user space buffer |
|
153 * @off: current offset in the configuration data |
|
154 * |
|
155 * Copies data from user space (possibly a *.rbf) to the CCAT FPGA's |
|
156 * configuration flash to user space. |
|
157 * |
|
158 * Return: the number of bytes written, or 0 if flash end is reached |
|
159 */ |
|
160 |
|
161 static ssize_t ccat_update_write(struct file *const f, const char __user * buf, |
|
162 size_t len, loff_t * off) |
|
163 { |
|
164 struct update_buffer *const update = f->private_data; |
|
165 if (*off + len > sizeof(update->data)) |
|
166 return 0; |
|
167 |
|
168 copy_from_user(update->data + *off, buf, len); |
|
169 *off += len; |
|
170 update->size = *off; |
|
171 return len; |
|
172 } |
|
173 |
|
174 static struct file_operations update_ops = { |
|
175 .owner = THIS_MODULE, |
|
176 .open = ccat_update_open, |
|
177 .release = ccat_update_release, |
|
178 .read = ccat_update_read, |
|
179 .write = ccat_update_write, |
|
180 }; |
|
181 |
|
182 /** |
|
183 * __ccat_update_cmd() - Helper to issue a FPGA flash command |
|
184 * @ioaddr: address of the CCAT Update function in PCI config space |
|
185 * @cmd: the command identifier |
|
186 * @clocks: the number of clocks associated with the specified command |
|
187 * |
|
188 * no write memory barrier is called and the busy flag is not evaluated |
|
189 */ |
|
190 static inline void __ccat_update_cmd(void __iomem * const ioaddr, uint8_t cmd, |
|
191 uint16_t clocks) |
|
192 { |
|
193 iowrite8((0xff00 & clocks) >> 8, ioaddr); |
|
194 iowrite8(0x00ff & clocks, ioaddr + 0x8); |
|
195 iowrite8(cmd, ioaddr + 0x10); |
|
196 } |
|
197 |
|
198 /** |
|
199 * ccat_update_cmd() - Helper to issue a FPGA flash command |
|
200 * @ioaddr: address of the CCAT Update function in PCI config space |
|
201 * @cmd: the command identifier |
|
202 * @clocks: the number of clocks associated with the specified command |
|
203 * |
|
204 * Triggers a full flash command cycle with write memory barrier and |
|
205 * command activate. This call blocks until the busy flag is reset. |
|
206 */ |
|
207 static inline void ccat_update_cmd(void __iomem * const ioaddr, uint8_t cmd, |
|
208 uint16_t clocks) |
|
209 { |
|
210 __ccat_update_cmd(ioaddr, cmd, clocks); |
|
211 wmb(); |
|
212 iowrite8(0xff, ioaddr + 0x7f8); |
|
213 wait_until_busy_reset(ioaddr); |
|
214 } |
|
215 |
|
216 /** |
|
217 * ccat_update_cmd_addr() - Helper to issue a FPGA flash command with address parameter |
|
218 * @ioaddr: address of the CCAT Update function in PCI config space |
|
219 * @cmd: the command identifier |
|
220 * @clocks: the number of clocks associated with the specified command |
|
221 * @addr: 24 bit address associated with the specified command |
|
222 * |
|
223 * Triggers a full flash command cycle with write memory barrier and |
|
224 * command activate. This call blocks until the busy flag is reset. |
|
225 */ |
|
226 static inline void ccat_update_cmd_addr(void __iomem * const ioaddr, |
|
227 uint8_t cmd, uint16_t clocks, |
|
228 uint32_t addr) |
|
229 { |
|
230 const uint8_t addr_0 = SWAP_BITS(addr & 0xff); |
|
231 const uint8_t addr_1 = SWAP_BITS((addr & 0xff00) >> 8); |
|
232 const uint8_t addr_2 = SWAP_BITS((addr & 0xff0000) >> 16); |
|
233 __ccat_update_cmd(ioaddr, cmd, clocks); |
|
234 iowrite8(addr_2, ioaddr + 0x18); |
|
235 iowrite8(addr_1, ioaddr + 0x20); |
|
236 iowrite8(addr_0, ioaddr + 0x28); |
|
237 wmb(); |
|
238 iowrite8(0xff, ioaddr + 0x7f8); |
|
239 wait_until_busy_reset(ioaddr); |
|
240 } |
|
241 |
|
242 /** |
|
243 * ccat_get_prom_id() - Read CCAT PROM ID |
|
244 * @ioaddr: address of the CCAT Update function in PCI config space |
|
245 * |
|
246 * Return: the CCAT FPGA's PROM identifier |
|
247 */ |
|
248 uint8_t ccat_get_prom_id(void __iomem * const ioaddr) |
|
249 { |
|
250 ccat_update_cmd(ioaddr, CCAT_GET_PROM_ID); |
|
251 return ioread8(ioaddr + 0x38); |
|
252 } |
|
253 |
|
254 /** |
|
255 * ccat_get_status() - Read CCAT Update status |
|
256 * @ioaddr: address of the CCAT Update function in PCI config space |
|
257 * |
|
258 * Return: the current status of the CCAT Update function |
|
259 */ |
|
260 static uint8_t ccat_get_status(void __iomem * const ioaddr) |
|
261 { |
|
262 ccat_update_cmd(ioaddr, CCAT_READ_STATUS); |
|
263 return ioread8(ioaddr + 0x20); |
|
264 } |
|
265 |
|
266 /** |
|
267 * ccat_wait_status_cleared() - wait until CCAT status is cleared |
|
268 * @ioaddr: address of the CCAT Update function in PCI config space |
|
269 * |
|
270 * Blocks until bit 7 of the CCAT Update status is reset |
|
271 */ |
|
272 |
|
273 static void ccat_wait_status_cleared(void __iomem * const ioaddr) |
|
274 { |
|
275 uint8_t status; |
|
276 do { |
|
277 status = ccat_get_status(ioaddr); |
|
278 } while (status & (1 << 7)); |
|
279 } |
|
280 |
|
281 /** |
|
282 * ccat_read_flash_block() - Read a block of CCAT configuration data from flash |
|
283 * @ioaddr: address of the CCAT Update function in PCI config space |
|
284 * @addr: 24 bit address of the block to read |
|
285 * @len: number of bytes to read from this block, len <= CCAT_DATA_BLOCK_SIZE |
|
286 * @buf: output buffer in user space |
|
287 * |
|
288 * Copies one block of configuration data from the CCAT FPGA's flash to |
|
289 * the user space buffer. |
|
290 * Note that the size of the FPGA's firmware is not known exactly so it |
|
291 * is very possible that the overall buffer ends with a lot of 0xff. |
|
292 * |
|
293 * Return: the number of bytes copied |
|
294 */ |
|
295 static int ccat_read_flash_block(void __iomem * const ioaddr, |
|
296 const uint32_t addr, const uint16_t len, |
|
297 char __user * const buf) |
|
298 { |
|
299 uint16_t i; |
|
300 const uint16_t clocks = 8 * len; |
|
301 ccat_update_cmd_addr(ioaddr, CCAT_READ_FLASH + clocks, addr); |
|
302 for (i = 0; i < len; i++) { |
|
303 put_user(ioread8(ioaddr + CCAT_DATA_IN_4 + 8 * i), buf + i); |
|
304 } |
|
305 return len; |
|
306 } |
|
307 |
|
308 /** |
|
309 * ccat_read_flash() - Read a chunk of CCAT configuration data from flash |
|
310 * @ioaddr: address of the CCAT Update function in PCI config space |
|
311 * @buf: output buffer in user space |
|
312 * @len: number of bytes to read |
|
313 * @off: offset in the configuration data |
|
314 * |
|
315 * Copies multiple blocks of configuration data from the CCAT FPGA's |
|
316 * flash to the user space buffer. |
|
317 * |
|
318 * Return: the number of bytes copied |
|
319 */ |
|
320 static int ccat_read_flash(void __iomem * const ioaddr, char __user * buf, |
|
321 uint32_t len, loff_t * off) |
|
322 { |
|
323 const loff_t start = *off; |
|
324 while (len > CCAT_DATA_BLOCK_SIZE) { |
|
325 *off += |
|
326 ccat_read_flash_block(ioaddr, *off, CCAT_DATA_BLOCK_SIZE, |
|
327 buf); |
|
328 buf += CCAT_DATA_BLOCK_SIZE; |
|
329 len -= CCAT_DATA_BLOCK_SIZE; |
|
330 } |
|
331 *off += ccat_read_flash_block(ioaddr, *off, len, buf); |
|
332 return *off - start; |
|
333 } |
|
334 |
|
335 /** |
|
336 * ccat_write_flash_block() - Write a block of CCAT configuration data to flash |
|
337 * @ioaddr: address of the CCAT Update function in PCI config space |
|
338 * @addr: 24 bit start address in the CCAT FPGA's flash |
|
339 * @len: number of bytes to write in this block, len <= CCAT_WRITE_BLOCK_SIZE |
|
340 * @buf: input buffer |
|
341 * |
|
342 * Copies one block of configuration data to the CCAT FPGA's flash |
|
343 * |
|
344 * Return: the number of bytes copied |
|
345 */ |
|
346 static int ccat_write_flash_block(void __iomem * const ioaddr, |
|
347 const uint32_t addr, const uint16_t len, |
|
348 const char *const buf) |
|
349 { |
|
350 const uint16_t clocks = 8 * len; |
|
351 uint16_t i; |
|
352 ccat_update_cmd(ioaddr, CCAT_WRITE_ENABLE); |
|
353 for (i = 0; i < len; i++) { |
|
354 iowrite8(buf[i], ioaddr + CCAT_DATA_OUT_4 + 8 * i); |
|
355 } |
|
356 ccat_update_cmd_addr(ioaddr, CCAT_WRITE_FLASH + clocks, addr); |
|
357 ccat_wait_status_cleared(ioaddr); |
|
358 return len; |
|
359 } |
|
360 |
|
361 /** |
|
362 * ccat_write_flash() - Write a new CCAT configuration to FPGA's flash |
|
363 * @update: a CCAT Update buffer containing the new FPGA configuration |
|
364 */ |
|
365 static void ccat_write_flash(const struct update_buffer *const update) |
|
366 { |
|
367 const char *buf = update->data; |
|
368 uint32_t off = 0; |
|
369 size_t len = update->size; |
|
370 while (len > CCAT_WRITE_BLOCK_SIZE) { |
|
371 ccat_write_flash_block(update->update->ioaddr, off, |
|
372 (uint16_t) CCAT_WRITE_BLOCK_SIZE, buf); |
|
373 off += CCAT_WRITE_BLOCK_SIZE; |
|
374 buf += CCAT_WRITE_BLOCK_SIZE; |
|
375 len -= CCAT_WRITE_BLOCK_SIZE; |
|
376 } |
|
377 ccat_write_flash_block(update->update->ioaddr, off, (uint16_t) len, |
|
378 buf); |
|
379 } |
|
380 |
|
381 /** |
|
382 * ccat_update_init() - Initialize the CCAT Update function |
|
383 */ |
|
384 struct ccat_update *ccat_update_init(const struct ccat_device *const ccatdev, |
|
385 void __iomem * const addr) |
|
386 { |
|
387 struct ccat_update *const update = kzalloc(sizeof(*update), GFP_KERNEL); |
|
388 if (!update) { |
|
389 return NULL; |
|
390 } |
|
391 kref_init(&update->refcount); |
|
392 update->ioaddr = ccatdev->bar[0].ioaddr + ioread32(addr + 0x8); |
|
393 memcpy_fromio(&update->info, addr, sizeof(update->info)); |
|
394 print_update_info(&update->info, update->ioaddr); |
|
395 |
|
396 if (0x00 != update->info.nRevision) { |
|
397 pr_warn("CCAT Update rev. %d not supported\n", |
|
398 update->info.nRevision); |
|
399 goto cleanup; |
|
400 } |
|
401 |
|
402 if (alloc_chrdev_region(&update->dev, 0, 1, DRV_NAME)) { |
|
403 pr_warn("alloc_chrdev_region() failed\n"); |
|
404 goto cleanup; |
|
405 } |
|
406 |
|
407 update->class = class_create(THIS_MODULE, "ccat_update"); |
|
408 if (NULL == update->class) { |
|
409 pr_warn("Create device class failed\n"); |
|
410 goto cleanup; |
|
411 } |
|
412 |
|
413 if (NULL == |
|
414 device_create(update->class, NULL, update->dev, NULL, |
|
415 "ccat_update")) { |
|
416 pr_warn("device_create() failed\n"); |
|
417 goto cleanup; |
|
418 } |
|
419 |
|
420 cdev_init(&update->cdev, &update_ops); |
|
421 update->cdev.owner = THIS_MODULE; |
|
422 update->cdev.ops = &update_ops; |
|
423 if (cdev_add(&update->cdev, update->dev, 1)) { |
|
424 pr_warn("add update device failed\n"); |
|
425 goto cleanup; |
|
426 } |
|
427 return update; |
|
428 cleanup: |
|
429 kref_put(&update->refcount, ccat_update_destroy); |
|
430 return NULL; |
|
431 } |
|
432 |
|
433 /** |
|
434 * ccat_update_destroy() - Cleanup the CCAT Update function |
|
435 * @ref: pointer to a struct kref embedded into a struct ccat_update, which we intend to destroy |
|
436 * |
|
437 * Retrieves the parent struct ccat_update and destroys it. |
|
438 */ |
|
439 static void ccat_update_destroy(struct kref *ref) |
|
440 { |
|
441 struct ccat_update *update = |
|
442 container_of(ref, struct ccat_update, refcount); |
|
443 cdev_del(&update->cdev); |
|
444 device_destroy(update->class, update->dev); |
|
445 class_destroy(update->class); |
|
446 unregister_chrdev_region(update->dev, 1); |
|
447 kfree(update); |
|
448 pr_debug("%s(): done\n", __FUNCTION__); |
|
449 } |
|
450 |
|
451 /** |
|
452 * ccat_update_remove() - Prepare the CCAT Update function for removal |
|
453 */ |
|
454 void ccat_update_remove(struct ccat_update *update) |
|
455 { |
|
456 kref_put(&update->refcount, ccat_update_destroy); |
|
457 pr_debug("%s(): done\n", __FUNCTION__); |
|
458 } |