p@2550: /** p@2550: Network Driver for Beckhoff CCAT communication controller p@2550: Copyright (C) 2014 Beckhoff Automation GmbH p@2550: Author: Patrick Bruenn p@2550: p@2550: This program is free software; you can redistribute it and/or modify p@2550: it under the terms of the GNU General Public License as published by p@2550: the Free Software Foundation; either version 2 of the License, or p@2550: (at your option) any later version. p@2550: p@2550: This program is distributed in the hope that it will be useful, p@2550: but WITHOUT ANY WARRANTY; without even the implied warranty of p@2550: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the p@2550: GNU General Public License for more details. p@2550: p@2550: You should have received a copy of the GNU General Public License along p@2550: with this program; if not, write to the Free Software Foundation, Inc., p@2550: 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. p@2550: */ p@2550: p@2550: #include p@2550: #include p@2550: #include p@2550: #include p@2550: #include "module.h" p@2636: p@2636: #define CCAT_DEVICES_MAX 5 p@2550: #define CCAT_DATA_IN_4 0x038 p@2550: #define CCAT_DATA_IN_N 0x7F0 p@2550: #define CCAT_DATA_OUT_4 0x030 p@2550: #define CCAT_DATA_BLOCK_SIZE (size_t)((CCAT_DATA_IN_N - CCAT_DATA_IN_4)/8) p@2550: #define CCAT_WRITE_BLOCK_SIZE 128 p@2550: #define CCAT_FLASH_SIZE (size_t)0xE0000 p@2550: p@2550: /** FUNCTION_NAME CMD, CLOCKS */ p@2550: #define CCAT_BULK_ERASE 0xE3, 8 p@2550: #define CCAT_GET_PROM_ID 0xD5, 40 p@2550: #define CCAT_READ_FLASH 0xC0, 32 p@2550: #define CCAT_READ_STATUS 0xA0, 16 p@2550: #define CCAT_WRITE_ENABLE 0x60, 8 p@2550: #define CCAT_WRITE_FLASH 0x40, 32 p@2550: p@2550: /* from http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith32Bits */ p@2550: #define SWAP_BITS(B) \ p@2550: ((((B) * 0x0802LU & 0x22110LU) | ((B) * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16) p@2550: p@2550: /** p@2550: * wait_until_busy_reset() - wait until the busy flag was reset p@2550: * @ioaddr: address of the CCAT Update function in PCI config space p@2550: */ p@2550: static inline void wait_until_busy_reset(void __iomem * const ioaddr) p@2550: { p@2550: wmb(); p@2550: while (ioread8(ioaddr + 1)) { p@2550: schedule(); p@2550: } p@2550: } p@2550: p@2571: /** p@2571: * __ccat_update_cmd() - Helper to issue a FPGA flash command p@2571: * @ioaddr: address of the CCAT Update function in PCI config space p@2571: * @cmd: the command identifier p@2571: * @clocks: the number of clocks associated with the specified command p@2571: * p@2571: * no write memory barrier is called and the busy flag is not evaluated p@2571: */ p@2571: static inline void __ccat_update_cmd(void __iomem * const ioaddr, u8 cmd, p@2571: u16 clocks) p@2571: { p@2571: iowrite8((0xff00 & clocks) >> 8, ioaddr); p@2571: iowrite8(0x00ff & clocks, ioaddr + 0x8); p@2571: iowrite8(cmd, ioaddr + 0x10); p@2571: } p@2571: p@2571: /** p@2571: * ccat_update_cmd() - Helper to issue a FPGA flash command p@2571: * @ioaddr: address of the CCAT Update function in PCI config space p@2571: * @cmd: the command identifier p@2571: * @clocks: the number of clocks associated with the specified command p@2571: * p@2571: * Triggers a full flash command cycle with write memory barrier and p@2571: * command activate. This call blocks until the busy flag is reset. p@2571: */ p@2571: static inline void ccat_update_cmd(void __iomem * const ioaddr, u8 cmd, p@2571: u16 clocks) p@2571: { p@2571: __ccat_update_cmd(ioaddr, cmd, clocks); p@2571: wmb(); p@2571: iowrite8(0xff, ioaddr + 0x7f8); p@2571: wait_until_busy_reset(ioaddr); p@2571: } p@2571: p@2571: /** p@2571: * ccat_update_cmd_addr() - Helper to issue a FPGA flash command with address parameter p@2571: * @ioaddr: address of the CCAT Update function in PCI config space p@2571: * @cmd: the command identifier p@2571: * @clocks: the number of clocks associated with the specified command p@2571: * @addr: 24 bit address associated with the specified command p@2571: * p@2571: * Triggers a full flash command cycle with write memory barrier and p@2571: * command activate. This call blocks until the busy flag is reset. p@2571: */ p@2571: static inline void ccat_update_cmd_addr(void __iomem * const ioaddr, p@2571: u8 cmd, u16 clocks, u32 addr) p@2571: { p@2571: const u8 addr_0 = SWAP_BITS(addr & 0xff); p@2571: const u8 addr_1 = SWAP_BITS((addr & 0xff00) >> 8); p@2571: const u8 addr_2 = SWAP_BITS((addr & 0xff0000) >> 16); p@2571: p@2571: __ccat_update_cmd(ioaddr, cmd, clocks); p@2571: iowrite8(addr_2, ioaddr + 0x18); p@2571: iowrite8(addr_1, ioaddr + 0x20); p@2571: iowrite8(addr_0, ioaddr + 0x28); p@2571: wmb(); p@2571: iowrite8(0xff, ioaddr + 0x7f8); p@2571: wait_until_busy_reset(ioaddr); p@2571: } p@2571: p@2571: /** p@2571: * ccat_get_status() - Read CCAT Update status p@2571: * @ioaddr: address of the CCAT Update function in PCI config space p@2571: * p@2571: * Return: the current status of the CCAT Update function p@2571: */ p@2571: static u8 ccat_get_status(void __iomem * const ioaddr) p@2571: { p@2571: ccat_update_cmd(ioaddr, CCAT_READ_STATUS); p@2571: return ioread8(ioaddr + 0x20); p@2571: } p@2571: p@2571: /** p@2571: * ccat_read_flash_block() - Read a block of CCAT configuration data from flash p@2571: * @ioaddr: address of the CCAT Update function in PCI config space p@2571: * @addr: 24 bit address of the block to read p@2571: * @len: number of bytes to read from this block, len <= CCAT_DATA_BLOCK_SIZE p@2571: * @buf: output buffer in user space p@2571: * p@2571: * Copies one block of configuration data from the CCAT FPGA's flash to p@2571: * the user space buffer. p@2571: * Note that the size of the FPGA's firmware is not known exactly so it p@2571: * is very possible that the overall buffer ends with a lot of 0xff. p@2571: * p@2571: * Return: the number of bytes copied p@2571: */ p@2571: static int ccat_read_flash_block(void __iomem * const ioaddr, p@2571: const u32 addr, const u16 len, p@2571: char __user * const buf) p@2571: { p@2571: u16 i; p@2571: const u16 clocks = 8 * len; p@2571: p@2571: ccat_update_cmd_addr(ioaddr, CCAT_READ_FLASH + clocks, addr); p@2571: for (i = 0; i < len; i++) { p@2571: put_user(ioread8(ioaddr + CCAT_DATA_IN_4 + 8 * i), buf + i); p@2571: } p@2571: return len; p@2571: } p@2571: p@2571: /** p@2571: * ccat_read_flash() - Read a chunk of CCAT configuration data from flash p@2571: * @ioaddr: address of the CCAT Update function in PCI config space p@2571: * @buf: output buffer in user space p@2571: * @len: number of bytes to read p@2571: * @off: offset in the configuration data p@2571: * p@2571: * Copies multiple blocks of configuration data from the CCAT FPGA's p@2571: * flash to the user space buffer. p@2571: * p@2571: * Return: the number of bytes copied p@2571: */ p@2571: static int ccat_read_flash(void __iomem * const ioaddr, char __user * buf, p@2571: u32 len, loff_t * off) p@2571: { p@2571: const loff_t start = *off; p@2571: p@2571: while (len > CCAT_DATA_BLOCK_SIZE) { p@2571: *off += p@2571: ccat_read_flash_block(ioaddr, *off, CCAT_DATA_BLOCK_SIZE, p@2571: buf); p@2571: buf += CCAT_DATA_BLOCK_SIZE; p@2571: len -= CCAT_DATA_BLOCK_SIZE; p@2571: } p@2571: *off += ccat_read_flash_block(ioaddr, *off, len, buf); p@2571: return *off - start; p@2571: } p@2571: p@2571: /** p@2571: * ccat_wait_status_cleared() - wait until CCAT status is cleared p@2571: * @ioaddr: address of the CCAT Update function in PCI config space p@2571: * p@2571: * Blocks until bit 7 of the CCAT Update status is reset p@2571: */ p@2571: static void ccat_wait_status_cleared(void __iomem * const ioaddr) p@2571: { p@2571: u8 status; p@2571: p@2571: do { p@2571: status = ccat_get_status(ioaddr); p@2571: } while (status & (1 << 7)); p@2571: } p@2571: p@2571: /** p@2571: * ccat_write_flash_block() - Write a block of CCAT configuration data to flash p@2571: * @ioaddr: address of the CCAT Update function in PCI config space p@2571: * @addr: 24 bit start address in the CCAT FPGA's flash p@2571: * @len: number of bytes to write in this block, len <= CCAT_WRITE_BLOCK_SIZE p@2571: * @buf: input buffer p@2571: * p@2571: * Copies one block of configuration data to the CCAT FPGA's flash p@2571: * p@2571: * Return: the number of bytes copied p@2571: */ p@2571: static int ccat_write_flash_block(void __iomem * const ioaddr, p@2571: const u32 addr, const u16 len, p@2571: const char *const buf) p@2571: { p@2571: const u16 clocks = 8 * len; p@2571: u16 i; p@2571: p@2571: ccat_update_cmd(ioaddr, CCAT_WRITE_ENABLE); p@2571: for (i = 0; i < len; i++) { p@2571: iowrite8(buf[i], ioaddr + CCAT_DATA_OUT_4 + 8 * i); p@2571: } p@2571: ccat_update_cmd_addr(ioaddr, CCAT_WRITE_FLASH + clocks, addr); p@2571: ccat_wait_status_cleared(ioaddr); p@2571: return len; p@2571: } p@2571: p@2571: /** p@2571: * ccat_write_flash() - Write a new CCAT configuration to FPGA's flash p@2571: * @update: a CCAT Update buffer containing the new FPGA configuration p@2571: */ p@2636: static void ccat_write_flash(const struct cdev_buffer *const buffer) p@2636: { p@2636: const char *buf = buffer->data; p@2571: u32 off = 0; p@2636: size_t len = buffer->size; p@2571: p@2571: while (len > CCAT_WRITE_BLOCK_SIZE) { p@2636: ccat_write_flash_block(buffer->ccdev->ioaddr, off, p@2571: (u16) CCAT_WRITE_BLOCK_SIZE, buf); p@2571: off += CCAT_WRITE_BLOCK_SIZE; p@2571: buf += CCAT_WRITE_BLOCK_SIZE; p@2571: len -= CCAT_WRITE_BLOCK_SIZE; p@2571: } p@2636: ccat_write_flash_block(buffer->ccdev->ioaddr, off, (u16) len, buf); p@2550: } p@2550: p@2550: static int ccat_update_release(struct inode *const i, struct file *const f) p@2550: { p@2636: const struct cdev_buffer *const buf = f->private_data; p@2636: void __iomem *ioaddr = buf->ccdev->ioaddr; p@2567: p@2550: if (buf->size > 0) { p@2636: ccat_update_cmd(ioaddr, CCAT_WRITE_ENABLE); p@2636: ccat_update_cmd(ioaddr, CCAT_BULK_ERASE); p@2636: ccat_wait_status_cleared(ioaddr); p@2550: ccat_write_flash(buf); p@2550: } p@2636: return ccat_cdev_release(i, f); p@2550: } p@2550: p@2550: /** p@2550: * ccat_update_read() - Read CCAT configuration data from flash p@2550: * @f: file handle previously initialized with ccat_update_open() p@2550: * @buf: buffer in user space provided for our data p@2550: * @len: length of the user space buffer p@2550: * @off: current offset of our file operation p@2550: * p@2550: * Copies data from the CCAT FPGA's configuration flash to user space. p@2550: * Note that the size of the FPGA's firmware is not known exactly so it p@2550: * is very possible that the overall buffer ends with a lot of 0xff. p@2550: * p@2550: * Return: the number of bytes written, or 0 if EOF reached p@2550: */ p@2550: static ssize_t ccat_update_read(struct file *const f, char __user * buf, p@2550: size_t len, loff_t * off) p@2550: { p@2636: struct cdev_buffer *buffer = f->private_data; p@2636: const size_t iosize = buffer->ccdev->iosize; p@2636: p@2636: if (*off >= iosize) { p@2550: return 0; p@2550: } p@2636: p@2636: len = min(len, (size_t) (iosize - *off)); p@2636: p@2636: return ccat_read_flash(buffer->ccdev->ioaddr, buf, len, off); p@2550: } p@2550: p@2550: /** p@2550: * ccat_update_write() - Write data to the CCAT FPGA's configuration flash p@2550: * @f: file handle previously initialized with ccat_update_open() p@2550: * @buf: buffer in user space providing the new configuration data (from *.rbf) p@2550: * @len: length of the user space buffer p@2550: * @off: current offset in the configuration data p@2550: * p@2550: * Copies data from user space (possibly a *.rbf) to the CCAT FPGA's p@2636: * configuration flash. p@2550: * p@2550: * Return: the number of bytes written, or 0 if flash end is reached p@2550: */ p@2550: static ssize_t ccat_update_write(struct file *const f, const char __user * buf, p@2550: size_t len, loff_t * off) p@2550: { p@2636: struct cdev_buffer *const buffer = f->private_data; p@2636: p@2636: if (*off + len > buffer->ccdev->iosize) { p@2550: return 0; p@2636: } p@2636: p@2636: if (copy_from_user(buffer->data + *off, buf, len)) { fp@2559: return -EFAULT; fp@2559: } fp@2559: p@2550: *off += len; p@2636: buffer->size = *off; p@2550: return len; p@2550: } p@2550: p@2636: static struct ccat_cdev dev_table[CCAT_DEVICES_MAX]; p@2636: static struct ccat_class cdev_class = { p@2636: .count = CCAT_DEVICES_MAX, p@2636: .devices = dev_table, p@2636: .name = "ccat_update", p@2636: .fops = { p@2636: .owner = THIS_MODULE, p@2636: .open = ccat_cdev_open, p@2636: .release = ccat_update_release, p@2636: .read = ccat_update_read, p@2636: .write = ccat_update_write, p@2636: }, p@2550: }; p@2550: p@2636: static int ccat_update_probe(struct ccat_function *func) p@2636: { p@2636: static const u16 SUPPORTED_REVISION = 0x00; p@2636: p@2636: if (SUPPORTED_REVISION != func->info.rev) { p@2636: pr_warn("CCAT Update rev. %d not supported\n", func->info.rev); p@2636: return -ENODEV; p@2636: } p@2636: return ccat_cdev_probe(func, &cdev_class, CCAT_FLASH_SIZE); p@2636: } p@2636: p@2636: struct ccat_driver update_driver = { p@2636: .type = CCATINFO_EPCS_PROM, p@2636: .probe = ccat_update_probe, p@2636: .remove = ccat_cdev_remove, p@2636: .cdev_class = &cdev_class, p@2636: };