lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date:	Wed, 16 May 2012 16:26:07 +0800
From:	<sonic.zhang@...log.com>
To:	Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
	Arnd Bergmann <arnd@...db.de>
CC:	LKML <linux-kernel@...r.kernel.org>,
	<uclinux-dist-devel@...ckfin.uclinux.org>,
	Sonic Zhang <sonic.zhang@...log.com>
Subject: [PATCH] driver: char: bfin_crc: CRC hardware accelerator driver for BF60x family processors.

From: Sonic Zhang <sonic.zhang@...log.com>

The CRC peripheral is a hardware block used to compute the CRC of the block
of data. This is based on a CRC32 engine which computes the CRC value of 32b
data words presented to it. For data words of < 32b in size, it is the
responsibility of the application to pack the data into 32b data units.

This driver defines data strcture crc_info and 4 IOCTL interfaces for applciations.
1) CRC Memory Scan
2) CRC Memory Transfer
3) CRC Data Verify
4) CRC Data Fill

Signed-off-by: Sonic Zhang <sonic.zhang@...log.com>
---
 drivers/char/Kconfig    |    8 +
 drivers/char/Makefile   |    1 +
 drivers/char/bfin_crc.c |  537 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 546 insertions(+), 0 deletions(-)
 create mode 100644 drivers/char/bfin_crc.c

diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index ee94686..69980ea 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -94,6 +94,14 @@ config BFIN_OTP_WRITE_ENABLE
 
 	  If unsure, say N.
 
+config BFIN_CRC
+	tristate "Blackfin hardware CRC driver"
+	depends on BF60x
+	default y
+	help
+	  This is a hardware CRC driver for BF60x. It is added to accelerate
+	  CRC calculation individually or within DMA in user applications.
+
 config PRINTER
 	tristate "Parallel printer support"
 	depends on PARPORT
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 0dc5d7c..500033e 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_VIOTAPE)		+= viotape.o
 obj-$(CONFIG_IBM_BSR)		+= bsr.o
 obj-$(CONFIG_SGI_MBCS)		+= mbcs.o
 obj-$(CONFIG_BFIN_OTP)		+= bfin-otp.o
+obj-$(CONFIG_BFIN_CRC)		+= bfin_crc.o
 
 obj-$(CONFIG_PRINTER)		+= lp.o
 
diff --git a/drivers/char/bfin_crc.c b/drivers/char/bfin_crc.c
new file mode 100644
index 0000000..3f7d1d8
--- /dev/null
+++ b/drivers/char/bfin_crc.c
@@ -0,0 +1,537 @@
+/*
+ * Blackfin On-Chip hardware CRC Driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/timer.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+
+#include <asm/blackfin.h>
+#include <asm/bfin_crc.h>
+#include <asm/dma.h>
+#include <asm/cacheflush.h>
+#include <asm/io.h>
+#include <asm/portmux.h>
+
+#define DRIVER_NAME "bfin-crc"
+
+static LIST_HEAD(bfin_crc_list);
+
+static void bfin_crc_config_dma(unsigned long dma_ch, unsigned char *addr,
+	unsigned long size, unsigned long dma_config, int mod_dir)
+{
+	unsigned int dma_count;
+	unsigned int dma_shift;
+	int dma_mod;
+
+	dma_config |= PSIZE_32;
+
+	if ((unsigned long)addr % 4 == 0) {
+		dma_config |= WDSIZE_32;
+		dma_count = size >> 2;
+		dma_shift = 2;
+	} else if ((unsigned long)addr % 2 == 0) {
+		dma_config |= WDSIZE_16;
+		dma_count = size >> 1;
+		dma_shift = 1;
+	} else {
+		dma_config |= WDSIZE_8;
+		dma_count = size;
+		dma_shift = 0;
+	}
+
+	dma_mod = (1 << dma_shift) * mod_dir;
+	if (mod_dir == -1)
+		addr += size + dma_mod;
+
+	set_dma_start_addr(dma_ch, (unsigned long)addr);
+	set_dma_x_count(dma_ch, dma_count);
+	set_dma_x_modify(dma_ch, dma_mod);
+	set_dma_config(dma_ch, dma_config);
+	SSYNC();
+	enable_dma(dma_ch);
+}
+
+static int bfin_crc_run(struct bfin_crc *crc)
+{
+	int ret = 0;
+	unsigned long control;
+	unsigned int timeout = 100000;
+	int mod_dir = 1;
+	struct crc_info *info = crc->info;
+
+	if (info->datasize == 0)
+		return 0;
+	else if (info->datasize % 4 != 0) {
+		dev_info(crc->mdev.this_device, "CRC data size should be multiply of 4 bytes.\n");
+		return -EINVAL;
+	}
+
+	/* config CRC */
+	control = crc->opmode << OPMODE_OFFSET;
+	control |= (info->bitmirr << BITMIRR_OFFSET);
+	control |= (info->bytmirr << BYTMIRR_OFFSET);
+	control |= (info->w16swp << W16SWP_OFFSET);
+	control |= (info->fdsel << FDSEL_OFFSET);
+	control |= (info->rsltmirr << RSLTMIRR_OFFSET);
+	control |= (info->polymirr << POLYMIRR_OFFSET);
+	control |= (info->cmpmirr << CMPMIRR_OFFSET);
+	control |= AUTOCLRZ;
+
+	if (crc->opmode == MODE_DMACPY_CRC)
+		control |= OBRSTALL;
+
+	crc->regs->control = control;
+	SSYNC();
+
+	if (crc->opmode == MODE_CALC_CRC || crc->opmode == MODE_DMACPY_CRC) {
+		crc->regs->poly = info->crc_poly;
+		SSYNC();
+
+		while (!(crc->regs->status & LUTDONE) && (--timeout) > 0)
+			cpu_relax();
+
+		if (!timeout) {
+			dev_dbg(crc->mdev.this_device, "Timeout when generating LUT.\n");
+			return -EBUSY;
+		}
+	}
+
+	crc->regs->datacntrld = 0;
+	crc->regs->datacnt = info->datasize >> 2;
+	crc->regs->compare = info->crc_compare;
+
+	/* setup CRC interrupts */
+	crc->regs->status = CMPERRI | DCNTEXPI;
+	crc->regs->intrenset = CMPERRI | DCNTEXPI;
+	SSYNC();
+
+	/* setup CRC receive DMA */
+	switch (crc->opmode) {
+	case MODE_DMACPY_CRC:
+		invalidate_dcache_range((unsigned long)info->out_addr,
+				(unsigned long)(info->out_addr + info->datasize));
+		if (info->out_addr < info->in_addr + info->datasize)
+			mod_dir = -1;
+		bfin_crc_config_dma(crc->dma_ch_dest, info->out_addr,
+				info->datasize, WNR, mod_dir);
+		break;
+	case MODE_DATA_FILL:
+		crc->regs->fillval = info->val_fill;
+		invalidate_dcache_range((unsigned long)info->out_addr,
+				(unsigned long)(info->out_addr + info->datasize));
+		bfin_crc_config_dma(crc->dma_ch_dest, info->out_addr,
+				info->datasize, WNR, 1);
+		break;
+	}
+
+	/* enable CRC operation */
+	crc->regs->control |= BLKEN;
+	SSYNC();
+
+	/* setup CRC transfer DMA */
+	switch (crc->opmode) {
+	case MODE_CALC_CRC:
+	case MODE_DATA_VERIFY:
+		flush_dcache_range((unsigned long)info->in_addr,
+				(unsigned long)(info->in_addr + info->datasize));
+		bfin_crc_config_dma(crc->dma_ch_src, info->in_addr,
+				info->datasize, 0, 1);
+		break;
+	case MODE_DMACPY_CRC:
+		flush_dcache_range((unsigned long)info->in_addr,
+				(unsigned long)(info->in_addr + info->datasize));
+		bfin_crc_config_dma(crc->dma_ch_src, info->in_addr,
+				info->datasize, 0, mod_dir);
+		break;
+	}
+
+	/* wait for completion */
+	if ((ret = wait_for_completion_interruptible(&crc->c)) < 0) {
+		dev_dbg(crc->mdev.this_device, "Completion waiting is interrupted.\n");
+		goto out;
+	}
+	if (crc->opmode == MODE_DMACPY_CRC || crc->opmode == MODE_DATA_FILL)
+		while (crc->regs->status & OBR)
+			cpu_relax();
+
+out:
+	clear_dma_irqstat(crc->dma_ch_src);
+	clear_dma_irqstat(crc->dma_ch_dest);
+	disable_dma(crc->dma_ch_src);
+	disable_dma(crc->dma_ch_dest);
+	crc->regs->intrenclr = CMPERRI | DCNTEXPI;
+	crc->regs->status = CMPERRI | DCNTEXPI;
+	crc->regs->control = 0;
+	SSYNC();
+	return ret;
+}
+
+/**
+ *	bfin_crc_handler - CRC interrupt handler
+ */
+static irqreturn_t bfin_crc_handler(int irq, void *dev_id)
+{
+	struct bfin_crc *crc = dev_id;
+
+	if (crc->regs->status & DCNTEXP) {
+		crc->regs->status = DCNTEXP;
+		SSYNC();
+		/* prepare results */
+		switch (crc->opmode) {
+		case MODE_CALC_CRC:
+		case MODE_DMACPY_CRC:
+			crc->info->crc_result = crc->regs->result;
+			break;
+		case MODE_DATA_VERIFY:
+			crc->info->pos_verify =
+				(crc->regs->status & CMPERR) ? crc->regs->datacntcap : 0;
+			break;
+		};
+		if (crc->regs->control | BLKEN)
+			complete(&crc->c);
+
+		crc->regs->control &= ~BLKEN;
+		return IRQ_HANDLED;
+	} else
+		return IRQ_NONE;
+}
+
+/**
+ *	bfin_crc_open - Open the Device
+ *	@inode: inode of device
+ *	@filp: file handle of device
+ */
+static int bfin_crc_open(struct inode *inode, struct file *filp)
+{
+	int minor;
+	struct bfin_crc *crc;
+	int ret;
+
+	minor = MINOR(inode->i_rdev);
+	filp->private_data = NULL;
+	list_for_each_entry(crc, &bfin_crc_list, list)
+		if (crc->mdev.minor == minor) {
+			filp->private_data = crc;
+			break;
+		}
+
+	if (!filp->private_data)
+		return -ENODEV;
+
+	if (mutex_lock_interruptible(&crc->mutex))
+		return -ERESTARTSYS;
+
+	init_completion(&crc->c);
+
+	ret = request_irq(crc->irq, bfin_crc_handler, IRQF_SHARED, DRIVER_NAME, crc);
+	if (ret) {
+		pr_err("unable to request blackfin crc irq\n");
+		goto out;
+	}
+
+	if (request_dma(crc->dma_ch_src, "BFIN_CRC_SRC") < 0) {
+		printk(KERN_NOTICE "Unable to attach Blackfin CRC source DMA channel\n");
+		ret = -EBUSY;
+		goto out;
+	}
+
+	if (request_dma(crc->dma_ch_dest, "BFIN_CRC_DEST") < 0) {
+		printk(KERN_NOTICE "Unable to attach Blackfin CRC destination DMA channel\n");
+		free_dma(crc->dma_ch_src);
+		ret = -EBUSY;
+		goto out;
+	}
+
+out:
+	mutex_unlock(&crc->mutex);
+
+	return 0;
+}
+
+/**
+ *	bfin_crc_close - Close the Device
+ *	@inode: inode of device
+ *	@filp: file handle of device
+ */
+static int bfin_crc_release(struct inode *inode, struct file *filp)
+{
+	int minor;
+	struct bfin_crc *crc;
+
+	minor = MINOR(inode->i_rdev);
+	filp->private_data = NULL;
+	list_for_each_entry(crc, &bfin_crc_list, list)
+		if (crc->mdev.minor == minor) {
+			filp->private_data = crc;
+			break;
+		}
+
+	if (!filp->private_data)
+		return -ENODEV;
+
+	if (mutex_lock_interruptible(&crc->mutex))
+		return -ERESTARTSYS;
+
+	free_dma(crc->dma_ch_src);
+	free_dma(crc->dma_ch_dest);
+	free_irq(crc->irq, crc);
+	mutex_unlock(&crc->mutex);
+
+	return 0;
+}
+
+/**
+ *	bfin_crc_ioctl - Operate CRC Device
+ *	@filp: file handle of device
+ *	@cmd: hardware crc command
+ *	@arg: argument
+ */
+static long bfin_crc_ioctl(struct file *filp,
+				unsigned int cmd, unsigned long arg)
+{
+	void __user *argp = (void __user *)arg;
+	struct crc_info bfin_crc_info;
+	struct bfin_crc *crc;
+	int ret = 0;
+
+	crc = filp->private_data;
+
+	if (mutex_lock_interruptible(&crc->mutex))
+		return -ERESTARTSYS;
+
+	if (copy_from_user(&bfin_crc_info, argp, sizeof(bfin_crc_info))) {
+		ret = -EFAULT;
+		goto out;
+	}
+
+	crc->info = &bfin_crc_info;
+	switch (cmd) {
+	case CRC_IOC_CALC_CRC:
+		crc->opmode = MODE_CALC_CRC;
+		break;
+	case CRC_IOC_MEMCPY_CRC:
+		crc->opmode = MODE_DMACPY_CRC;
+		break;
+	case CRC_IOC_VERIFY_VAL:
+		crc->opmode = MODE_DATA_VERIFY;
+		break;
+	case CRC_IOC_FILL_VAL:
+		crc->opmode = MODE_DATA_FILL;
+		break;
+	default:
+		ret = -ENOTTY;
+		goto out;
+	}
+	ret = bfin_crc_run(crc);
+
+	if (ret >= 0 && copy_to_user(argp, &bfin_crc_info, sizeof(bfin_crc_info)))
+		ret = -EFAULT;
+
+out:
+	crc->info = NULL;
+	crc->opmode = 0;
+	mutex_unlock(&crc->mutex);
+	return ret;
+}
+
+#ifdef CONFIG_PM
+/**
+ *	bfin_crc_suspend - suspend the watchdog
+ *	@pdev: device being suspended
+ *	@state: requested suspend state
+ */
+static int bfin_crc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct bfin_crc *crc = platform_get_drvdata(pdev);
+	int i = 100000;
+
+	while ((crc->regs->control & BLKEN) && --i)
+		cpu_relax();
+
+	if (i == 0)
+		crc->regs->control &= ~BLKEN;
+
+	return 0;
+}
+
+/**
+ *	bfin_crc_resume - resume the watchdog
+ *	@pdev: device being resumed
+ */
+static int bfin_crc_resume(struct platform_device *pdev)
+{
+	return 0;
+}
+#else
+# define bfin_crc_suspend NULL
+# define bfin_crc_resume NULL
+#endif
+
+static const struct file_operations bfin_crc_fops = {
+	.owner		= THIS_MODULE,
+	.llseek		= no_llseek,
+	.unlocked_ioctl	= bfin_crc_ioctl,
+	.open		= bfin_crc_open,
+	.release	= bfin_crc_release,
+};
+
+/**
+ *	bfin_crc_probe - Initialize module
+ *
+ *	Registers the misc device.  Actual device
+ *	initialization is handled by bfin_crc_open().
+ */
+static int __devinit bfin_crc_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	struct bfin_crc *crc = NULL;
+	int ret;
+
+	crc = kzalloc(sizeof(*crc), GFP_KERNEL);
+	if (!crc) {
+		dev_err(&pdev->dev, "fail to malloc bfin_crc\n");
+		return -ENOMEM;
+	}
+
+	mutex_init(&crc->mutex);
+	crc->mdev.minor	= MISC_DYNAMIC_MINOR;
+	snprintf(crc->name, 20, "%s%d", DRIVER_NAME, pdev->id);
+	crc->mdev.name	= crc->name;
+	crc->mdev.fops	= &bfin_crc_fops;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n");
+		ret = -ENOENT;
+		goto out_error_free_mem;
+	}
+
+	crc->regs = ioremap(res->start, resource_size(res));
+	if (!crc->regs) {
+		dev_err(&pdev->dev, "Cannot map CRC IO\n");
+		ret = -ENXIO;
+		goto out_error_free_mem;
+	}
+
+	crc->irq = platform_get_irq(pdev, 0);
+	if (crc->irq < 0) {
+		dev_err(&pdev->dev, "No CRC DCNTEXP IRQ specified\n");
+		ret = -ENOENT;
+		goto out_error_unmap;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "No CRC DMA source channel specified\n");
+		ret = -ENOENT;
+		goto out_error_unmap;
+	}
+	crc->dma_ch_src = res->start;
+
+	res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "No CRC DMA destination channel specified\n");
+		ret = -ENOENT;
+		goto out_error_unmap;
+	}
+	crc->dma_ch_dest = res->start;
+
+	ret = misc_register(&crc->mdev);
+	if (ret) {
+		dev_err(&pdev->dev, "cannot register CRC miscdev\n");
+		return ret;
+	}
+
+	list_add(&crc->list, &bfin_crc_list);
+	dev_set_drvdata(&pdev->dev, crc);
+
+	dev_info(&pdev->dev, "initialized\n");
+
+	return 0;
+
+out_error_unmap:
+	iounmap((void *)crc->regs);
+out_error_free_mem:
+	kfree(crc);
+	return ret;
+}
+
+/**
+ *	bfin_crc_remove - Initialize module
+ *
+ *	Unregisters the misc device.  Actual device
+ *	deinitialization is handled by bfin_crc_close().
+ */
+static int __devexit bfin_crc_remove(struct platform_device *pdev)
+{
+	struct bfin_crc *crc = platform_get_drvdata(pdev);
+
+	dev_set_drvdata(&pdev->dev, NULL);
+
+	if (crc) {
+		misc_deregister(&crc->mdev);
+		list_del(&crc->list);
+		iounmap((void *)crc->regs);
+		kfree(crc);
+	}
+
+	return 0;
+}
+
+static struct platform_driver bfin_crc_driver = {
+	.probe     = bfin_crc_probe,
+	.remove    = __devexit_p(bfin_crc_remove),
+	.suspend   = bfin_crc_suspend,
+	.resume    = bfin_crc_resume,
+	.driver    = {
+		.name  = DRIVER_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+/**
+ *	bfin_crc_init - Initialize module
+ *
+ *	Checks the module params and registers the platform driver.
+ *	Real work is in the platform probe function.
+ */
+static int __init bfin_crc_init(void)
+{
+	int ret;
+
+	pr_info("Blackfin hardware CRC driver\n");
+
+	ret = platform_driver_register(&bfin_crc_driver);
+	if (ret) {
+		pr_info(KERN_ERR "unable to register driver\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+/**
+ *	bfin_crc_exit - Deinitialize module
+ */
+static void __exit bfin_crc_exit(void)
+{
+	platform_driver_unregister(&bfin_crc_driver);
+}
+
+module_init(bfin_crc_init);
+module_exit(bfin_crc_exit);
+
+MODULE_AUTHOR("Sonic Zhang <sonic.zhang@...log.com>");
+MODULE_DESCRIPTION("Blackfin hardware CRC Driver");
+MODULE_LICENSE("GPL");
-- 
1.7.0.4


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ