fp@2589: /**
fp@2589:     Network Driver for Beckhoff CCAT communication controller
fp@2589:     Copyright (C) 2014  Beckhoff Automation GmbH
fp@2589:     Author: Patrick Bruenn <p.bruenn@beckhoff.com>
fp@2589: 
fp@2589:     This program is free software; you can redistribute it and/or modify
fp@2589:     it under the terms of the GNU General Public License as published by
fp@2589:     the Free Software Foundation; either version 2 of the License, or
fp@2589:     (at your option) any later version.
fp@2589: 
fp@2589:     This program is distributed in the hope that it will be useful,
fp@2589:     but WITHOUT ANY WARRANTY; without even the implied warranty of
fp@2589:     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
fp@2589:     GNU General Public License for more details.
fp@2589: 
fp@2589:     You should have received a copy of the GNU General Public License along
fp@2589:     with this program; if not, write to the Free Software Foundation, Inc.,
fp@2589:     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
fp@2589: */
fp@2589: 
fp@2589: #include <linux/fs.h>
fp@2589: #include <linux/kernel.h>
fp@2589: #include <linux/module.h>
fp@2589: #include <linux/sched.h>
fp@2589: #include <linux/uaccess.h>
fp@2589: #include "module.h"
fp@2589: #include "update.h"
fp@2589: 
fp@2589: #define CCAT_DATA_IN_4 0x038
fp@2589: #define CCAT_DATA_IN_N 0x7F0
fp@2589: #define CCAT_DATA_OUT_4 0x030
fp@2589: #define CCAT_DATA_BLOCK_SIZE (size_t)((CCAT_DATA_IN_N - CCAT_DATA_IN_4)/8)
fp@2589: #define CCAT_WRITE_BLOCK_SIZE 128
fp@2589: #define CCAT_FLASH_SIZE (size_t)0xE0000
fp@2589: 
fp@2589: /**     FUNCTION_NAME            CMD,  CLOCKS          */
fp@2589: #define CCAT_BULK_ERASE          0xE3, 8
fp@2589: #define CCAT_GET_PROM_ID         0xD5, 40
fp@2589: #define CCAT_READ_FLASH          0xC0, 32
fp@2589: #define CCAT_READ_STATUS         0xA0, 16
fp@2589: #define CCAT_WRITE_ENABLE        0x60, 8
fp@2589: #define CCAT_WRITE_FLASH         0x40, 32
fp@2589: 
fp@2589: /* from http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith32Bits */
fp@2589: #define SWAP_BITS(B) \
fp@2589: 	((((B) * 0x0802LU & 0x22110LU) | ((B) * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16)
fp@2589: 
fp@2589: /**
fp@2589:  * struct update_buffer - keep track of a CCAT FPGA update
fp@2589:  * @update: pointer to a valid ccat_update object
fp@2589:  * @data: buffer used for write operations
fp@2589:  * @size: number of bytes written to the data buffer, if 0 on ccat_update_release() no data will be written to FPGA
fp@2589:  */
fp@2589: struct update_buffer {
fp@2589: 	struct ccat_update *update;
fp@2589: 	char data[CCAT_FLASH_SIZE];
fp@2589: 	size_t size;
fp@2589: };
fp@2589: 
fp@2589: /**
fp@2589:  * wait_until_busy_reset() - wait until the busy flag was reset
fp@2589:  * @ioaddr: address of the CCAT Update function in PCI config space
fp@2589:  */
fp@2589: static inline void wait_until_busy_reset(void __iomem * const ioaddr)
fp@2589: {
fp@2589: 	wmb();
fp@2589: 	while (ioread8(ioaddr + 1)) {
fp@2589: 		schedule();
fp@2589: 	}
fp@2589: }
fp@2589: 
fp@2589: /**
fp@2589:  * __ccat_update_cmd() - Helper to issue a FPGA flash command
fp@2589:  * @ioaddr: address of the CCAT Update function in PCI config space
fp@2589:  * @cmd: the command identifier
fp@2589:  * @clocks: the number of clocks associated with the specified command
fp@2589:  *
fp@2589:  * no write memory barrier is called and the busy flag is not evaluated
fp@2589:  */
fp@2589: static inline void __ccat_update_cmd(void __iomem * const ioaddr, u8 cmd,
fp@2589: 				     u16 clocks)
fp@2589: {
fp@2589: 	iowrite8((0xff00 & clocks) >> 8, ioaddr);
fp@2589: 	iowrite8(0x00ff & clocks, ioaddr + 0x8);
fp@2589: 	iowrite8(cmd, ioaddr + 0x10);
fp@2589: }
fp@2589: 
fp@2589: /**
fp@2589:  * ccat_update_cmd() - Helper to issue a FPGA flash command
fp@2589:  * @ioaddr: address of the CCAT Update function in PCI config space
fp@2589:  * @cmd: the command identifier
fp@2589:  * @clocks: the number of clocks associated with the specified command
fp@2589:  *
fp@2589:  * Triggers a full flash command cycle with write memory barrier and
fp@2589:  * command activate. This call blocks until the busy flag is reset.
fp@2589:  */
fp@2589: static inline void ccat_update_cmd(void __iomem * const ioaddr, u8 cmd,
fp@2589: 				   u16 clocks)
fp@2589: {
fp@2589: 	__ccat_update_cmd(ioaddr, cmd, clocks);
fp@2589: 	wmb();
fp@2589: 	iowrite8(0xff, ioaddr + 0x7f8);
fp@2589: 	wait_until_busy_reset(ioaddr);
fp@2589: }
fp@2589: 
fp@2589: /**
fp@2589:  * ccat_update_cmd_addr() - Helper to issue a FPGA flash command with address parameter
fp@2589:  * @ioaddr: address of the CCAT Update function in PCI config space
fp@2589:  * @cmd: the command identifier
fp@2589:  * @clocks: the number of clocks associated with the specified command
fp@2589:  * @addr: 24 bit address associated with the specified command
fp@2589:  *
fp@2589:  * Triggers a full flash command cycle with write memory barrier and
fp@2589:  * command activate. This call blocks until the busy flag is reset.
fp@2589:  */
fp@2589: static inline void ccat_update_cmd_addr(void __iomem * const ioaddr,
fp@2589: 					u8 cmd, u16 clocks, u32 addr)
fp@2589: {
fp@2589: 	const u8 addr_0 = SWAP_BITS(addr & 0xff);
fp@2589: 	const u8 addr_1 = SWAP_BITS((addr & 0xff00) >> 8);
fp@2589: 	const u8 addr_2 = SWAP_BITS((addr & 0xff0000) >> 16);
fp@2589: 
fp@2589: 	__ccat_update_cmd(ioaddr, cmd, clocks);
fp@2589: 	iowrite8(addr_2, ioaddr + 0x18);
fp@2589: 	iowrite8(addr_1, ioaddr + 0x20);
fp@2589: 	iowrite8(addr_0, ioaddr + 0x28);
fp@2589: 	wmb();
fp@2589: 	iowrite8(0xff, ioaddr + 0x7f8);
fp@2589: 	wait_until_busy_reset(ioaddr);
fp@2589: }
fp@2589: 
fp@2589: /**
fp@2589:  * ccat_get_status() - Read CCAT Update status
fp@2589:  * @ioaddr: address of the CCAT Update function in PCI config space
fp@2589:  *
fp@2589:  * Return: the current status of the CCAT Update function
fp@2589:  */
fp@2589: static u8 ccat_get_status(void __iomem * const ioaddr)
fp@2589: {
fp@2589: 	ccat_update_cmd(ioaddr, CCAT_READ_STATUS);
fp@2589: 	return ioread8(ioaddr + 0x20);
fp@2589: }
fp@2589: 
fp@2589: /**
fp@2589:  * ccat_read_flash_block() - Read a block of CCAT configuration data from flash
fp@2589:  * @ioaddr: address of the CCAT Update function in PCI config space
fp@2589:  * @addr: 24 bit address of the block to read
fp@2589:  * @len: number of bytes to read from this block, len <= CCAT_DATA_BLOCK_SIZE
fp@2589:  * @buf: output buffer in user space
fp@2589:  *
fp@2589:  * Copies one block of configuration data from the CCAT FPGA's flash to
fp@2589:  * the user space buffer.
fp@2589:  * Note that the size of the FPGA's firmware is not known exactly so it
fp@2589:  * is very possible that the overall buffer ends with a lot of 0xff.
fp@2589:  *
fp@2589:  * Return: the number of bytes copied
fp@2589:  */
fp@2589: static int ccat_read_flash_block(void __iomem * const ioaddr,
fp@2589: 				 const u32 addr, const u16 len,
fp@2589: 				 char __user * const buf)
fp@2589: {
fp@2589: 	u16 i;
fp@2589: 	const u16 clocks = 8 * len;
fp@2589: 
fp@2589: 	ccat_update_cmd_addr(ioaddr, CCAT_READ_FLASH + clocks, addr);
fp@2589: 	for (i = 0; i < len; i++) {
fp@2589: 		put_user(ioread8(ioaddr + CCAT_DATA_IN_4 + 8 * i), buf + i);
fp@2589: 	}
fp@2589: 	return len;
fp@2589: }
fp@2589: 
fp@2589: /**
fp@2589:  * ccat_read_flash() - Read a chunk of CCAT configuration data from flash
fp@2589:  * @ioaddr: address of the CCAT Update function in PCI config space
fp@2589:  * @buf: output buffer in user space
fp@2589:  * @len: number of bytes to read
fp@2589:  * @off: offset in the configuration data
fp@2589:  *
fp@2589:  * Copies multiple blocks of configuration data from the CCAT FPGA's
fp@2589:  * flash to the user space buffer.
fp@2589:  *
fp@2589:  * Return: the number of bytes copied
fp@2589:  */
fp@2589: static int ccat_read_flash(void __iomem * const ioaddr, char __user * buf,
fp@2589: 			   u32 len, loff_t * off)
fp@2589: {
fp@2589: 	const loff_t start = *off;
fp@2589: 
fp@2589: 	while (len > CCAT_DATA_BLOCK_SIZE) {
fp@2589: 		*off +=
fp@2589: 		    ccat_read_flash_block(ioaddr, *off, CCAT_DATA_BLOCK_SIZE,
fp@2589: 					  buf);
fp@2589: 		buf += CCAT_DATA_BLOCK_SIZE;
fp@2589: 		len -= CCAT_DATA_BLOCK_SIZE;
fp@2589: 	}
fp@2589: 	*off += ccat_read_flash_block(ioaddr, *off, len, buf);
fp@2589: 	return *off - start;
fp@2589: }
fp@2589: 
fp@2589: /**
fp@2589:  * ccat_wait_status_cleared() - wait until CCAT status is cleared
fp@2589:  * @ioaddr: address of the CCAT Update function in PCI config space
fp@2589:  *
fp@2589:  * Blocks until bit 7 of the CCAT Update status is reset
fp@2589:  */
fp@2589: static void ccat_wait_status_cleared(void __iomem * const ioaddr)
fp@2589: {
fp@2589: 	u8 status;
fp@2589: 
fp@2589: 	do {
fp@2589: 		status = ccat_get_status(ioaddr);
fp@2589: 	} while (status & (1 << 7));
fp@2589: }
fp@2589: 
fp@2589: /**
fp@2589:  * ccat_write_flash_block() - Write a block of CCAT configuration data to flash
fp@2589:  * @ioaddr: address of the CCAT Update function in PCI config space
fp@2589:  * @addr: 24 bit start address in the CCAT FPGA's flash
fp@2589:  * @len: number of bytes to write in this block, len <= CCAT_WRITE_BLOCK_SIZE
fp@2589:  * @buf: input buffer
fp@2589:  *
fp@2589:  * Copies one block of configuration data to the CCAT FPGA's flash
fp@2589:  *
fp@2589:  * Return: the number of bytes copied
fp@2589:  */
fp@2589: static int ccat_write_flash_block(void __iomem * const ioaddr,
fp@2589: 				  const u32 addr, const u16 len,
fp@2589: 				  const char *const buf)
fp@2589: {
fp@2589: 	const u16 clocks = 8 * len;
fp@2589: 	u16 i;
fp@2589: 
fp@2589: 	ccat_update_cmd(ioaddr, CCAT_WRITE_ENABLE);
fp@2589: 	for (i = 0; i < len; i++) {
fp@2589: 		iowrite8(buf[i], ioaddr + CCAT_DATA_OUT_4 + 8 * i);
fp@2589: 	}
fp@2589: 	ccat_update_cmd_addr(ioaddr, CCAT_WRITE_FLASH + clocks, addr);
fp@2589: 	ccat_wait_status_cleared(ioaddr);
fp@2589: 	return len;
fp@2589: }
fp@2589: 
fp@2589: /**
fp@2589:  * ccat_write_flash() - Write a new CCAT configuration to FPGA's flash
fp@2589:  * @update: a CCAT Update buffer containing the new FPGA configuration
fp@2589:  */
fp@2589: static void ccat_write_flash(const struct update_buffer *const update)
fp@2589: {
fp@2589: 	const char *buf = update->data;
fp@2589: 	u32 off = 0;
fp@2589: 	size_t len = update->size;
fp@2589: 
fp@2589: 	while (len > CCAT_WRITE_BLOCK_SIZE) {
fp@2589: 		ccat_write_flash_block(update->update->ioaddr, off,
fp@2589: 				       (u16) CCAT_WRITE_BLOCK_SIZE, buf);
fp@2589: 		off += CCAT_WRITE_BLOCK_SIZE;
fp@2589: 		buf += CCAT_WRITE_BLOCK_SIZE;
fp@2589: 		len -= CCAT_WRITE_BLOCK_SIZE;
fp@2589: 	}
fp@2589: 	ccat_write_flash_block(update->update->ioaddr, off, (u16) len, buf);
fp@2589: }
fp@2589: 
fp@2589: /**
fp@2589:  * ccat_update_destroy() - Cleanup the CCAT Update function
fp@2589:  * @ref: pointer to a struct kref embedded into a struct ccat_update, which we intend to destroy
fp@2589:  *
fp@2589:  * Retrieves the parent struct ccat_update and destroys it.
fp@2589:  */
fp@2589: static void ccat_update_destroy(struct kref *ref)
fp@2589: {
fp@2589: 	struct ccat_update *update =
fp@2589: 	    container_of(ref, struct ccat_update, refcount);
fp@2589: 
fp@2589: 	cdev_del(&update->cdev);
fp@2589: 	device_destroy(update->class, update->dev);
fp@2589: 	class_destroy(update->class);
fp@2589: 	unregister_chrdev_region(update->dev, 1);
fp@2589: 	kfree(update);
fp@2589: 	pr_debug("%s(): done\n", __FUNCTION__);
fp@2589: }
fp@2589: 
fp@2589: static int ccat_update_open(struct inode *const i, struct file *const f)
fp@2589: {
fp@2589: 	struct ccat_update *update =
fp@2589: 	    container_of(i->i_cdev, struct ccat_update, cdev);
fp@2589: 	struct update_buffer *buf;
fp@2589: 
fp@2589: 	kref_get(&update->refcount);
fp@2589: 	if (atomic_read(&update->refcount.refcount) > 2) {
fp@2589: 		kref_put(&update->refcount, ccat_update_destroy);
fp@2589: 		return -EBUSY;
fp@2589: 	}
fp@2589: 
fp@2589: 	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
fp@2589: 	if (!buf) {
fp@2589: 		kref_put(&update->refcount, ccat_update_destroy);
fp@2589: 		return -ENOMEM;
fp@2589: 	}
fp@2589: 
fp@2589: 	buf->update = update;
fp@2589: 	f->private_data = buf;
fp@2589: 	return 0;
fp@2589: }
fp@2589: 
fp@2589: static int ccat_update_release(struct inode *const i, struct file *const f)
fp@2589: {
fp@2589: 	const struct update_buffer *const buf = f->private_data;
fp@2589: 	struct ccat_update *const update = buf->update;
fp@2589: 
fp@2589: 	if (buf->size > 0) {
fp@2589: 		ccat_update_cmd(update->ioaddr, CCAT_WRITE_ENABLE);
fp@2589: 		ccat_update_cmd(update->ioaddr, CCAT_BULK_ERASE);
fp@2589: 		ccat_wait_status_cleared(update->ioaddr);
fp@2589: 		ccat_write_flash(buf);
fp@2589: 	}
fp@2589: 	kfree(f->private_data);
fp@2589: 	kref_put(&update->refcount, ccat_update_destroy);
fp@2589: 	return 0;
fp@2589: }
fp@2589: 
fp@2589: /**
fp@2589:  * ccat_update_read() - Read CCAT configuration data from flash
fp@2589:  * @f: file handle previously initialized with ccat_update_open()
fp@2589:  * @buf: buffer in user space provided for our data
fp@2589:  * @len: length of the user space buffer
fp@2589:  * @off: current offset of our file operation
fp@2589:  *
fp@2589:  * Copies data from the CCAT FPGA's configuration flash to user space.
fp@2589:  * Note that the size of the FPGA's firmware is not known exactly so it
fp@2589:  * is very possible that the overall buffer ends with a lot of 0xff.
fp@2589:  *
fp@2589:  * Return: the number of bytes written, or 0 if EOF reached
fp@2589:  */
fp@2589: static ssize_t ccat_update_read(struct file *const f, char __user * buf,
fp@2589: 				size_t len, loff_t * off)
fp@2589: {
fp@2589: 	struct update_buffer *update = f->private_data;
fp@2589: 
fp@2589: 	if (!buf || !off) {
fp@2589: 		return -EINVAL;
fp@2589: 	}
fp@2589: 	if (*off >= CCAT_FLASH_SIZE) {
fp@2589: 		return 0;
fp@2589: 	}
fp@2589: 	if (*off + len >= CCAT_FLASH_SIZE) {
fp@2589: 		len = CCAT_FLASH_SIZE - *off;
fp@2589: 	}
fp@2589: 	return ccat_read_flash(update->update->ioaddr, buf, len, off);
fp@2589: }
fp@2589: 
fp@2589: /**
fp@2589:  * ccat_update_write() - Write data to the CCAT FPGA's configuration flash
fp@2589:  * @f: file handle previously initialized with ccat_update_open()
fp@2589:  * @buf: buffer in user space providing the new configuration data (from *.rbf)
fp@2589:  * @len: length of the user space buffer
fp@2589:  * @off: current offset in the configuration data
fp@2589:  *
fp@2589:  * Copies data from user space (possibly a *.rbf) to the CCAT FPGA's
fp@2589:  * configuration flash to user space.
fp@2589:  *
fp@2589:  * Return: the number of bytes written, or 0 if flash end is reached
fp@2589:  */
fp@2589: 
fp@2589: static ssize_t ccat_update_write(struct file *const f, const char __user * buf,
fp@2589: 				 size_t len, loff_t * off)
fp@2589: {
fp@2589: 	struct update_buffer *const update = f->private_data;
fp@2589: 
fp@2589: 	if (*off + len > sizeof(update->data))
fp@2589: 		return 0;
fp@2589: 
fp@2589: 	if (copy_from_user(update->data + *off, buf, len)) {
fp@2589: 		return -EFAULT;
fp@2589: 	}
fp@2589: 
fp@2589: 	*off += len;
fp@2589: 	update->size = *off;
fp@2589: 	return len;
fp@2589: }
fp@2589: 
fp@2589: static struct file_operations update_ops = {
fp@2589: 	.owner = THIS_MODULE,
fp@2589: 	.open = ccat_update_open,
fp@2589: 	.release = ccat_update_release,
fp@2589: 	.read = ccat_update_read,
fp@2589: 	.write = ccat_update_write,
fp@2589: };
fp@2589: 
fp@2589: /**
fp@2589:  * ccat_get_prom_id() - Read CCAT PROM ID
fp@2589:  * @ioaddr: address of the CCAT Update function in PCI config space
fp@2589:  *
fp@2589:  * Return: the CCAT FPGA's PROM identifier
fp@2589:  */
fp@2589: u8 ccat_get_prom_id(void __iomem * const ioaddr)
fp@2589: {
fp@2589: 	ccat_update_cmd(ioaddr, CCAT_GET_PROM_ID);
fp@2589: 	return ioread8(ioaddr + 0x38);
fp@2589: }
fp@2589: 
fp@2589: /**
fp@2589:  * ccat_update_init() - Initialize the CCAT Update function
fp@2589:  */
fp@2589: struct ccat_update *ccat_update_init(const struct ccat_device *const ccatdev,
fp@2589: 				     void __iomem * const addr)
fp@2589: {
fp@2589: 	struct ccat_update *const update = kzalloc(sizeof(*update), GFP_KERNEL);
fp@2589: 
fp@2589: 	if (!update) {
fp@2589: 		return NULL;
fp@2589: 	}
fp@2589: 	kref_init(&update->refcount);
fp@2589: 	update->ioaddr = ccatdev->bar[0].ioaddr + ioread32(addr + 0x8);
fp@2589: 	memcpy_fromio(&update->info, addr, sizeof(update->info));
fp@2589: 
fp@2589: 	if (0x00 != update->info.rev) {
fp@2589: 		pr_warn("CCAT Update rev. %d not supported\n",
fp@2589: 			update->info.rev);
fp@2589: 		goto cleanup;
fp@2589: 	}
fp@2589: 
fp@2589: 	if (alloc_chrdev_region(&update->dev, 0, 1, KBUILD_MODNAME)) {
fp@2589: 		pr_warn("alloc_chrdev_region() failed\n");
fp@2589: 		goto cleanup;
fp@2589: 	}
fp@2589: 
fp@2589: 	update->class = class_create(THIS_MODULE, "ccat_update");
fp@2589: 	if (NULL == update->class) {
fp@2589: 		pr_warn("Create device class failed\n");
fp@2589: 		goto cleanup;
fp@2589: 	}
fp@2589: 
fp@2589: 	if (NULL ==
fp@2589: 	    device_create(update->class, NULL, update->dev, NULL,
fp@2589: 			  "ccat_update")) {
fp@2589: 		pr_warn("device_create() failed\n");
fp@2589: 		goto cleanup;
fp@2589: 	}
fp@2589: 
fp@2589: 	cdev_init(&update->cdev, &update_ops);
fp@2589: 	update->cdev.owner = THIS_MODULE;
fp@2589: 	update->cdev.ops = &update_ops;
fp@2589: 	if (cdev_add(&update->cdev, update->dev, 1)) {
fp@2589: 		pr_warn("add update device failed\n");
fp@2589: 		goto cleanup;
fp@2589: 	}
fp@2589: 	return update;
fp@2589: cleanup:
fp@2589: 	kref_put(&update->refcount, ccat_update_destroy);
fp@2589: 	return NULL;
fp@2589: }
fp@2589: 
fp@2589: /**
fp@2589:  * ccat_update_remove() - Prepare the CCAT Update function for removal
fp@2589:  */
fp@2589: void ccat_update_remove(struct ccat_update *update)
fp@2589: {
fp@2589: 	kref_put(&update->refcount, ccat_update_destroy);
fp@2589: 	pr_debug("%s(): done\n", __FUNCTION__);
fp@2589: }