All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Németh Márton" <nm127@freemail.hu>
To: Chris Ball <cjb@laptop.org>,
	linux-mmc@vger.kernel.org, Greg Kroah-Hartman <gregkh@suse.de>,
	devel@driverdev.osuosl.org
Cc: techeng <dzshen@gmail.com>,
	clyu@citiz.net, LKML <linux-kernel@vger.kernel.org>
Subject: [PATCH] Add Winbond WB528SD Secure Digital (SD) card reader driver
Date: Sat, 21 Jan 2012 11:52:37 +0100	[thread overview]
Message-ID: <4F1A98F5.3080402@freemail.hu> (raw)

From: Márton Németh <nm127@freemail.hu>

This driver version of Winbond WB528SD can detect mechanical card
presence only. The information is provided through sysfs.

Signed-off-by: Márton Németh <nm127@freemail.hu>
Cc: techeng <dzshen@gmail.com>
---
 drivers/staging/Kconfig           |    2 +
 drivers/staging/Makefile          |    1 +
 drivers/staging/wb528sd/Kconfig   |   16 ++
 drivers/staging/wb528sd/Makefile  |    1 +
 drivers/staging/wb528sd/wb528sd.c |  274 +++++++++++++++++++++++++++++++++++++
 5 files changed, 294 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/wb528sd/Kconfig
 create mode 100644 drivers/staging/wb528sd/Makefile
 create mode 100644 drivers/staging/wb528sd/wb528sd.c

diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 21e2f4b..c1ce429 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -132,4 +132,6 @@ source "drivers/staging/omapdrm/Kconfig"

 source "drivers/staging/android/Kconfig"

+source "drivers/staging/wb528sd/Kconfig"
+
 endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index 7c5808d..7da8c3b 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -57,3 +57,4 @@ obj-$(CONFIG_INTEL_MEI)		+= mei/
 obj-$(CONFIG_MFD_NVEC)		+= nvec/
 obj-$(CONFIG_DRM_OMAP)		+= omapdrm/
 obj-$(CONFIG_ANDROID)		+= android/
+obj-$(CONFIG_WB528SD)		+= wb528sd/
diff --git a/drivers/staging/wb528sd/Kconfig b/drivers/staging/wb528sd/Kconfig
new file mode 100644
index 0000000..b7af426
--- /dev/null
+++ b/drivers/staging/wb528sd/Kconfig
@@ -0,0 +1,16 @@
+config WB528SD
+	tristate "Winbond 528SD Secure Digital (SD) card reader"
+	depends on PCI
+	help
+	  The Winbond 528SD Secure Digital (SD) card reader connects to
+	  the PCI bus and can be found in laptop Clevo model D4J, product
+	  code D410J. It can be identified by its PCI ID 1050:8481
+	  (for example by using "lspci" and "lspci -n" commands).
+	  The driver currently only detects whether an SD card is
+	  mechanically inserted in the reader (dummy SD card is also
+	  detected). The information can be fetched in Clevo D410J
+	  with the command
+	  "cat /sys/devices/pci0000:00/0000:00:0e.0/card_present".
+
+	  Currently (Jan 2012) there is a lack of register description
+	  of this device.
diff --git a/drivers/staging/wb528sd/Makefile b/drivers/staging/wb528sd/Makefile
new file mode 100644
index 0000000..758026d
--- /dev/null
+++ b/drivers/staging/wb528sd/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_WB528SD) += wb528sd.o
diff --git a/drivers/staging/wb528sd/wb528sd.c b/drivers/staging/wb528sd/wb528sd.c
new file mode 100644
index 0000000..7f6380a
--- /dev/null
+++ b/drivers/staging/wb528sd/wb528sd.c
@@ -0,0 +1,274 @@
+/*
+ * Winbond 528SD Secure Digital (SD) card reader
+ *
+ * # lspci -d 1050:8481 -vv -xx
+ * 00:0e.0 Mass storage controller: Winbond Electronics Corp Device 8481 (rev 01)
+ *         Subsystem: Winbond Electronics Corp Device 1050
+ *         Control: I/O+ Mem+ BusMaster- SpecCycle- MemWINV+ VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx-
+ *         Status: Cap+ 66MHz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort+ <TAbort- <MAbort- >SERR- <PERR- INTx-
+ *         Interrupt: pin A routed to IRQ 19
+ *         Region 0: Memory at d0001000 (32-bit, non-prefetchable) [size=4K]
+ *         Region 1: Memory at d0000000 (32-bit, non-prefetchable) [size=4K]
+ *         Capabilities: [c0] Power Management version 2
+ *                 Flags: PMEClk- DSI- D1+ D2+ AuxCurrent=100mA PME(D0-,D1+,D2+,D3hot+,D3cold+)
+ *                 Status: D0 NoSoftRst- PME-Enable- DSel=0 DScale=0 PME+
+ * 00: 50 10 81 84 13 00 90 0a 01 00 80 01 10 42 80 00
+ * 10: 00 10 00 d0 00 00 00 d0 00 00 00 00 00 00 00 00
+ * 20: 00 00 00 00 00 00 00 00 00 00 00 00 50 10 50 10
+ * 30: 00 00 00 00 c0 00 00 00 00 00 00 00 05 01 08 1a
+ *
+ * There are two memory mapped regions: region 0 and region 1.
+ * Region 0 has a 4KiB address window but it contains only 256 bytes
+ * of registers, the same registers repeats every 256 bytes.
+ * Region 1 truly has a 4KiB address window.
+ *
+ * There is one interrupt associated to this device.
+ *
+ * Region 0 registers:
+ * BASE0+0x00: ?
+ * ...
+ * BASE0+0xFF: ?
+ *
+ * Region 1 registers:
+ * BASE1+0x000: ?
+ * ...
+ * BASE1+0x51C: bit7: ?
+ *              bit6: ?
+ *              bit5: ?
+ *              bit4: ?
+ *              bit3: ?
+ *              bit2: ?
+ *              bit1: ?
+ *              bit0: mechanical card persence (dummy card is also detected)
+ *                    0: card not present
+ *                    1: card present
+ * ...
+ * BASE1+0xFFF: ?
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+
+MODULE_AUTHOR("Márton Németh <nm127@freemail.hu>");
+MODULE_DESCRIPTION("Winbond 528SD Secure Digital (SD) card reader");
+MODULE_LICENSE("GPL");
+
+#define PCI_DEVICE_ID_WINBOND_528SD	0x8481
+
+#define WB528SD_REG_STATUS_51C		0x51C
+#define WB528SD_CARD_PRESENCE_MASK	0x01
+
+#define WB528SD_SIZE_4K		0x1000
+
+struct wb528sd_data {
+	void __iomem *ioaddr0;
+	void __iomem *ioaddr1;
+};
+
+static ssize_t card_present_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct wb528sd_data *data = dev_get_drvdata(dev);
+	unsigned int data51c;
+
+	data51c = ioread8(data->ioaddr1 + WB528SD_REG_STATUS_51C);
+
+	return snprintf(buf, PAGE_SIZE, "%u\n",
+		data51c & WB528SD_CARD_PRESENCE_MASK ? 1 : 0);
+}
+static DEVICE_ATTR(card_present, S_IRUGO, card_present_show, NULL);
+
+static irqreturn_t wb528sd_handler(int irq, void *dev_id)
+{
+#if 0
+	struct pci_dev *dev = dev_id;
+	struct wb528sd_data *data = pci_get_drvdata(dev);
+#endif
+	int ret = IRQ_NONE;
+
+	if (0) {
+		/* TODO: find out how to detect if wb528sd is the source
+		 * of the interrupt. Note that the interrupt might be
+		 * shared with other hardware devices.
+		 */
+		ret = IRQ_HANDLED;
+	}
+
+	return ret;
+}
+
+#if 0
+static void dump_content(char *name, void __iomem *base, unsigned long length)
+{
+	unsigned int i;
+	unsigned int data;
+
+	for (i = 0; i < length; i += 4) {
+		data = ioread32(base + i);
+		printk(KERN_DEBUG "%s+0x%X: 0x%X\n", name, i, data);
+	}
+}
+#endif
+
+static int probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+	unsigned long addr0_start, addr0_end, addr0_len, addr0_flags;
+	unsigned long addr1_start, addr1_end, addr1_len, addr1_flags;
+	void __iomem *ioaddr0;
+	void __iomem *ioaddr1;
+	int result;
+	u8 irq;
+	struct wb528sd_data *data = NULL;
+	int ret;
+
+	addr0_start = pci_resource_start(dev, 0);
+	addr0_end = pci_resource_end(dev, 0);
+	addr0_len = pci_resource_len(dev, 0);
+	addr0_flags = pci_resource_flags(dev, 0);
+	printk(KERN_DEBUG "Resource 0: 0x%lX..0x%lX, length=0x%lX, flags=0x%lX\n",
+		addr0_start, addr0_end, addr0_len, addr0_flags);
+
+	addr1_start = pci_resource_start(dev, 1);
+	addr1_end = pci_resource_end(dev, 1);
+	addr1_len = pci_resource_len(dev, 1);
+	addr1_flags = pci_resource_flags(dev, 1);
+	printk(KERN_DEBUG "Resource 1: 0x%lX..0x%lX, length=0x%lX, flags=0x%lX\n",
+		addr1_start, addr1_end, addr1_len, addr1_flags);
+
+	printk(KERN_DEBUG "dev->irq: IRQ #%u\n", dev->irq);
+
+	result = pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq);
+	if (!result)
+		printk(KERN_DEBUG "PCI_INTERRUPT_LINE: IRQ #%u\n", irq);
+	else
+		printk(KERN_DEBUG "Can't read PCI_INTERRUPT_LINE\n");
+
+	if (!(addr0_flags & IORESOURCE_MEM)) {
+		dev_err(&dev->dev, "region #0 not an MMIO resource, aborting\n");
+		return -ENODEV;
+	}
+	if (addr0_len != WB528SD_SIZE_4K) {
+		dev_err(&dev->dev, "Invalid PCI mem region size, aborting\n");
+		return -ENODEV;
+	}
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data) {
+		dev_err(&dev->dev, "%s : kzalloc failed", __func__);
+		return -ENOMEM;
+	}
+
+	printk(KERN_DEBUG "pci_enable_device()\n");
+	result = pci_enable_device(dev);
+	if (result) {
+		printk(KERN_DEBUG "Error enabling wb528sd PCI device: %u\n",
+			result);
+		kfree(data);
+		return result;
+	}
+
+	printk(KERN_DEBUG "pci_request_regions()\n");
+	result = pci_request_regions(dev, KBUILD_MODNAME);
+	if (result) {
+		printk(KERN_DEBUG "pci_request_regions failed, error %d\n",
+			result);
+		pci_disable_device(dev);
+		kfree(data);
+		return result;
+	}
+
+	ioaddr0 = pci_iomap(dev, 0, 0);
+	if (!ioaddr0) {
+		printk(KERN_DEBUG "cannot remap MMIO #0\n");
+		pci_release_regions(dev);
+		pci_disable_device(dev);
+		kfree(data);
+		return result;
+	} else {
+		data->ioaddr0 = ioaddr0;
+	}
+
+	ioaddr1 = pci_iomap(dev, 1, 0);
+	if (!ioaddr1) {
+		printk(KERN_DEBUG "cannot remap MMIO #1\n");
+		pci_release_regions(dev);
+		pci_disable_device(dev);
+		kfree(data);
+		return result;
+	} else {
+		data->ioaddr1 = ioaddr1;
+	}
+
+	printk(KERN_DEBUG "request_irq()\n");
+	result = request_irq(dev->irq, wb528sd_handler,
+			     IRQF_SHARED, KBUILD_MODNAME, dev);
+	if (result != 0) {
+		printk(KERN_DEBUG "Error requesting IRQ #%u device: %d\n",
+			dev->irq, result);
+		pci_release_regions(dev);
+		pci_disable_device(dev);
+		kfree(data);
+		return result;
+	}
+
+	/* TODO: enable wb528sd interrupt generation */
+
+	pci_set_drvdata(dev, data);
+
+	ret = device_create_file(&dev->dev, &dev_attr_card_present);
+	if (!ret)
+		;
+
+	printk(KERN_DEBUG "done\n");
+
+	return 0;
+}
+
+static void remove(struct pci_dev *dev)
+{
+	struct wb528sd_data *data = pci_get_drvdata(dev);
+
+	printk(KERN_DEBUG "%s: remove\n", KBUILD_MODNAME);
+
+	device_remove_file(&dev->dev, &dev_attr_card_present);
+	pci_disable_device(dev);
+	pci_release_regions(dev);
+	if (data) {
+		pci_iounmap(dev, data->ioaddr0);
+		pci_iounmap(dev, data->ioaddr1);
+	}
+	free_irq(dev->irq, dev);
+
+}
+
+static DEFINE_PCI_DEVICE_TABLE(wb528sd_ids) = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND_528SD) },
+	{ 0, },
+};
+MODULE_DEVICE_TABLE(pci, wb528sd_ids);
+
+static struct pci_driver pci_driver = {
+	.name = "wb528sd",
+	.id_table = wb528sd_ids,
+	.probe = probe,
+	.remove = remove,
+};
+
+static int __init wb528sd_init(void)
+{
+	printk(KERN_DEBUG "%s: init\n", KBUILD_MODNAME);
+	return pci_register_driver(&pci_driver);
+}
+
+static void wb528sd_exit(void)
+{
+	printk(KERN_DEBUG "%s: exit\n", KBUILD_MODNAME);
+	pci_unregister_driver(&pci_driver);
+}
+
+module_init(wb528sd_init);
+module_exit(wb528sd_exit);
-- 
1.7.2.5

             reply	other threads:[~2012-01-21 10:52 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-01-21 10:52 Németh Márton [this message]
2012-01-21 12:51 ` [PATCH] Add Winbond WB528SD Secure Digital (SD) card reader driver Greg KH
2012-01-21 14:54   ` Németh Márton
2012-01-21 15:02     ` Greg KH

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4F1A98F5.3080402@freemail.hu \
    --to=nm127@freemail.hu \
    --cc=cjb@laptop.org \
    --cc=clyu@citiz.net \
    --cc=devel@driverdev.osuosl.org \
    --cc=dzshen@gmail.com \
    --cc=gregkh@suse.de \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mmc@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.