Initial Contribution

msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142

Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index d9fd862..c174306 100644
--- a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -83,6 +83,7 @@
 	struct mutex		buf_lock;
 	unsigned		users;
 	u8			*buffer;
+	u8			*bufferrx;
 };
 
 static LIST_HEAD(device_list);
@@ -92,6 +93,30 @@
 module_param(bufsiz, uint, S_IRUGO);
 MODULE_PARM_DESC(bufsiz, "data bytes in biggest supported SPI message");
 
+/*
+ * This can be used for testing the controller, given the busnum and the
+ * cs required to use. If those parameters are used, spidev is
+ * dynamically added as device on the busnum, and messages can be sent
+ * via this interface.
+ */
+static int busnum = -1;
+module_param(busnum, int, S_IRUGO);
+MODULE_PARM_DESC(busnum, "bus num of the controller");
+
+static int chipselect = -1;
+module_param(chipselect, int, S_IRUGO);
+MODULE_PARM_DESC(chipselect, "chip select of the desired device");
+
+static int maxspeed = 10000000;
+module_param(maxspeed, int, S_IRUGO);
+MODULE_PARM_DESC(maxspeed, "max_speed of the desired device");
+
+static int spimode = SPI_MODE_3;
+module_param(spimode, int, S_IRUGO);
+MODULE_PARM_DESC(spimode, "mode of the desired device");
+
+static struct spi_device *spi;
+
 /*-------------------------------------------------------------------------*/
 
 /*
@@ -221,7 +246,7 @@
 	struct spi_transfer	*k_tmp;
 	struct spi_ioc_transfer *u_tmp;
 	unsigned		n, total;
-	u8			*buf;
+	u8			*buf, *bufrx;
 	int			status = -EFAULT;
 
 	spi_message_init(&msg);
@@ -234,6 +259,7 @@
 	 * to initialize a kernel version of the same transfer.
 	 */
 	buf = spidev->buffer;
+	bufrx = spidev->bufferrx;
 	total = 0;
 	for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;
 			n;
@@ -247,7 +273,7 @@
 		}
 
 		if (u_tmp->rx_buf) {
-			k_tmp->rx_buf = buf;
+			k_tmp->rx_buf = bufrx;
 			if (!access_ok(VERIFY_WRITE, (u8 __user *)
 						(uintptr_t) u_tmp->rx_buf,
 						u_tmp->len))
@@ -261,6 +287,7 @@
 				goto done;
 		}
 		buf += k_tmp->len;
+		bufrx += k_tmp->len;
 
 		k_tmp->cs_change = !!u_tmp->cs_change;
 		k_tmp->bits_per_word = u_tmp->bits_per_word;
@@ -285,7 +312,7 @@
 		goto done;
 
 	/* copy any rx data out of bounce buffer */
-	buf = spidev->buffer;
+	buf = spidev->bufferrx;
 	for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) {
 		if (u_tmp->rx_buf) {
 			if (__copy_to_user((u8 __user *)
@@ -503,6 +530,15 @@
 				status = -ENOMEM;
 			}
 		}
+		if (!spidev->bufferrx) {
+			spidev->bufferrx = kmalloc(bufsiz, GFP_KERNEL);
+			if (!spidev->bufferrx) {
+				dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");
+				kfree(spidev->buffer);
+				spidev->buffer = NULL;
+				status = -ENOMEM;
+			}
+		}
 		if (status == 0) {
 			spidev->users++;
 			filp->private_data = spidev;
@@ -531,6 +567,8 @@
 
 		kfree(spidev->buffer);
 		spidev->buffer = NULL;
+		kfree(spidev->bufferrx);
+		spidev->bufferrx = NULL;
 
 		/* ... after we unbound from the underlying device? */
 		spin_lock_irq(&spidev->spi_lock);
@@ -674,21 +712,58 @@
 
 	spidev_class = class_create(THIS_MODULE, "spidev");
 	if (IS_ERR(spidev_class)) {
-		unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
-		return PTR_ERR(spidev_class);
+		status = PTR_ERR(spidev_class);
+		goto error_class;
 	}
 
 	status = spi_register_driver(&spidev_spi_driver);
-	if (status < 0) {
-		class_destroy(spidev_class);
-		unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
+	if (status < 0)
+		goto error_register;
+
+	if (busnum != -1 && chipselect != -1) {
+		struct spi_board_info chip = {
+					.modalias	= "spidev",
+					.mode		= spimode,
+					.bus_num	= busnum,
+					.chip_select	= chipselect,
+					.max_speed_hz	= maxspeed,
+		};
+
+		struct spi_master *master;
+
+		master = spi_busnum_to_master(busnum);
+		if (!master) {
+			status = -ENODEV;
+			goto error_busnum;
+		}
+
+		/* We create a virtual device that will sit on the bus */
+		spi = spi_new_device(master, &chip);
+		if (!spi) {
+			status = -EBUSY;
+			goto error_mem;
+		}
+		dev_dbg(&spi->dev, "busnum=%d cs=%d bufsiz=%d maxspeed=%d",
+			busnum, chipselect, bufsiz, maxspeed);
 	}
+	return 0;
+error_mem:
+error_busnum:
+	spi_unregister_driver(&spidev_spi_driver);
+error_register:
+	class_destroy(spidev_class);
+error_class:
+	unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
 	return status;
 }
 module_init(spidev_init);
 
 static void __exit spidev_exit(void)
 {
+	if (spi) {
+		spi_unregister_device(spi);
+		spi = NULL;
+	}
 	spi_unregister_driver(&spidev_spi_driver);
 	class_destroy(spidev_class);
 	unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);