linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] Add support for Sony SxS cards
@ 2014-07-07  9:53 Kieran Kunhya
  2014-07-07  9:53 ` [PATCH] block: Add support for Sony SxS cards Signed-off-by: Kieran Kunhya <kieran@kunhya.com> Kieran Kunhya
  0 siblings, 1 reply; 7+ messages in thread
From: Kieran Kunhya @ 2014-07-07  9:53 UTC (permalink / raw)
  To: axboe
  Cc: akpm, jmoyer, rdunlap, fabf, minchan, linus.walleij, abhansali,
	gnomes, jkosina, linux-kernel, Kieran Kunhya

This patch adds a reverse-engineered read-only driver for Sony SxS cards.
SxS cards are an Expresscard based memory card used in professional cameras.

I've not managed to get block device DMA working for some reason and currently
this driver uses a memcpy. I may well be using the block device DMA API incorrectly.

The block device DMA code can be found here:
https://github.com/kierank/sxs-linux/blob/dma/sxs.c

Any comments on the DMA code would also be appreciated.

Kieran Kunhya (1):
  block: Add support for Sony SxS cards     Signed-off-by: Kieran
    Kunhya <kieran@kunhya.com>

 drivers/block/Kconfig  |    9 +
 drivers/block/Makefile |    1 +
 drivers/block/sxs.c    |  491 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 501 insertions(+)
 create mode 100644 drivers/block/sxs.c

-- 
1.7.9.5


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH] block: Add support for Sony SxS cards Signed-off-by: Kieran Kunhya <kieran@kunhya.com>
  2014-07-07  9:53 [PATCH] Add support for Sony SxS cards Kieran Kunhya
@ 2014-07-07  9:53 ` Kieran Kunhya
  2014-07-07 14:41   ` Randy Dunlap
  0 siblings, 1 reply; 7+ messages in thread
From: Kieran Kunhya @ 2014-07-07  9:53 UTC (permalink / raw)
  To: axboe
  Cc: akpm, jmoyer, rdunlap, fabf, minchan, linus.walleij, abhansali,
	gnomes, jkosina, linux-kernel, Kieran Kunhya

---
 drivers/block/Kconfig  |    9 +
 drivers/block/Makefile |    1 +
 drivers/block/sxs.c    |  491 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 501 insertions(+)
 create mode 100644 drivers/block/sxs.c

diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig
index 014a1cf..50ed13e 100644
--- a/drivers/block/Kconfig
+++ b/drivers/block/Kconfig
@@ -356,6 +356,15 @@ config BLK_DEV_SX8
 
 	  Use devices /dev/sx8/$N and /dev/sx8/$Np$M.
 
+config BLK_DEV_SXS
+        tristate "Sony SxS card support"
+        depends on PCI
+        ---help---
+          Saying Y or M here will enable support for reading
+          from Sony SxS cards.
+
+          It creates a device called /dev/sxs
+
 config BLK_DEV_RAM
 	tristate "RAM block device support"
 	---help---
diff --git a/drivers/block/Makefile b/drivers/block/Makefile
index 02b688d..1e10fb6 100644
--- a/drivers/block/Makefile
+++ b/drivers/block/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_VIRTIO_BLK)	+= virtio_blk.o
 
 obj-$(CONFIG_BLK_DEV_SX8)	+= sx8.o
 obj-$(CONFIG_BLK_DEV_HD)	+= hd.o
+obj-$(CONFIG_BLK_DEV_SXS)       += sxs.o
 
 obj-$(CONFIG_XEN_BLKDEV_FRONTEND)	+= xen-blkfront.o
 obj-$(CONFIG_XEN_BLKDEV_BACKEND)	+= xen-blkback/
diff --git a/drivers/block/sxs.c b/drivers/block/sxs.c
new file mode 100644
index 0000000..fa5588d
--- /dev/null
+++ b/drivers/block/sxs.c
@@ -0,0 +1,491 @@
+/*
+ *  sxs.c: Driver for Sony SxS cards
+ *
+ *  Copyright 2014 Kieran Kunhya
+ *
+ *  Author/maintainer:  Kieran Kunhya <kieran@kunhya.com>
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License.  See the file "COPYING" in the main directory of this archive
+ *  for more details.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/blkdev.h>
+#include <linux/genhd.h>
+#include <linux/hdreg.h>
+#include <linux/bio.h>
+
+#include <linux/completion.h>
+#include <linux/dma-mapping.h>
+#include <linux/log2.h>
+
+#include <asm/byteorder.h>
+
+#define DRV_NAME "sxs"
+
+#define PCI_DEVICE_ID_SXS_81CE 0x81ce
+#define PCI_DEVICE_ID_SXS_905C 0x905c
+
+#define SXS_MASTER_LINK_REG_L 0x10
+#define SXS_MASTER_LINK_REG_H 0x14
+#define SXS_MASTER_ADDR_REG_L 0x18
+#define SXS_MASTER_ADDR_REG_H 0x1c
+#define SXS_MASTER_SIZE_REG   0x20
+#define SXS_ENABLE_REG  0x28
+#define SXS_CONTROL_REG 0x2c
+#define SXS_STATUS_REG  0x6c
+#define SXS_RESPONSE_BUF 0x40
+
+#define KERNEL_SECTOR_SIZE 512
+
+static struct pci_device_id ids[] = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_SONY, PCI_DEVICE_ID_SXS_81CE), },
+	{ PCI_DEVICE(PCI_VENDOR_ID_SONY, PCI_DEVICE_ID_SXS_905C), },
+	{ 0, }
+};
+MODULE_DEVICE_TABLE(pci, ids);
+
+struct sxs_device {
+	struct pci_dev *pci_dev;
+	spinlock_t lock;
+	void __iomem *mmio;
+
+	int    sxs_major;
+	struct gendisk *disk;
+	int    sector_size;
+	int    num_sectors;
+	int    sector_shift;
+	struct request_queue *queue;
+
+	struct completion irq_response;
+};
+
+static int sxs_getgeo(struct block_device *bdev, struct hd_geometry *geo)
+{
+	struct sxs_device *dev = bdev->bd_disk->private_data;
+	long size;
+
+	/* Make something up here */
+	size = dev->num_sectors*(dev->sector_size/KERNEL_SECTOR_SIZE);
+	geo->cylinders = (size & ~0x3f) >> 6;
+	geo->heads = 4;
+	geo->sectors = 16;
+
+	return 0;
+}
+
+static const struct block_device_operations sxs_opts = {
+	.owner          = THIS_MODULE,
+	.getgeo         = sxs_getgeo
+};
+
+static void test_read(struct sxs_device *dev, unsigned long sector,
+		      unsigned long nsect, char *buffer)
+{
+	struct pci_dev *pdev = dev->pci_dev;
+	u32 status;
+	u32 data[4];
+	u16 *tmp;
+	u32 *tmp2;
+
+	void *dma2;
+	dma_addr_t dma2_handle;
+	void *dma3;
+	dma_addr_t dma3_handle;
+
+	sector >>= dev->sector_shift;
+	nsect >>= dev->sector_shift;
+
+	/* Read */
+	dma2 = pci_alloc_consistent(pdev, 8192, &dma2_handle);
+	memset(dma2, 0, 8192);
+
+	dma3 = pci_alloc_consistent(pdev, 8192, &dma3_handle);
+	memset(dma3, 0, 8192);
+
+	tmp = dma2;
+	tmp2 = dma3;
+	tmp2[0] = dma2_handle;
+	tmp2[2] = dma3_handle;
+
+	printk_ratelimited(KERN_DEBUG"CALL %lu %lu\n",
+			   sector & 0xffffffff, nsect & 0xffffffff);
+
+	INIT_COMPLETION(dev->irq_response);
+	status = readl(dev->mmio+SXS_STATUS_REG);
+	data[0] = cpu_to_le32(0x00010028);
+	data[1] = cpu_to_le32(sector & 0xffffffff);
+	data[2] = 0x0;
+	data[3] = cpu_to_le32(nsect & 0xffffffff);
+	memcpy_toio(dev->mmio, data, sizeof(data));
+	writel(0xa0, dev->mmio+SXS_ENABLE_REG);
+	writel(0x80, dev->mmio+SXS_CONTROL_REG);
+
+	if (!wait_for_completion_timeout(&dev->irq_response,
+					 msecs_to_jiffies(5000))) {
+		pr_debug("No IRQ\n");
+	}
+
+	INIT_COMPLETION(dev->irq_response);
+	writel(dma3_handle, dev->mmio+SXS_MASTER_LINK_REG_L);
+	writel(0x0, dev->mmio+SXS_MASTER_LINK_REG_H);
+	writel(0x20, dev->mmio+SXS_CONTROL_REG);
+
+	if (!wait_for_completion_timeout(&dev->irq_response,
+					 msecs_to_jiffies(5000))) {
+		pr_debug("No IRQ\n");
+	}
+
+	/* FIXME: Use DMA properly */
+	memcpy(buffer, dma2, dev->sector_size * nsect);
+
+	printk_ratelimited(KERN_DEBUG"boot-signature %x\n", tmp[255]);
+
+	writel(0, dev->mmio+SXS_ENABLE_REG);
+
+	pci_free_consistent(pdev, 8192, dma3, dma3_handle);
+	pci_free_consistent(pdev, 8192, dma2, dma2_handle);
+}
+
+static void sxs_request(struct request_queue *q, struct bio *bio)
+{
+	int i;
+	struct bio_vec *bvec;
+	char *buffer;
+	unsigned long flags;
+	struct sxs_device *dev = q->queuedata;
+	sector_t sector = bio->bi_sector;
+
+	bio_for_each_segment(bvec, bio, i) {
+		printk_ratelimited(KERN_DEBUG"REQUEST %i %i %i\n",
+				   bio_cur_bytes(bio), bio->bi_vcnt,
+				   bvec->bv_len);
+		buffer = bvec_kmap_irq(bvec, &flags);
+		test_read(dev, sector, bio_cur_bytes(bio) >> 9, buffer);
+		sector += bio_cur_bytes(bio) >> 9;
+		bvec_kunmap_irq(buffer, &flags);
+	}
+
+	bio_endio(bio, 0);
+}
+
+static int setup_disk(struct sxs_device *dev)
+{
+	int ret = 0;
+
+	dev->sxs_major = register_blkdev(0, "sxs");
+	if (!dev->sxs_major) {
+		ret = -EBUSY;
+		goto end;
+	}
+
+	dev->queue = blk_alloc_queue(GFP_KERNEL);
+	if (!dev->queue) {
+		ret = -ENOMEM;
+		goto end;
+	}
+
+	blk_queue_make_request(dev->queue, sxs_request);
+	blk_queue_logical_block_size(dev->queue, dev->sector_size);
+	dev->queue->queuedata = dev;
+
+	/* XXX: can SxS have more partitions? */
+	dev->disk = alloc_disk(4);
+	if (!dev->disk) {
+		pr_notice("could not allocate disk\n");
+		goto end;
+	}
+	dev->disk->major = dev->sxs_major;
+	dev->disk->first_minor = 0;
+	dev->disk->fops = &sxs_opts;
+	dev->disk->queue = dev->queue;
+	dev->disk->private_data = dev;
+	snprintf(dev->disk->disk_name, 32, "sxs");
+	set_capacity(dev->disk, dev->num_sectors*
+				(dev->sector_size/KERNEL_SECTOR_SIZE));
+	add_disk(dev->disk);
+
+end:
+	return ret;
+}
+
+static void read_response_buf(void __iomem *mmio, u32 *output)
+{
+	memcpy_fromio(output, mmio+SXS_RESPONSE_BUF, 4*4);
+}
+
+static int is_write_protected(struct sxs_device *dev)
+{
+	u32 status;
+
+	status = readl(dev->mmio+SXS_STATUS_REG);
+
+	return (status >> 8) & 1;
+}
+
+/* Setup the card exactly as the Windows driver does,
+ * even the strange parts! */
+static int boot_check(struct sxs_device *dev)
+{
+	int i, ret = 0;
+	u32 status;
+	u32 output[4];
+
+	status = readl(dev->mmio+SXS_STATUS_REG);
+	pr_debug("STATUS: %x", status);
+
+	if ((status & 0xa0) != 0xa0) {
+		if ((status & 0xff) != 0x20)
+			writel(1, dev->mmio+SXS_CONTROL_REG);
+
+		for (i = 0; i < 40; i++) {
+			status = readl(dev->mmio+SXS_STATUS_REG);
+			if (status & 0x80)
+				break;
+			msleep(100);
+		}
+		if (i == 40)
+			ret = -EBUSY;
+		else {
+			read_response_buf(dev->mmio, output);
+			/* Not clear what these values mean */
+			pr_debug("Boot Response %x %x %x %x\n",
+				 output[0], output[1],
+				 output[2], output[3]);
+		}
+	}
+
+	return ret;
+}
+
+static irqreturn_t sxs_irq(int irq, void *data)
+{
+	u32 status;
+	irqreturn_t ret = IRQ_HANDLED;
+	struct sxs_device *dev = data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	status = readl(dev->mmio+SXS_STATUS_REG);
+
+	if (status != 0x80000000)
+		writel(0x80000000, dev->mmio+SXS_STATUS_REG);
+
+	printk_ratelimited(KERN_DEBUG"IRQ\n");
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	complete(&dev->irq_response);
+
+	return ret;
+}
+
+static void setup_card(struct sxs_device *dev)
+{
+	u32 status;
+	u32 data[4];
+
+	status = readl(dev->mmio+SXS_STATUS_REG);
+	memset(data, 0, sizeof(data));
+
+	memcpy_toio(dev->mmio, data, sizeof(data));
+	writel(0xa0, dev->mmio+SXS_ENABLE_REG);
+	writel(0x80, dev->mmio+SXS_CONTROL_REG);
+
+	if (!wait_for_completion_timeout(&dev->irq_response,
+					 msecs_to_jiffies(1000))) {
+		pr_debug("No IRQ\n");
+	}
+
+	writel(0, dev->mmio+SXS_ENABLE_REG);
+}
+
+static int get_size(struct sxs_device *dev)
+{
+	struct pci_dev *pdev = dev->pci_dev;
+	u32 status;
+	u32 data[4];
+	int ret = 0;
+	u32 *tmp2;
+
+	void *dma;
+	dma_addr_t dma_handle;
+
+	dma = pci_alloc_consistent(pdev, 8192, &dma_handle);
+	memset(dma, 0, 8192);
+
+	INIT_COMPLETION(dev->irq_response);
+	status = readl(dev->mmio+SXS_STATUS_REG);
+	data[0] = cpu_to_le32(0x8);
+	data[1] = 0x0;
+	data[2] = 0x0;
+	data[3] = cpu_to_le32(0x1);
+	memcpy_toio(dev->mmio, data, sizeof(data));
+	writel(0xa0, dev->mmio+SXS_ENABLE_REG);
+	writel(0x80, dev->mmio+SXS_CONTROL_REG);
+
+	if (!wait_for_completion_timeout(&dev->irq_response,
+					 msecs_to_jiffies(1000))) {
+		pr_debug("No IRQ\n");
+		return -EIO;
+	}
+
+	INIT_COMPLETION(dev->irq_response);
+	writel(dma_handle, dev->mmio+SXS_MASTER_ADDR_REG_L);
+	writel(0x0, dev->mmio+SXS_MASTER_ADDR_REG_H);
+	writel(0x800, dev->mmio+SXS_MASTER_SIZE_REG);
+	writel(0x20, dev->mmio+SXS_CONTROL_REG);
+
+	if (!wait_for_completion_timeout(&dev->irq_response,
+					 msecs_to_jiffies(1000))) {
+		pr_debug("No IRQ\n");
+		ret = -EIO;
+		goto error1;
+	}
+
+	tmp2 = dma;
+
+	writel(0, dev->mmio+SXS_ENABLE_REG);
+
+	/* XXX: this might be different for larger disks */
+	dev->sector_size = le32_to_cpu(tmp2[8]) & 0xffff;
+	dev->num_sectors = le32_to_cpu(tmp2[9]) * le32_to_cpu(tmp2[10]);
+	dev->sector_shift = ilog2(dev->sector_size /
+				  KERNEL_SECTOR_SIZE);
+	pr_debug("Sector size: %x Num sectors: %x\n",
+		 dev->sector_size, dev->num_sectors);
+
+error1:
+	pci_free_consistent(pdev, 8192, dma, dma_handle);
+
+	return ret;
+}
+
+static int probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	int error = 0;
+	struct sxs_device *dev;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		goto error1;
+	spin_lock_init(&dev->lock);
+	dev->pci_dev = pdev;
+
+	error = pci_enable_device(pdev);
+	if (error < 0)
+		goto error2;
+
+	pci_enable_msi(pdev);
+
+	error = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+	if (error)
+		goto error3;
+
+	error = pci_request_regions(pdev, DRV_NAME);
+	if (error)
+		goto error3;
+
+	dev->mmio = pci_ioremap_bar(pdev, 0);
+	if (!dev->mmio)
+		goto error4;
+
+	pci_set_master(pdev);
+
+	if (request_irq(pdev->irq, &sxs_irq, IRQF_SHARED, DRV_NAME, dev))
+		goto error5;
+
+	if (boot_check(dev) < 0)
+		goto error6;
+
+	init_completion(&dev->irq_response);
+
+	setup_card(dev);
+
+	if (get_size(dev) < 0)
+		goto error6;
+
+	if (setup_disk(dev) < 0)
+		goto error7;
+
+	pci_set_drvdata(pdev, dev);
+
+	pr_debug("sxs driver successfully loaded\n");
+	return 0;
+
+error7:
+	if (dev->sxs_major)
+		unregister_blkdev(dev->sxs_major, "sxs");
+
+	if (dev->disk) {
+		del_gendisk(dev->disk);
+		put_disk(dev->disk);
+	}
+
+	if (dev->queue)
+		blk_cleanup_queue(dev->queue);
+error6:
+	free_irq(pdev->irq, dev);
+error5:
+	iounmap(dev->mmio);
+error4:
+	pci_release_regions(pdev);
+error3:
+	pci_disable_device(pdev);
+error2:
+	kfree(dev);
+error1:
+	return error;
+}
+
+static void remove(struct pci_dev *pdev)
+{
+	struct sxs_device *dev = pci_get_drvdata(pdev);
+
+	if (dev->sxs_major)
+		unregister_blkdev(dev->sxs_major, "sxs");
+
+	if (dev->disk) {
+		del_gendisk(dev->disk);
+		put_disk(dev->disk);
+	}
+	if (dev->queue)
+		blk_cleanup_queue(dev->queue);
+	free_irq(pdev->irq, dev);
+	iounmap(dev->mmio);
+	pci_release_regions(pdev);
+	pci_disable_msi(pdev);
+	pci_disable_device(pdev);
+	kfree(dev);
+}
+
+static struct pci_driver sxs_driver = {
+	.name = DRV_NAME,
+	.id_table = ids,
+	.probe = probe,
+	.remove = remove,
+};
+
+static int __init sxs_init(void)
+{
+	return pci_register_driver(&sxs_driver);
+}
+
+static void __exit sxs_exit(void)
+{
+	pci_unregister_driver(&sxs_driver);
+}
+
+MODULE_AUTHOR("Kieran Kunhya");
+MODULE_LICENSE("GPL");
+
+module_init(sxs_init);
+module_exit(sxs_exit);
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH] block: Add support for Sony SxS cards Signed-off-by: Kieran Kunhya <kieran@kunhya.com>
  2014-07-07  9:53 ` [PATCH] block: Add support for Sony SxS cards Signed-off-by: Kieran Kunhya <kieran@kunhya.com> Kieran Kunhya
@ 2014-07-07 14:41   ` Randy Dunlap
  2014-07-20  0:01     ` [PATCHv2] block: Add support for Sony SxS cards Kieran Kunhya
  0 siblings, 1 reply; 7+ messages in thread
From: Randy Dunlap @ 2014-07-07 14:41 UTC (permalink / raw)
  To: Kieran Kunhya, axboe
  Cc: akpm, jmoyer, fabf, minchan, linus.walleij, abhansali, gnomes,
	jkosina, linux-kernel

On 07/07/2014 02:53 AM, Kieran Kunhya wrote:

Signed-off-by: should be here,
not in the Subject line.

> ---
>  drivers/block/Kconfig  |    9 +
>  drivers/block/Makefile |    1 +
>  drivers/block/sxs.c    |  491 ++++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 501 insertions(+)
>  create mode 100644 drivers/block/sxs.c
> 
> diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig
> index 014a1cf..50ed13e 100644
> --- a/drivers/block/Kconfig
> +++ b/drivers/block/Kconfig
> @@ -356,6 +356,15 @@ config BLK_DEV_SX8
>  
>  	  Use devices /dev/sx8/$N and /dev/sx8/$Np$M.
>  
> +config BLK_DEV_SXS
> +        tristate "Sony SxS card support"
> +        depends on PCI
> +        ---help---
> +          Saying Y or M here will enable support for reading
> +          from Sony SxS cards.
> +
> +          It creates a device called /dev/sxs
> +

Use tab to indent tristate, depends, and help.
Use tab + 2 spaces to indent the help text.

>  config BLK_DEV_RAM
>  	tristate "RAM block device support"
>  	---help---
> diff --git a/drivers/block/Makefile b/drivers/block/Makefile
> index 02b688d..1e10fb6 100644
> --- a/drivers/block/Makefile
> +++ b/drivers/block/Makefile
> @@ -33,6 +33,7 @@ obj-$(CONFIG_VIRTIO_BLK)	+= virtio_blk.o
>  
>  obj-$(CONFIG_BLK_DEV_SX8)	+= sx8.o
>  obj-$(CONFIG_BLK_DEV_HD)	+= hd.o
> +obj-$(CONFIG_BLK_DEV_SXS)       += sxs.o

Use tab(s) here             ^^^^^^
not spaces.

>  
>  obj-$(CONFIG_XEN_BLKDEV_FRONTEND)	+= xen-blkfront.o
>  obj-$(CONFIG_XEN_BLKDEV_BACKEND)	+= xen-blkback/


-- 
~Randy

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCHv2] block: Add support for Sony SxS cards
  2014-07-07 14:41   ` Randy Dunlap
@ 2014-07-20  0:01     ` Kieran Kunhya
  2014-09-27 23:43       ` [PATCHv3] " Kieran Kunhya
  0 siblings, 1 reply; 7+ messages in thread
From: Kieran Kunhya @ 2014-07-20  0:01 UTC (permalink / raw)
  To: axboe
  Cc: akpm, jmoyer, rdunlap, fabf, minchan, linus.walleij, abhansali,
	gnomes, jkosina, linux-kernel, Kieran Kunhya


Signed-off-by: Kieran Kunhya <kieran@kunhya.com>
---
 drivers/block/Kconfig  |    9 +
 drivers/block/Makefile |    1 +
 drivers/block/sxs.c    |  491 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 501 insertions(+)
 create mode 100644 drivers/block/sxs.c

diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig
index 014a1cf..8ebb655 100644
--- a/drivers/block/Kconfig
+++ b/drivers/block/Kconfig
@@ -356,6 +356,15 @@ config BLK_DEV_SX8
 
 	  Use devices /dev/sx8/$N and /dev/sx8/$Np$M.
 
+config BLK_DEV_SXS
+	tristate "Sony SxS card support"
+	depends on PCI
+	---help---
+	  Saying Y or M here will enable support for reading
+	  from Sony SxS cards.
+
+	  It creates a device called /dev/sxs
+
 config BLK_DEV_RAM
 	tristate "RAM block device support"
 	---help---
diff --git a/drivers/block/Makefile b/drivers/block/Makefile
index 02b688d..59b9c79 100644
--- a/drivers/block/Makefile
+++ b/drivers/block/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_VIRTIO_BLK)	+= virtio_blk.o
 
 obj-$(CONFIG_BLK_DEV_SX8)	+= sx8.o
 obj-$(CONFIG_BLK_DEV_HD)	+= hd.o
+obj-$(CONFIG_BLK_DEV_SXS)	+= sxs.o
 
 obj-$(CONFIG_XEN_BLKDEV_FRONTEND)	+= xen-blkfront.o
 obj-$(CONFIG_XEN_BLKDEV_BACKEND)	+= xen-blkback/
diff --git a/drivers/block/sxs.c b/drivers/block/sxs.c
new file mode 100644
index 0000000..88ccd61
--- /dev/null
+++ b/drivers/block/sxs.c
@@ -0,0 +1,491 @@
+/*
+ *  sxs.c: Driver for Sony SxS cards
+ *
+ *  Copyright 2014 Kieran Kunhya
+ *
+ *  Author/maintainer:  Kieran Kunhya <kieran@kunhya.com>
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License.  See the file "COPYING" in the main directory of this archive
+ *  for more details.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/blkdev.h>
+#include <linux/genhd.h>
+#include <linux/hdreg.h>
+#include <linux/bio.h>
+
+#include <linux/completion.h>
+#include <linux/dma-mapping.h>
+#include <linux/log2.h>
+
+#include <asm/byteorder.h>
+
+#define DRV_NAME "sxs"
+
+#define PCI_DEVICE_ID_SXS_81CE 0x81ce
+#define PCI_DEVICE_ID_SXS_905C 0x905c
+
+#define SXS_MASTER_LINK_REG_L 0x10
+#define SXS_MASTER_LINK_REG_H 0x14
+#define SXS_MASTER_ADDR_REG_L 0x18
+#define SXS_MASTER_ADDR_REG_H 0x1c
+#define SXS_MASTER_SIZE_REG   0x20
+#define SXS_ENABLE_REG  0x28
+#define SXS_CONTROL_REG 0x2c
+#define SXS_STATUS_REG  0x6c
+#define SXS_RESPONSE_BUF 0x40
+
+#define KERNEL_SECTOR_SIZE 512
+
+static struct pci_device_id ids[] = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_SONY, PCI_DEVICE_ID_SXS_81CE), },
+	{ PCI_DEVICE(PCI_VENDOR_ID_SONY, PCI_DEVICE_ID_SXS_905C), },
+	{ 0, }
+};
+MODULE_DEVICE_TABLE(pci, ids);
+
+struct sxs_device {
+	struct pci_dev *pci_dev;
+	spinlock_t lock;
+	void __iomem *mmio;
+
+	int    sxs_major;
+	struct gendisk *disk;
+	int    sector_size;
+	int    num_sectors;
+	int    sector_shift;
+	struct request_queue *queue;
+
+	struct completion irq_response;
+};
+
+static int sxs_getgeo(struct block_device *bdev, struct hd_geometry *geo)
+{
+	struct sxs_device *dev = bdev->bd_disk->private_data;
+	long size;
+
+	/* Make something up here */
+	size = dev->num_sectors*(dev->sector_size/KERNEL_SECTOR_SIZE);
+	geo->cylinders = (size & ~0x3f) >> 6;
+	geo->heads = 4;
+	geo->sectors = 16;
+
+	return 0;
+}
+
+static const struct block_device_operations sxs_opts = {
+	.owner          = THIS_MODULE,
+	.getgeo         = sxs_getgeo
+};
+
+static void memcpy_read(struct sxs_device *dev, unsigned long sector,
+						unsigned long nsect, char *buffer)
+{
+	struct pci_dev *pdev = dev->pci_dev;
+	u32 status;
+	u32 data[4];
+	u16 *tmp;
+	u32 *tmp2;
+
+	void *dma2;
+	dma_addr_t dma2_handle;
+	void *dma3;
+	dma_addr_t dma3_handle;
+
+	sector >>= dev->sector_shift;
+	nsect >>= dev->sector_shift;
+
+	/* Read */
+	dma2 = pci_alloc_consistent(pdev, 8192, &dma2_handle);
+	memset(dma2, 0, 8192);
+
+	dma3 = pci_alloc_consistent(pdev, 8192, &dma3_handle);
+	memset(dma3, 0, 8192);
+
+	tmp = dma2;
+	tmp2 = dma3;
+	tmp2[0] = dma2_handle;
+	tmp2[2] = dma3_handle;
+
+	printk_ratelimited(KERN_DEBUG"CALL %lu %lu\n",
+			   sector & 0xffffffff, nsect & 0xffffffff);
+
+	INIT_COMPLETION(dev->irq_response);
+	status = readl(dev->mmio+SXS_STATUS_REG);
+	data[0] = cpu_to_le32(0x00010028);
+	data[1] = cpu_to_le32(sector & 0xffffffff);
+	data[2] = 0x0;
+	data[3] = cpu_to_le32(nsect & 0xffffffff);
+	memcpy_toio(dev->mmio, data, sizeof(data));
+	writel(0xa0, dev->mmio+SXS_ENABLE_REG);
+	writel(0x80, dev->mmio+SXS_CONTROL_REG);
+
+	if (!wait_for_completion_timeout(&dev->irq_response,
+					 msecs_to_jiffies(5000))) {
+		pr_debug("No IRQ\n");
+	}
+
+	INIT_COMPLETION(dev->irq_response);
+	writel(dma3_handle, dev->mmio+SXS_MASTER_LINK_REG_L);
+	writel(0x0, dev->mmio+SXS_MASTER_LINK_REG_H);
+	writel(0x20, dev->mmio+SXS_CONTROL_REG);
+
+	if (!wait_for_completion_timeout(&dev->irq_response,
+					 msecs_to_jiffies(5000))) {
+		pr_debug("No IRQ\n");
+	}
+
+	/* FIXME: Use DMA properly */
+	memcpy(buffer, dma2, dev->sector_size * nsect);
+
+	printk_ratelimited(KERN_DEBUG"boot-signature %x\n", tmp[255]);
+
+	writel(0, dev->mmio+SXS_ENABLE_REG);
+
+	pci_free_consistent(pdev, 8192, dma3, dma3_handle);
+	pci_free_consistent(pdev, 8192, dma2, dma2_handle);
+}
+
+static void sxs_request(struct request_queue *q, struct bio *bio)
+{
+	int i;
+	struct bio_vec *bvec;
+	char *buffer;
+	unsigned long flags;
+	struct sxs_device *dev = q->queuedata;
+	sector_t sector = bio->bi_sector;
+
+	bio_for_each_segment(bvec, bio, i) {
+		printk_ratelimited(KERN_DEBUG"REQUEST %i %i %i\n",
+				   bio_cur_bytes(bio), bio->bi_vcnt,
+				   bvec->bv_len);
+		buffer = bvec_kmap_irq(bvec, &flags);
+		memcpy_read(dev, sector, bio_cur_bytes(bio) >> 9, buffer);
+		sector += bio_cur_bytes(bio) >> 9;
+		bvec_kunmap_irq(buffer, &flags);
+	}
+
+	bio_endio(bio, 0);
+}
+
+static int setup_disk(struct sxs_device *dev)
+{
+	int ret = 0;
+
+	dev->sxs_major = register_blkdev(0, "sxs");
+	if (!dev->sxs_major) {
+		ret = -EBUSY;
+		goto end;
+	}
+
+	dev->queue = blk_alloc_queue(GFP_KERNEL);
+	if (!dev->queue) {
+		ret = -ENOMEM;
+		goto end;
+	}
+
+	blk_queue_make_request(dev->queue, sxs_request);
+	blk_queue_logical_block_size(dev->queue, dev->sector_size);
+	dev->queue->queuedata = dev;
+
+	/* XXX: can SxS have more partitions? */
+	dev->disk = alloc_disk(4);
+	if (!dev->disk) {
+		pr_notice("could not allocate disk\n");
+		goto end;
+	}
+	dev->disk->major = dev->sxs_major;
+	dev->disk->first_minor = 0;
+	dev->disk->fops = &sxs_opts;
+	dev->disk->queue = dev->queue;
+	dev->disk->private_data = dev;
+	snprintf(dev->disk->disk_name, 32, "sxs");
+	set_capacity(dev->disk, dev->num_sectors*
+				(dev->sector_size/KERNEL_SECTOR_SIZE));
+	add_disk(dev->disk);
+
+end:
+	return ret;
+}
+
+static void read_response_buf(void __iomem *mmio, u32 *output)
+{
+	memcpy_fromio(output, mmio+SXS_RESPONSE_BUF, 4*4);
+}
+
+static int is_write_protected(struct sxs_device *dev)
+{
+	u32 status;
+
+	status = readl(dev->mmio+SXS_STATUS_REG);
+
+	return (status >> 8) & 1;
+}
+
+/* Setup the card exactly as the Windows driver does,
+ * even the strange parts! */
+static int boot_check(struct sxs_device *dev)
+{
+	int i, ret = 0;
+	u32 status;
+	u32 output[4];
+
+	status = readl(dev->mmio+SXS_STATUS_REG);
+	pr_debug("STATUS: %x", status);
+
+	if ((status & 0xa0) != 0xa0) {
+		if ((status & 0xff) != 0x20)
+			writel(1, dev->mmio+SXS_CONTROL_REG);
+
+		for (i = 0; i < 40; i++) {
+			status = readl(dev->mmio+SXS_STATUS_REG);
+			if (status & 0x80)
+				break;
+			msleep(100);
+		}
+		if (i == 40)
+			ret = -EBUSY;
+		else {
+			read_response_buf(dev->mmio, output);
+			/* Not clear what these values mean */
+			pr_debug("Boot Response %x %x %x %x\n",
+				 output[0], output[1],
+				 output[2], output[3]);
+		}
+	}
+
+	return ret;
+}
+
+static irqreturn_t sxs_irq(int irq, void *data)
+{
+	u32 status;
+	irqreturn_t ret = IRQ_HANDLED;
+	struct sxs_device *dev = data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	status = readl(dev->mmio+SXS_STATUS_REG);
+
+	if (status != 0x80000000)
+		writel(0x80000000, dev->mmio+SXS_STATUS_REG);
+
+	printk_ratelimited(KERN_DEBUG"IRQ\n");
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	complete(&dev->irq_response);
+
+	return ret;
+}
+
+static void setup_card(struct sxs_device *dev)
+{
+	u32 status;
+	u32 data[4];
+
+	status = readl(dev->mmio+SXS_STATUS_REG);
+	memset(data, 0, sizeof(data));
+
+	memcpy_toio(dev->mmio, data, sizeof(data));
+	writel(0xa0, dev->mmio+SXS_ENABLE_REG);
+	writel(0x80, dev->mmio+SXS_CONTROL_REG);
+
+	if (!wait_for_completion_timeout(&dev->irq_response,
+					 msecs_to_jiffies(1000))) {
+		pr_debug("No IRQ\n");
+	}
+
+	writel(0, dev->mmio+SXS_ENABLE_REG);
+}
+
+static int get_size(struct sxs_device *dev)
+{
+	struct pci_dev *pdev = dev->pci_dev;
+	u32 status;
+	u32 data[4];
+	int ret = 0;
+	u32 *tmp2;
+
+	void *dma;
+	dma_addr_t dma_handle;
+
+	dma = pci_alloc_consistent(pdev, 8192, &dma_handle);
+	memset(dma, 0, 8192);
+
+	INIT_COMPLETION(dev->irq_response);
+	status = readl(dev->mmio+SXS_STATUS_REG);
+	data[0] = cpu_to_le32(0x8);
+	data[1] = 0x0;
+	data[2] = 0x0;
+	data[3] = cpu_to_le32(0x1);
+	memcpy_toio(dev->mmio, data, sizeof(data));
+	writel(0xa0, dev->mmio+SXS_ENABLE_REG);
+	writel(0x80, dev->mmio+SXS_CONTROL_REG);
+
+	if (!wait_for_completion_timeout(&dev->irq_response,
+					 msecs_to_jiffies(1000))) {
+		pr_debug("No IRQ\n");
+		return -EIO;
+	}
+
+	INIT_COMPLETION(dev->irq_response);
+	writel(dma_handle, dev->mmio+SXS_MASTER_ADDR_REG_L);
+	writel(0x0, dev->mmio+SXS_MASTER_ADDR_REG_H);
+	writel(0x800, dev->mmio+SXS_MASTER_SIZE_REG);
+	writel(0x20, dev->mmio+SXS_CONTROL_REG);
+
+	if (!wait_for_completion_timeout(&dev->irq_response,
+					 msecs_to_jiffies(1000))) {
+		pr_debug("No IRQ\n");
+		ret = -EIO;
+		goto error1;
+	}
+
+	tmp2 = dma;
+
+	writel(0, dev->mmio+SXS_ENABLE_REG);
+
+	/* XXX: this might be different for larger disks */
+	dev->sector_size = le32_to_cpu(tmp2[8]) & 0xffff;
+	dev->num_sectors = le32_to_cpu(tmp2[9]) * le32_to_cpu(tmp2[10]);
+	dev->sector_shift = ilog2(dev->sector_size /
+				  KERNEL_SECTOR_SIZE);
+	pr_debug("Sector size: %x Num sectors: %x\n",
+		 dev->sector_size, dev->num_sectors);
+
+error1:
+	pci_free_consistent(pdev, 8192, dma, dma_handle);
+
+	return ret;
+}
+
+static int probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	int error = 0;
+	struct sxs_device *dev;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		goto error1;
+	spin_lock_init(&dev->lock);
+	dev->pci_dev = pdev;
+
+	error = pci_enable_device(pdev);
+	if (error < 0)
+		goto error2;
+
+	pci_enable_msi(pdev);
+
+	error = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+	if (error)
+		goto error3;
+
+	error = pci_request_regions(pdev, DRV_NAME);
+	if (error)
+		goto error3;
+
+	dev->mmio = pci_ioremap_bar(pdev, 0);
+	if (!dev->mmio)
+		goto error4;
+
+	pci_set_master(pdev);
+
+	if (request_irq(pdev->irq, &sxs_irq, IRQF_SHARED, DRV_NAME, dev))
+		goto error5;
+
+	if (boot_check(dev) < 0)
+		goto error6;
+
+	init_completion(&dev->irq_response);
+
+	setup_card(dev);
+
+	if (get_size(dev) < 0)
+		goto error6;
+
+	if (setup_disk(dev) < 0)
+		goto error7;
+
+	pci_set_drvdata(pdev, dev);
+
+	pr_debug("sxs driver successfully loaded\n");
+	return 0;
+
+error7:
+	if (dev->sxs_major)
+		unregister_blkdev(dev->sxs_major, "sxs");
+
+	if (dev->disk) {
+		del_gendisk(dev->disk);
+		put_disk(dev->disk);
+	}
+
+	if (dev->queue)
+		blk_cleanup_queue(dev->queue);
+error6:
+	free_irq(pdev->irq, dev);
+error5:
+	iounmap(dev->mmio);
+error4:
+	pci_release_regions(pdev);
+error3:
+	pci_disable_device(pdev);
+error2:
+	kfree(dev);
+error1:
+	return error;
+}
+
+static void remove(struct pci_dev *pdev)
+{
+	struct sxs_device *dev = pci_get_drvdata(pdev);
+
+	if (dev->sxs_major)
+		unregister_blkdev(dev->sxs_major, "sxs");
+
+	if (dev->disk) {
+		del_gendisk(dev->disk);
+		put_disk(dev->disk);
+	}
+	if (dev->queue)
+		blk_cleanup_queue(dev->queue);
+	free_irq(pdev->irq, dev);
+	iounmap(dev->mmio);
+	pci_release_regions(pdev);
+	pci_disable_msi(pdev);
+	pci_disable_device(pdev);
+	kfree(dev);
+}
+
+static struct pci_driver sxs_driver = {
+	.name = DRV_NAME,
+	.id_table = ids,
+	.probe = probe,
+	.remove = remove,
+};
+
+static int __init sxs_init(void)
+{
+	return pci_register_driver(&sxs_driver);
+}
+
+static void __exit sxs_exit(void)
+{
+	pci_unregister_driver(&sxs_driver);
+}
+
+MODULE_AUTHOR("Kieran Kunhya");
+MODULE_LICENSE("GPL");
+
+module_init(sxs_init);
+module_exit(sxs_exit);
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCHv3] block: Add support for Sony SxS cards
  2014-07-20  0:01     ` [PATCHv2] block: Add support for Sony SxS cards Kieran Kunhya
@ 2014-09-27 23:43       ` Kieran Kunhya
  2014-09-28  2:31         ` Jens Axboe
  0 siblings, 1 reply; 7+ messages in thread
From: Kieran Kunhya @ 2014-09-27 23:43 UTC (permalink / raw)
  To: Kieran Kunhya, Andrew Morton, David S. Miller,
	Greg Kroah-Hartman, Joe Perches, Mauro Carvalho Chehab,
	Antti Palosaari, Jingoo Han, Jens Axboe, Ramprasad Chinthekindi,
	Pekka Enberg, Minchan Kim, Fabian Frederick, Akhil Bhansali,
	Jiri Kosina, Alan
  Cc: linux-kernel

Signed-off-by: Kieran Kunhya <kieran@kunhya.com>
---
 MAINTAINERS            |    5 +
 drivers/block/Kconfig  |    9 +
 drivers/block/Makefile |    1 +
 drivers/block/sxs.c    |  494 ++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 509 insertions(+)
 create mode 100644 drivers/block/sxs.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 3705430..f3a5231 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8539,6 +8539,11 @@ M:	Maxim Levitsky <maximlevitsky@gmail.com>
 S:	Maintained
 F:	drivers/memstick/core/ms_block.*
 
+SONY SXS CARD SUPPORT
+M:      Kieran Kunhya <kieran@kunhya.com>
+S:      Maintained
+F:      drivers/block/sxs.c
+
 SOUND
 M:	Jaroslav Kysela <perex@perex.cz>
 M:	Takashi Iwai <tiwai@suse.de>
diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig
index 014a1cf..0b41ee0 100644
--- a/drivers/block/Kconfig
+++ b/drivers/block/Kconfig
@@ -356,6 +356,15 @@ config BLK_DEV_SX8
 
 	  Use devices /dev/sx8/$N and /dev/sx8/$Np$M.
 
+config BLK_DEV_SXS
+       tristate "Sony SxS card support"
+       depends on PCI
+       ---help---
+         Saying Y or M here will enable support for reading
+         from Sony SxS cards.
+
+         It creates a device called /dev/sxs
+
 config BLK_DEV_RAM
 	tristate "RAM block device support"
 	---help---
diff --git a/drivers/block/Makefile b/drivers/block/Makefile
index 02b688d..59b9c79 100644
--- a/drivers/block/Makefile
+++ b/drivers/block/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_VIRTIO_BLK)	+= virtio_blk.o
 
 obj-$(CONFIG_BLK_DEV_SX8)	+= sx8.o
 obj-$(CONFIG_BLK_DEV_HD)	+= hd.o
+obj-$(CONFIG_BLK_DEV_SXS)	+= sxs.o
 
 obj-$(CONFIG_XEN_BLKDEV_FRONTEND)	+= xen-blkfront.o
 obj-$(CONFIG_XEN_BLKDEV_BACKEND)	+= xen-blkback/
diff --git a/drivers/block/sxs.c b/drivers/block/sxs.c
new file mode 100644
index 0000000..a2da71d
--- /dev/null
+++ b/drivers/block/sxs.c
@@ -0,0 +1,494 @@
+/*
+ *  sxs.c: Driver for Sony SxS cards
+ *
+ *  Copyright 2014 Kieran Kunhya
+ *
+ *  Author/maintainer:  Kieran Kunhya <kieran@kunhya.com>
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License.  See the file "COPYING" in the main directory of this archive
+ *  for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/blkdev.h>
+#include <linux/genhd.h>
+#include <linux/hdreg.h>
+#include <linux/bio.h>
+
+#include <linux/completion.h>
+#include <linux/dma-mapping.h>
+#include <linux/log2.h>
+
+#include <asm/byteorder.h>
+
+#define DRV_NAME "sxs"
+
+#define PCI_DEVICE_ID_SXS_81CE 0x81ce
+#define PCI_DEVICE_ID_SXS_905C 0x905c
+
+#define SXS_MASTER_LINK_REG_L 0x10
+#define SXS_MASTER_LINK_REG_H 0x14
+#define SXS_MASTER_ADDR_REG_L 0x18
+#define SXS_MASTER_ADDR_REG_H 0x1c
+#define SXS_MASTER_SIZE_REG   0x20
+#define SXS_ENABLE_REG  0x28
+#define SXS_CONTROL_REG 0x2c
+#define SXS_STATUS_REG  0x6c
+#define SXS_RESPONSE_BUF 0x40
+
+#define KERNEL_SECTOR_SIZE 512
+
+static struct pci_device_id ids[] = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_SONY, PCI_DEVICE_ID_SXS_81CE), },
+	{ PCI_DEVICE(PCI_VENDOR_ID_SONY, PCI_DEVICE_ID_SXS_905C), },
+	{ 0, }
+};
+MODULE_DEVICE_TABLE(pci, ids);
+
+struct sxs_device {
+	struct pci_dev *pci_dev;
+	spinlock_t lock;
+	void __iomem *mmio;
+
+	int    sxs_major;
+	struct gendisk *disk;
+	int    sector_size;
+	int    num_sectors;
+	int    sector_shift;
+	struct request_queue *queue;
+
+	struct completion irq_response;
+};
+
+static int sxs_getgeo(struct block_device *bdev, struct hd_geometry *geo)
+{
+	struct sxs_device *dev = bdev->bd_disk->private_data;
+	long size;
+
+	/* Make something up here */
+	size = dev->num_sectors*(dev->sector_size/KERNEL_SECTOR_SIZE);
+	geo->cylinders = (size & ~0x3f) >> 6;
+	geo->heads = 4;
+	geo->sectors = 16;
+
+	return 0;
+}
+
+static const struct block_device_operations sxs_opts = {
+	.owner          = THIS_MODULE,
+	.getgeo         = sxs_getgeo
+};
+
+static void sxs_memcpy_read(struct sxs_device *dev, unsigned long sector,
+			    unsigned long nsect, char *buffer)
+{
+	struct pci_dev *pdev = dev->pci_dev;
+	u32 status;
+	u32 data[4];
+	u16 *tmp;
+	u32 *tmp2;
+
+	void *dma2;
+	dma_addr_t dma2_handle;
+	void *dma3;
+	dma_addr_t dma3_handle;
+
+	sector >>= dev->sector_shift;
+	nsect >>= dev->sector_shift;
+
+	/* Read */
+	dma2 = pci_alloc_consistent(pdev, 8192, &dma2_handle);
+	dma3 = pci_alloc_consistent(pdev, 8192, &dma3_handle);
+
+	tmp = dma2;
+	tmp2 = dma3;
+	tmp2[0] = dma2_handle;
+	tmp2[2] = dma3_handle;
+
+	dev_dbg_ratelimited(&pdev->dev, "CALL %lu %lu\n",
+			    sector & 0xffffffff, nsect & 0xffffffff);
+
+	reinit_completion(&dev->irq_response);
+	status = readl(dev->mmio+SXS_STATUS_REG);
+	data[0] = cpu_to_le32(0x00010028);
+	data[1] = cpu_to_le32(sector & 0xffffffff);
+	data[2] = 0x0;
+	data[3] = cpu_to_le32(nsect & 0xffffffff);
+	memcpy_toio(dev->mmio, data, sizeof(data));
+	writel(0xa0, dev->mmio+SXS_ENABLE_REG);
+	writel(0x80, dev->mmio+SXS_CONTROL_REG);
+
+	if (!wait_for_completion_timeout(&dev->irq_response,
+					 msecs_to_jiffies(5000))) {
+		dev_dbg(&pdev->dev, "No IRQ\n");
+	}
+
+	reinit_completion(&dev->irq_response);
+	writel(dma3_handle, dev->mmio+SXS_MASTER_LINK_REG_L);
+	writel(0x0, dev->mmio+SXS_MASTER_LINK_REG_H);
+	writel(0x20, dev->mmio+SXS_CONTROL_REG);
+
+	if (!wait_for_completion_timeout(&dev->irq_response,
+					 msecs_to_jiffies(5000))) {
+		dev_dbg(&pdev->dev, "No IRQ\n");
+	}
+
+	/* FIXME: Use DMA properly */
+	memcpy(buffer, dma2, dev->sector_size * nsect);
+
+	dev_dbg_ratelimited(&pdev->dev, "boot-signature %x\n",
+			    tmp[255]);
+
+	writel(0, dev->mmio+SXS_ENABLE_REG);
+
+	pci_free_consistent(pdev, 8192, dma3, dma3_handle);
+	pci_free_consistent(pdev, 8192, dma2, dma2_handle);
+}
+
+static void sxs_request(struct request_queue *q, struct bio *bio)
+{
+	struct bvec_iter iter;
+	struct bio_vec bvec;
+	char *buffer;
+	unsigned long flags;
+	struct sxs_device *dev = q->queuedata;
+	struct pci_dev *pdev = dev->pci_dev;
+	sector_t sector = bio->bi_iter.bi_sector;
+
+	bio_for_each_segment(bvec, bio, iter) {
+		dev_dbg_ratelimited(&pdev->dev, "REQUEST %i %i %i\n",
+				    bio_cur_bytes(bio), bio->bi_vcnt,
+				    bvec.bv_len);
+		buffer = bvec_kmap_irq(&bvec, &flags);
+		sxs_memcpy_read(dev, sector, bio_cur_bytes(bio) >> 9,
+				buffer);
+		sector += bio_cur_bytes(bio) >> 9;
+		bvec_kunmap_irq(buffer, &flags);
+	}
+
+	bio_endio(bio, 0);
+}
+
+static int sxs_setup_disk(struct sxs_device *dev)
+{
+	struct pci_dev *pdev = dev->pci_dev;
+	int ret = 0;
+
+	dev->sxs_major = register_blkdev(0, "sxs");
+	if (!dev->sxs_major) {
+		ret = -EBUSY;
+		goto end;
+	}
+
+	dev->queue = blk_alloc_queue(GFP_KERNEL);
+	if (!dev->queue) {
+		ret = -ENOMEM;
+		goto end;
+	}
+
+	blk_queue_make_request(dev->queue, sxs_request);
+	blk_queue_logical_block_size(dev->queue, dev->sector_size);
+	dev->queue->queuedata = dev;
+
+	/* XXX: can SxS have more partitions? */
+	dev->disk = alloc_disk(4);
+	if (!dev->disk) {
+		dev_notice(&pdev->dev, "could not allocate disk\n");
+		goto end;
+	}
+	dev->disk->major = dev->sxs_major;
+	dev->disk->first_minor = 0;
+	dev->disk->fops = &sxs_opts;
+	dev->disk->queue = dev->queue;
+	dev->disk->private_data = dev;
+	snprintf(dev->disk->disk_name, 32, "sxs");
+	set_capacity(dev->disk, dev->num_sectors*
+				(dev->sector_size/KERNEL_SECTOR_SIZE));
+	add_disk(dev->disk);
+
+end:
+	return ret;
+}
+
+static void sxs_read_response_buf(void __iomem *mmio, u32 *output)
+{
+	memcpy_fromio(output, mmio+SXS_RESPONSE_BUF, 4*4);
+}
+
+static int sxs_is_write_protected(struct sxs_device *dev)
+{
+	u32 status;
+
+	status = readl(dev->mmio+SXS_STATUS_REG);
+
+	return (status >> 8) & 1;
+}
+
+/* Setup the card exactly as the Windows driver does,
+ * even the strange parts! */
+static int sxs_boot_check(struct sxs_device *dev)
+{
+	struct pci_dev *pdev = dev->pci_dev;
+	int i, ret = 0;
+	u32 status;
+	u32 output[4];
+
+	status = readl(dev->mmio+SXS_STATUS_REG);
+	dev_dbg(&pdev->dev, "STATUS: %x", status);
+
+	if ((status & 0xa0) != 0xa0) {
+		if ((status & 0xff) != 0x20)
+			writel(1, dev->mmio+SXS_CONTROL_REG);
+
+		for (i = 0; i < 40; i++) {
+			status = readl(dev->mmio+SXS_STATUS_REG);
+			if (status & 0x80)
+				break;
+			msleep(100);
+		}
+		if (i == 40)
+			ret = -EBUSY;
+		else {
+			sxs_read_response_buf(dev->mmio, output);
+			/* Not clear what these values mean */
+			pr_debug("Boot Response %x %x %x %x\n",
+				 output[0], output[1],
+				 output[2], output[3]);
+		}
+	}
+
+	return ret;
+}
+
+static irqreturn_t sxs_irq(int irq, void *data)
+{
+	u32 status;
+	irqreturn_t ret = IRQ_HANDLED;
+	struct sxs_device *dev = data;
+	struct pci_dev *pdev = dev->pci_dev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	status = readl(dev->mmio+SXS_STATUS_REG);
+
+	if (status != 0x80000000)
+		writel(0x80000000, dev->mmio+SXS_STATUS_REG);
+
+	dev_dbg_ratelimited(&pdev->dev, "IRQ\n");
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	complete(&dev->irq_response);
+
+	return ret;
+}
+
+static void sxs_setup_card(struct sxs_device *dev)
+{
+	struct pci_dev *pdev = dev->pci_dev;
+	u32 status;
+	u32 data[4];
+
+	status = readl(dev->mmio+SXS_STATUS_REG);
+	memset(data, 0, sizeof(data));
+
+	memcpy_toio(dev->mmio, data, sizeof(data));
+	writel(0xa0, dev->mmio+SXS_ENABLE_REG);
+	writel(0x80, dev->mmio+SXS_CONTROL_REG);
+
+	if (!wait_for_completion_timeout(&dev->irq_response,
+					 msecs_to_jiffies(1000))) {
+		dev_dbg(&pdev->dev, "No IRQ\n");
+	}
+
+	writel(0, dev->mmio+SXS_ENABLE_REG);
+}
+
+static int sxs_get_size(struct sxs_device *dev)
+{
+	struct pci_dev *pdev = dev->pci_dev;
+	u32 status;
+	u32 data[4];
+	int ret = 0;
+	u32 *tmp2;
+
+	void *dma;
+	dma_addr_t dma_handle;
+
+	dma = pci_alloc_consistent(pdev, 8192, &dma_handle);
+
+	reinit_completion(&dev->irq_response);
+	status = readl(dev->mmio+SXS_STATUS_REG);
+	data[0] = cpu_to_le32(0x8);
+	data[1] = 0x0;
+	data[2] = 0x0;
+	data[3] = cpu_to_le32(0x1);
+	memcpy_toio(dev->mmio, data, sizeof(data));
+	writel(0xa0, dev->mmio+SXS_ENABLE_REG);
+	writel(0x80, dev->mmio+SXS_CONTROL_REG);
+
+	if (!wait_for_completion_timeout(&dev->irq_response,
+					 msecs_to_jiffies(1000))) {
+		dev_dbg(&pdev->dev, "No IRQ\n");
+		return -EIO;
+	}
+
+	reinit_completion(&dev->irq_response);
+	writel(dma_handle, dev->mmio+SXS_MASTER_ADDR_REG_L);
+	writel(0x0, dev->mmio+SXS_MASTER_ADDR_REG_H);
+	writel(0x800, dev->mmio+SXS_MASTER_SIZE_REG);
+	writel(0x20, dev->mmio+SXS_CONTROL_REG);
+
+	if (!wait_for_completion_timeout(&dev->irq_response,
+					 msecs_to_jiffies(1000))) {
+		dev_dbg(&pdev->dev, "No IRQ\n");
+		ret = -EIO;
+		goto error1;
+	}
+
+	tmp2 = dma;
+
+	writel(0, dev->mmio+SXS_ENABLE_REG);
+
+	/* XXX: this might be different for larger disks */
+	dev->sector_size = le32_to_cpu(tmp2[8]) & 0xffff;
+	dev->num_sectors = le32_to_cpu(tmp2[9]) * le32_to_cpu(tmp2[10]);
+	dev->sector_shift = ilog2(dev->sector_size /
+				  KERNEL_SECTOR_SIZE);
+	pr_debug("Sector size: %x Num sectors: %x\n",
+		 dev->sector_size, dev->num_sectors);
+
+error1:
+	pci_free_consistent(pdev, 8192, dma, dma_handle);
+
+	return ret;
+}
+
+static int sxs_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	int error = 0;
+	struct sxs_device *dev;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		goto error1;
+	spin_lock_init(&dev->lock);
+	dev->pci_dev = pdev;
+
+	error = pci_enable_device(pdev);
+	if (error < 0)
+		goto error2;
+
+	pci_enable_msi(pdev);
+
+	error = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+	if (error)
+		goto error3;
+
+	error = pci_request_regions(pdev, DRV_NAME);
+	if (error)
+		goto error3;
+
+	dev->mmio = pci_ioremap_bar(pdev, 0);
+	if (!dev->mmio)
+		goto error4;
+
+	pci_set_master(pdev);
+
+	if (request_irq(pdev->irq, &sxs_irq, IRQF_SHARED, DRV_NAME, dev))
+		goto error5;
+
+	if (sxs_boot_check(dev) < 0)
+		goto error6;
+
+	init_completion(&dev->irq_response);
+
+	sxs_setup_card(dev);
+
+	if (sxs_get_size(dev) < 0)
+		goto error6;
+
+	if (sxs_setup_disk(dev) < 0)
+		goto error7;
+
+	pci_set_drvdata(pdev, dev);
+
+	dev_dbg(&pdev->dev, "sxs driver successfully loaded\n");
+	return 0;
+
+error7:
+	if (dev->sxs_major)
+		unregister_blkdev(dev->sxs_major, "sxs");
+
+	if (dev->disk) {
+		del_gendisk(dev->disk);
+		put_disk(dev->disk);
+	}
+
+	if (dev->queue)
+		blk_cleanup_queue(dev->queue);
+error6:
+	free_irq(pdev->irq, dev);
+error5:
+	iounmap(dev->mmio);
+error4:
+	pci_release_regions(pdev);
+error3:
+	pci_disable_device(pdev);
+error2:
+	kfree(dev);
+error1:
+	return error;
+}
+
+static void sxs_remove(struct pci_dev *pdev)
+{
+	struct sxs_device *dev = pci_get_drvdata(pdev);
+
+	if (dev->sxs_major)
+		unregister_blkdev(dev->sxs_major, "sxs");
+
+	if (dev->disk) {
+		del_gendisk(dev->disk);
+		put_disk(dev->disk);
+	}
+	if (dev->queue)
+		blk_cleanup_queue(dev->queue);
+	free_irq(pdev->irq, dev);
+	iounmap(dev->mmio);
+	pci_release_regions(pdev);
+	pci_disable_msi(pdev);
+	pci_disable_device(pdev);
+	kfree(dev);
+}
+
+static struct pci_driver sxs_driver = {
+	.name = DRV_NAME,
+	.id_table = ids,
+	.probe = sxs_probe,
+	.remove = sxs_remove,
+};
+
+static int __init sxs_init(void)
+{
+	return pci_register_driver(&sxs_driver);
+}
+
+static void __exit sxs_exit(void)
+{
+	pci_unregister_driver(&sxs_driver);
+}
+
+MODULE_AUTHOR("Kieran Kunhya");
+MODULE_LICENSE("GPL");
+
+module_init(sxs_init);
+module_exit(sxs_exit);
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCHv3] block: Add support for Sony SxS cards
  2014-09-27 23:43       ` [PATCHv3] " Kieran Kunhya
@ 2014-09-28  2:31         ` Jens Axboe
       [not found]           ` <1411915159.78518.YahooMailNeo@web161504.mail.bf1.yahoo.com>
  0 siblings, 1 reply; 7+ messages in thread
From: Jens Axboe @ 2014-09-28  2:31 UTC (permalink / raw)
  To: Kieran Kunhya, Andrew Morton, David S. Miller,
	Greg Kroah-Hartman, Joe Perches, Mauro Carvalho Chehab,
	Antti Palosaari, Jingoo Han, Ramprasad Chinthekindi,
	Pekka Enberg, Minchan Kim, Fabian Frederick, Akhil Bhansali,
	Jiri Kosina, Alan
  Cc: linux-kernel

On 2014-09-27 17:43, Kieran Kunhya wrote:
> +static void sxs_memcpy_read(struct sxs_device *dev, unsigned long sector,
> +			    unsigned long nsect, char *buffer)
> +{
> +	struct pci_dev *pdev = dev->pci_dev;
> +	u32 status;
> +	u32 data[4];
> +	u16 *tmp;
> +	u32 *tmp2;
> +
> +	void *dma2;
> +	dma_addr_t dma2_handle;
> +	void *dma3;
> +	dma_addr_t dma3_handle;
> +
> +	sector >>= dev->sector_shift;
> +	nsect >>= dev->sector_shift;
> +
> +	/* Read */
> +	dma2 = pci_alloc_consistent(pdev, 8192, &dma2_handle);
> +	dma3 = pci_alloc_consistent(pdev, 8192, &dma3_handle);
> +
> +	tmp = dma2;
> +	tmp2 = dma3;
> +	tmp2[0] = dma2_handle;
> +	tmp2[2] = dma3_handle;
> +
> +	dev_dbg_ratelimited(&pdev->dev, "CALL %lu %lu\n",
> +			    sector & 0xffffffff, nsect & 0xffffffff);
> +
> +	reinit_completion(&dev->irq_response);
> +	status = readl(dev->mmio+SXS_STATUS_REG);
> +	data[0] = cpu_to_le32(0x00010028);
> +	data[1] = cpu_to_le32(sector & 0xffffffff);
> +	data[2] = 0x0;
> +	data[3] = cpu_to_le32(nsect & 0xffffffff);
> +	memcpy_toio(dev->mmio, data, sizeof(data));
> +	writel(0xa0, dev->mmio+SXS_ENABLE_REG);
> +	writel(0x80, dev->mmio+SXS_CONTROL_REG);
> +
> +	if (!wait_for_completion_timeout(&dev->irq_response,
> +					 msecs_to_jiffies(5000))) {
> +		dev_dbg(&pdev->dev, "No IRQ\n");
> +	}
> +
> +	reinit_completion(&dev->irq_response);
> +	writel(dma3_handle, dev->mmio+SXS_MASTER_LINK_REG_L);
> +	writel(0x0, dev->mmio+SXS_MASTER_LINK_REG_H);
> +	writel(0x20, dev->mmio+SXS_CONTROL_REG);
> +
> +	if (!wait_for_completion_timeout(&dev->irq_response,
> +					 msecs_to_jiffies(5000))) {
> +		dev_dbg(&pdev->dev, "No IRQ\n");
> +	}
> +
> +	/* FIXME: Use DMA properly */
> +	memcpy(buffer, dma2, dev->sector_size * nsect);
> +
> +	dev_dbg_ratelimited(&pdev->dev, "boot-signature %x\n",
> +			    tmp[255]);
> +
> +	writel(0, dev->mmio+SXS_ENABLE_REG);
> +
> +	pci_free_consistent(pdev, 8192, dma3, dma3_handle);
> +	pci_free_consistent(pdev, 8192, dma2, dma2_handle);
> +}
> +
> +static void sxs_request(struct request_queue *q, struct bio *bio)
> +{
> +	struct bvec_iter iter;
> +	struct bio_vec bvec;
> +	char *buffer;
> +	unsigned long flags;
> +	struct sxs_device *dev = q->queuedata;
> +	struct pci_dev *pdev = dev->pci_dev;
> +	sector_t sector = bio->bi_iter.bi_sector;
> +
> +	bio_for_each_segment(bvec, bio, iter) {
> +		dev_dbg_ratelimited(&pdev->dev, "REQUEST %i %i %i\n",
> +				    bio_cur_bytes(bio), bio->bi_vcnt,
> +				    bvec.bv_len);
> +		buffer = bvec_kmap_irq(&bvec, &flags);
> +		sxs_memcpy_read(dev, sector, bio_cur_bytes(bio) >> 9,
> +				buffer);
> +		sector += bio_cur_bytes(bio) >> 9;
> +		bvec_kunmap_irq(buffer, &flags);
> +	}
> +
> +	bio_endio(bio, 0);

So a few questions here... This device only does reads? And it seems to 
be assuming that only reads end up in the request handler? How so?

Second question. IO never fails? There's no status checking and IO is 
always ended successfully. This too seems odd.

Third, this wong work as-is, at least not of HIGHMEM is used. After the 
bvec_kmap_irq(), irqs will be disabled. But your sxs_memcpy_read() 
invokes schedule through the completion waits, that will instantly go bad.

-- 
Jens Axboe


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCHv3] block: Add support for Sony SxS cards
       [not found]           ` <1411915159.78518.YahooMailNeo@web161504.mail.bf1.yahoo.com>
@ 2014-09-28 16:35             ` Jens Axboe
  0 siblings, 0 replies; 7+ messages in thread
From: Jens Axboe @ 2014-09-28 16:35 UTC (permalink / raw)
  To: Kieran Kunhya, Andrew Morton, David S. Miller,
	Greg Kroah-Hartman, Joe Perches, Mauro Carvalho Chehab,
	Antti Palosaari, Jingoo Han, Ramprasad Chinthekindi,
	Pekka Enberg, Minchan Kim, Fabian Frederick, Akhil Bhansali,
	Jiri Kosina, Alan
  Cc: linux-kernel

On 09/28/2014 08:39 AM, Kieran Kunhya wrote:
>> So a few questions here... This device only does reads? And it seems to 
>> be assuming that only reads end up in the request handler? How so?
> 
> I have only reverse engineered the read-part of the device. Yes, the 
> driver should check whether the request is a read. 

And you should also deny any writeable opens, in that case. Add an open
method and check for FMODE_WRITE. Then add a check ala:

	if (bio_data_dir(bio)) {
		bio_endio(bio, -EROFS);
		return;
	}

and the top of your sxs_request().

>> Second question. IO never fails? There's no status checking and IO is 
>> always ended successfully. This too seems odd.
> 
> I will fix this.
> 
>> Third, this wong work as-is, at least not of HIGHMEM is used. After the 
>> bvec_kmap_irq(), irqs will be disabled. But your sxs_memcpy_read() 
>> invokes schedule through the completion waits, that will instantly go bad.
> 
> Is there a better way of doing this? I spent a long time trying to get DMA 
> to work but couldn't so had to resort to this ugly hack. I assume the memcpy 
> also needs to have some locking somewhere?

It really is pretty horrible and slow, but if you can't get DMA working,
then so be it. And yes, you should add a mutex that is held for the
duration of the memcpy_read(), otherwise things will go bad very fast if
two or more processes attempt to read from it at the same time. A quick
work-around for the irq issue would be to have the block layer bounce
the highmem pages for you. That's actually the default behavior, but I
would make it explicit with a call to:

	blk_queue_bounce_limit(q, BLK_BOUNCE_HIGH);

after the blk_queue_make_request() call. If you do that, then get rid of
the bvec_kmap_irq(), and just do:

	buffer = page_address(bvec.bv_page) + bvec.bv_offset;

to get the destination for your read.

-- 
Jens Axboe


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2014-09-28 16:35 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-07-07  9:53 [PATCH] Add support for Sony SxS cards Kieran Kunhya
2014-07-07  9:53 ` [PATCH] block: Add support for Sony SxS cards Signed-off-by: Kieran Kunhya <kieran@kunhya.com> Kieran Kunhya
2014-07-07 14:41   ` Randy Dunlap
2014-07-20  0:01     ` [PATCHv2] block: Add support for Sony SxS cards Kieran Kunhya
2014-09-27 23:43       ` [PATCHv3] " Kieran Kunhya
2014-09-28  2:31         ` Jens Axboe
     [not found]           ` <1411915159.78518.YahooMailNeo@web161504.mail.bf1.yahoo.com>
2014-09-28 16:35             ` Jens Axboe

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).