Title: [7886] trunk/drivers/char/bfin_sport.c: bfin_sport: clean up & overhaul
Revision
7886
Author
vapier
Date
2009-11-27 02:32:25 -0500 (Fri, 27 Nov 2009)

Log Message

bfin_sport: clean up & overhaul

 - convert to pr_* helper macros
 - convert wait queue to completion interface
 - do not send signals to processes
 - use a dynamic major number
 - have sport device numbers be static (sport0 is always sport0)
 - allow device to be opened multiple times w/out dumping
 - use proper volatile markings on sport mmrs
 - have module remove clean up everything
 - drop dumping of hardcoded DMA channel 4

Modified Paths

Diff

Modified: trunk/drivers/char/bfin_sport.c (7885 => 7886)


--- trunk/drivers/char/bfin_sport.c	2009-11-27 04:37:52 UTC (rev 7885)
+++ trunk/drivers/char/bfin_sport.c	2009-11-27 07:32:25 UTC (rev 7886)
@@ -6,7 +6,11 @@
  * Licensed under the GPL-2 or later.
  */
 
+#define DRV_NAME "bfin_sport"
+#define pr_fmt(fmt) DRV_NAME ": " fmt
+
 #include <linux/cdev.h>
+#include <linux/completion.h>
 #include <linux/delay.h>
 #include <linux/device.h>
 #include <linux/fcntl.h>
@@ -16,29 +20,18 @@
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/moduleparam.h>
 #include <linux/mutex.h>
-#include <linux/sched.h>
-#include <linux/signal.h>
 #include <linux/types.h>
 #include <linux/uaccess.h>
-#include <linux/wait.h>
 
+#include <asm/atomic.h>
 #include <asm/blackfin.h>
 #include <asm/bfin_sport.h>
 #include <asm/dma.h>
 #include <asm/cacheflush.h>
-#include <asm/system.h>
 #include <asm/portmux.h>
 
-#define SPORT_MAJOR	237
-#define SPORT_NR_DEVS	2
-
 struct sport_dev {
-	struct cdev cdev;	/* Char device structure */
-
-	int sport_num;
-
 	int dma_rx_chan;
 	int dma_tx_chan;
 
@@ -54,19 +47,15 @@
 
 	int err_irq;
 
-	struct mutex mutex;	/* mutual exclusion semaphore */
-	struct task_struct *task;
+	struct mutex mutex;
+	unsigned int open_count;
 
-	wait_queue_head_t waitq;
-	int	wait_con;
-	struct sport_register *regs;
+	struct completion c;
+
+	volatile struct sport_register *regs;
 	struct sport_config config;
 };
 
-static int sport_major = SPORT_MAJOR;
-static int sport_minor;
-static int sport_nr_devs = SPORT_NR_DEVS;	/* number of bare sport devices */
-
 /* XXX: this should get pushed to platform device */
 #define SPORT_REQ(x) \
 	[x] = {P_SPORT##x##_TFS, P_SPORT##x##_DTPRI, P_SPORT##x##_TSCLK, P_SPORT##x##_DTSEC, \
@@ -93,9 +82,9 @@
 		.rx_irq      = IRQ_SPORT##x##_RX, \
 		.tx_irq      = IRQ_SPORT##x##_TX, \
 		.err_irq     = IRQ_SPORT##x##_ERROR, \
-		.regs        = (struct sport_register *)SPORT##x##_TCR1, \
+		.regs        = (void *)SPORT##x##_TCR1, \
 	}
-static struct sport_dev sport_devices[] = {
+static struct sport_dev sport_devs[] = {
 #ifdef SPORT0_TCR1
 	SPORT_PARAMS(0),
 #endif
@@ -110,13 +99,11 @@
 #endif
 };
 
-#define DRV_NAME "bfin_sport"
-
 static irqreturn_t dma_rx_irq_handler(int irq, void *dev_id);
 static irqreturn_t dma_tx_irq_handler(int irq, void *dev_id);
 
 /* note: multichannel is in units of 8 channels, tdm_count is # channels NOT / 8 ! */
-static int sport_set_multichannel(struct sport_register *regs,
+static int sport_set_multichannel(volatile struct sport_register *regs,
 				  int tdm_count, int packed, int frame_delay)
 {
 	if (tdm_count) {
@@ -158,7 +145,7 @@
 		/* Request rx dma and set irq handler */
 		ret = request_dma(dev->dma_rx_chan, "sport_rx_dma_chan");
 		if (ret) {
-			printk(KERN_ERR "Unable to request sport rx dma channel\n");
+			pr_err("unable to request sport rx dma channel\n");
 			return ret;
 		}
 		set_dma_callback(dev->dma_rx_chan, dma_rx_irq_handler, dev);
@@ -166,7 +153,7 @@
 		/* Request tx dma and set irq handler */
 		ret = request_dma(dev->dma_tx_chan, "sport_tx_dma_chan");
 		if (ret) {
-			printk(KERN_ERR "Unable to request sport tx dma channel\n");
+			pr_err("unable to request sport tx dma channel\n");
 			return ret;
 		}
 		set_dma_callback(dev->dma_tx_chan, dma_tx_irq_handler, dev);
@@ -254,8 +241,7 @@
 	else if (word_len <= 32)
 		wordsize = WDSIZE_32;
 	else
-		printk(KERN_ERR "%s: word_len:%d is error\n", __func__,
-		       word_len);
+		pr_err("word_len of %d is invalid\n", word_len);
 
 	return wordsize;
 }
@@ -269,8 +255,7 @@
 	SSYNC();
 	disable_dma(dev->dma_rx_chan);
 
-	dev->wait_con = 1;
-	wake_up(&dev->waitq);
+	complete(&dev->c);
 
 	clear_dma_irqstat(dev->dma_rx_chan);
 	return IRQ_HANDLED;
@@ -284,14 +269,14 @@
 	pr_debug("%s enter\n", __func__);
 	status = get_dma_curr_irqstat(dev->dma_tx_chan);
 	while (status & DMA_RUN) {
+		pr_debug("status:0x%04x\n", status);
 		status = get_dma_curr_irqstat(dev->dma_tx_chan);
-		pr_debug("status:0x%04x\n", status);
 	}
 	status = dev->regs->stat;
 	while (!(status & TXHRE)) {
-		pr_debug("%s status:%x\n", __func__, status);
+		pr_debug("status:%x\n", status);
 		udelay(1);
-		status = *(volatile unsigned short *)&dev->regs->stat;
+		status = dev->regs->stat;
 	}
 	/* Wait for the last byte sent out */
 	udelay(500);
@@ -301,8 +286,7 @@
 	SSYNC();
 	disable_dma(dev->dma_tx_chan);
 
-	dev->wait_con = 1;
-	wake_up(&dev->waitq);
+	complete(&dev->c);
 
 	/* Clear the interrupt status */
 	clear_dma_irqstat(dev->dma_tx_chan);
@@ -345,8 +329,7 @@
 
 	if (dev->rx_received >= dev->rx_len) {
 		dev->regs->rcr1 &= ~RSPEN;
-		dev->wait_con = 1;
-		wake_up(&dev->waitq);
+		complete(&dev->c);
 	}
 
 	return IRQ_HANDLED;
@@ -392,16 +375,15 @@
 
 		stat = dev->regs->stat;
 		while (!(stat & TXHRE)) {
+			pr_debug("%s: stat:%x\n", __func__, stat);
 			udelay(1);
-			pr_debug("%s:stat:%x\n", __func__, stat);
-			stat = *(volatile unsigned short *)&dev->regs->stat;
+			stat = dev->regs->stat;
 		}
 		udelay(500);
 		dev->regs->tcr1 &= ~TSPEN;
 		SSYNC();
-		pr_debug("%s:stat:%x\n", __func__, stat);
-		dev->wait_con = 1;
-		wake_up(&dev->waitq);
+		pr_debug("%s: stat:%x\n", __func__, stat);
+		complete(&dev->c);
 	}
 
 	return IRQ_HANDLED;
@@ -426,21 +408,18 @@
 		SSYNC();
 
 		if (!dev->config.dma_enabled && !dev->config.int_clk) {
-			if (status & TUVF) {
-				dev->wait_con = 1;
-				wake_up(&dev->waitq);
-			}
+			if (status & TUVF)
+				complete(&dev->c);
 		} else
-			printk(KERN_WARNING "sport %d status error:%s%s%s%s\n",
-			       dev->sport_num,
+			pr_warning("sport %p status error:%s%s%s%s\n",
+			       dev->regs,
 			       status & TOVF ? " TOVF" : "",
 			       status & TUVF ? " TUVF" : "",
 			       status & ROVF ? " ROVF" : "",
 			       status & RUVF ? " RUVF" : "");
 	}
 
-	if (dev->config.dma_enabled || dev->config.int_clk)
-		send_sig(SIGABRT, dev->task, 1);
+	/* XXX: should we always complete here and have read/write error ? */
 
 	return IRQ_HANDLED;
 }
@@ -451,13 +430,27 @@
 
 static int sport_open(struct inode *inode, struct file *filp)
 {
-	int ret;
-	struct sport_dev *dev;	/* device information */
+	int ret, minor;
+	struct sport_dev *dev;
 
 	pr_debug("%s enter\n", __func__);
-	dev = container_of(inode->i_cdev, struct sport_dev, cdev);
+
+	minor = MINOR(inode->i_rdev);
+	if (minor >= ARRAY_SIZE(sport_devs))
+		return -ENODEV;
+
+	dev = &sport_devs[minor];
+	if (!dev->regs)
+		return -ENODEV;
+
+	if (mutex_lock_interruptible(&dev->mutex))
+		return -ERESTARTSYS;
+
 	filp->private_data = dev;	/* for other methods */
 
+	if (dev->open_count++)
+		goto done;
+
 	memset(&dev->config, 0, sizeof(dev->config));
 
 	dev->rx_buf = NULL;
@@ -466,34 +459,34 @@
 	dev->tx_buf = NULL;
 	dev->tx_len = 0;
 	dev->tx_sent = 0;
-	dev->wait_con = 0;
+	init_completion(&dev->c);
 
 	ret = request_irq(dev->tx_irq, sport_tx_handler, IRQF_SHARED, DRV_NAME "-tx", dev);
 	if (ret) {
-		printk(KERN_ERR "Unable to request sport tx irq\n");
+		pr_err("unable to request sport tx irq\n");
 		goto fail;
 	}
 
 	ret = request_irq(dev->rx_irq, sport_rx_handler, IRQF_SHARED, DRV_NAME "-rx", dev);
 	if (ret) {
-		printk(KERN_ERR "Unable to request sport rx irq\n");
+		pr_err("unable to request sport rx irq\n");
 		goto fail1;
 	}
 
 	ret = request_irq(dev->err_irq, sport_err_handler, 0, DRV_NAME "-err", dev);
 	if (ret) {
-		printk(KERN_ERR "Unable to request sport err irq\n");
+		pr_err("unable to request sport err irq\n");
 		goto fail2;
 	}
 
-	ret = peripheral_request_list(sport_req[dev->sport_num], DRV_NAME);
+	ret = peripheral_request_list(sport_req[minor], DRV_NAME);
 	if (ret) {
-		printk(KERN_ERR DRV_NAME ": Requesting Peripherals failed\n");
+		pr_err("requesting peripherals failed\n");
 		goto fail3;
 	}
 
-	dev->task = current;
-
+ done:
+	mutex_unlock(&dev->mutex);
 	return 0;
 
  fail3:
@@ -503,17 +496,26 @@
  fail1:
 	free_irq(dev->tx_irq, dev);
  fail:
-
+	mutex_unlock(&dev->mutex);
 	return ret;
 }
 
 static int sport_release(struct inode *inode, struct file *filp)
 {
+	int minor;
 	struct sport_dev *dev;
 
 	pr_debug("%s enter\n", __func__);
-	dev = container_of(inode->i_cdev, struct sport_dev, cdev);
 
+	minor = MINOR(inode->i_rdev);
+	dev = &sport_devs[minor];
+
+	if (mutex_lock_interruptible(&dev->mutex))
+		return -ERESTARTSYS;
+
+	if (--dev->open_count)
+		goto done;
+
 	dev->regs->tcr1 &= ~TSPEN;
 	dev->regs->rcr1 &= ~RSPEN;
 
@@ -526,15 +528,16 @@
 	}
 	free_irq(dev->err_irq, dev);
 
-	peripheral_free_list(sport_req[dev->sport_num]);
+	peripheral_free_list(sport_req[minor]);
 
+ done:
+	mutex_unlock(&dev->mutex);
 	return 0;
 }
 
 static ssize_t sport_read(struct file *filp, char __user *buf, size_t count,
 			  loff_t *f_pos)
 {
-	DECLARE_COMPLETION(done);
 	struct sport_dev *dev = filp->private_data;
 	struct sport_config *cfg = &dev->config;
 
@@ -543,7 +546,6 @@
 	if (mutex_lock_interruptible(&dev->mutex))
 		return -ERESTARTSYS;
 
-	dev->wait_con = 0;
 	if (cfg->dma_enabled) {
 		int word_bytes = (cfg->word_len + 7) / 8;
 		uint16_t dma_config, xcount, ycount;
@@ -584,13 +586,11 @@
 	dev->regs->rcr1 |= RSPEN;
 	SSYNC();
 
-	if (wait_event_interruptible(dev->waitq, dev->wait_con) < 0) {
+	if (wait_for_completion_interruptible(&dev->c)) {
 		pr_debug("Receive a signal to interrupt\n");
-		dev->wait_con = 0;
-		mutex_unlock(&dev->mutex);
-		return -ERESTARTSYS;
+		count = -ERESTARTSYS;
+		/* fall through */
 	}
-	dev->wait_con = 0;
 
 	pr_debug("Complete called in dma rx irq handler\n");
 	mutex_unlock(&dev->mutex);
@@ -598,19 +598,9 @@
 	return count;
 }
 
-static void dump_dma_regs(void)
-{
-	struct dma_register *dma = (struct dma_register *)DMA4_NEXT_DESC_PTR;
-
-	pr_debug("%s config:0x%04x, x_count:0x%04x,"
-		 " x_modify:0x%04x\n", __func__, dma->cfg,
-		 dma->x_count, dma->x_modify);
-}
-
 static ssize_t sport_write(struct file *filp, const char __user *buf,
 			   size_t count, loff_t *f_pos)
 {
-	DECLARE_COMPLETION(done);
 	struct sport_dev *dev = filp->private_data;
 	struct sport_config *cfg = &dev->config;
 	pr_debug("%s count:%ld  dma_tx_chan:%d\n",
@@ -619,7 +609,6 @@
 	if (mutex_lock_interruptible(&dev->mutex))
 		return -ERESTARTSYS;
 
-	dev->wait_con = 0;
 	/* Configure dma to start transfer */
 	if (cfg->dma_enabled) {
 		uint16_t dma_config, xcount, ycount;
@@ -651,7 +640,6 @@
 		set_dma_config(dev->dma_tx_chan, dma_config);
 
 		enable_dma(dev->dma_tx_chan);
-		dump_dma_regs();
 	} else {
 		/* Configure parameters to start PIO transfer */
 		dev->tx_buf = buf;
@@ -664,13 +652,11 @@
 	SSYNC();
 
 	pr_debug("wait for transfer finished\n");
-	if (wait_event_interruptible(dev->waitq, dev->wait_con) < 0) {
+	if (wait_for_completion_interruptible(&dev->c)) {
 		pr_debug("Receive a signal to interrupt\n");
-		dev->wait_con = 0;
-		mutex_unlock(&dev->mutex);
-		return -ERESTARTSYS;
+		count = -ERESTARTSYS;
+		/* fall through */
 	}
-	dev->wait_con = 0;
 	pr_debug("waiting over\n");
 
 	mutex_unlock(&dev->mutex);
@@ -706,18 +692,22 @@
 	unsigned short i;
 	p = buf;
 
-	for (i = 0; i < sport_nr_devs; ++i)
+	for (i = 0; i < ARRAY_SIZE(sport_devs); ++i) {
+		if (!sport_devs[i].regs)
+			continue;
 		p += sprintf(p,
-			"sport%d:\nrx_irq=%d, rx_received=%d, tx_irq=%d, tx_sent=%d,\n"
-			"mode=%d, channels=%d, data_format=%d, word_len=%d.\n",
-			i, sport_devices[i].rx_irq,
-			sport_devices[i].rx_received,
-			sport_devices[i].tx_irq,
-			sport_devices[i].tx_sent,
-			sport_devices[i].config.mode,
-			sport_devices[i].config.channels,
-			sport_devices[i].config.data_format,
-			sport_devices[i].config.word_len);
+			"sport%d:\n"
+			"\trx_irq=%d rx_received=%d tx_irq=%d tx_sent=%d\n"
+			"\tmode=%d channels=%d data_format=%d word_len=%d\n",
+			i, sport_devs[i].rx_irq,
+			sport_devs[i].rx_received,
+			sport_devs[i].tx_irq,
+			sport_devs[i].tx_sent,
+			sport_devs[i].config.mode,
+			sport_devs[i].config.channels,
+			sport_devs[i].config.data_format,
+			sport_devs[i].config.word_len);
+	}
 
 	return p - buf;
 }
@@ -735,63 +725,77 @@
 
 static CLASS_ATTR(status, S_IRUGO, &sport_status_show, NULL);
 
+static struct cdev sport_cdev;
+static dev_t sport_dev;
+
 static void __exit sport_cleanup_module(void)
 {
 	int i;
-	dev_t devno = MKDEV(sport_major, sport_minor);
 
-	for (i = 0; i < sport_nr_devs; ++i)
-		cdev_del(&sport_devices[i].cdev);
+	cdev_del(&sport_cdev);
+	unregister_chrdev_region(sport_dev, ARRAY_SIZE(sport_devs));
 
-	unregister_chrdev_region(devno, sport_nr_devs);
-
+	for (i = 0; i < ARRAY_SIZE(sport_devs); ++i)
+		if (sport_devs[i].regs)
+			device_destroy(sport_class, sport_dev + i);
 	class_destroy(sport_class);
 }
 module_exit(sport_cleanup_module);
 
-static void __init sport_setup_cdev(struct sport_dev *dev, int index)
-{
-	int err, devno = MKDEV(sport_major, sport_minor + index);
-
-	cdev_init(&dev->cdev, &sport_fops);
-	dev->cdev.owner = THIS_MODULE;
-	err = cdev_add(&dev->cdev, devno, 1);
-	if (err)
-		printk(KERN_NOTICE "Error %d adding sport%d", err, index);
-}
-
 static int __init sport_init_module(void)
 {
-	int minor;
-	int result, i;
-	dev_t dev = 0;
+	int ret, i, nr_devs;
+	nr_devs = ARRAY_SIZE(sport_devs);
 
-	dev = MKDEV(sport_major, sport_minor);
-	result = register_chrdev_region(dev, sport_nr_devs, "sport");
-	if (result < 0) {
-		printk(KERN_WARNING "sport: can't get major %d\n", sport_major);
-		return result;
+	ret = alloc_chrdev_region(&sport_dev, 0, nr_devs, "sport");
+	if (ret) {
+		pr_err("can't alloc %i minors\n", nr_devs);
+		goto err;
 	}
 
+	cdev_init(&sport_cdev, &sport_fops);
+	sport_cdev.owner = THIS_MODULE;
+	ret = cdev_add(&sport_cdev, sport_dev, nr_devs);
+	if (ret) {
+		pr_err("cdev_add() failed\n");
+		goto err_chrdev;
+	}
+
 	sport_class = class_create(THIS_MODULE, "sport");
-	result = class_create_file(sport_class, &class_attr_status);
-	if (result) {
-		unregister_chrdev_region(dev, sport_nr_devs);
-		return result;
+	ret = class_create_file(sport_class, &class_attr_status);
+	if (ret) {
+		pr_err("class_create(sport) failed\n");
+		goto err_cdev;
 	}
-	for (minor = 0; minor < sport_nr_devs; minor++)
-		device_create(sport_class, NULL, MKDEV(sport_major, minor),
-		              NULL, "sport%d", minor);
 
-	/* Initialize each device. */
-	for (i = 0; i < sport_nr_devs; ++i) {
-		sport_setup_cdev(&sport_devices[i], i);
-		sport_devices[i].sport_num = i;
-		mutex_init(&sport_devices[i].mutex);
-		init_waitqueue_head(&sport_devices[i].waitq);
+	for (i = 0; i < nr_devs; ++i) {
+		struct device *dev;
+		if (!sport_devs[i].regs)
+			continue;
+
+		dev = device_create(sport_class, NULL, sport_dev + i,
+		                    &sport_devs[i], "sport%d", i);
+		if (!dev)
+			goto err_dev;
+
+		mutex_init(&sport_devs[i].mutex);
+
+		pr_info("registered sport%i\n", i);
 	}
 
 	return 0;
+
+ err_dev:
+	while (--i > 0)
+		if (sport_devs[i].regs)
+			device_destroy(sport_class, sport_dev + i);
+	class_destroy(sport_class);
+ err_cdev:
+	cdev_del(&sport_cdev);
+ err_chrdev:
+	unregister_chrdev_region(sport_dev, nr_devs);
+ err:
+	return ret;
 }
 module_init(sport_init_module);
 
_______________________________________________
Linux-kernel-commits mailing list
[email protected]
https://blackfin.uclinux.org/mailman/listinfo/linux-kernel-commits

Reply via email to