[PATCH 4/5] [ARM] pxa: lx: add PCMCIA driver
Russell King
linux at arm.linux.org.uk
Sat Apr 18 10:20:51 EDT 2009
Add GPIO-based PCMCIA driver for LX platform.
Signed-off-by: Russell King <rmk+kernel at arm.linux.org.uk>
CC: <linux-pcmcia at lists.infradead.org>
---
drivers/pcmcia/Kconfig | 2 +-
drivers/pcmcia/Makefile | 1 +
drivers/pcmcia/pxa2xx_lx.c | 227 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 229 insertions(+), 1 deletions(-)
create mode 100644 drivers/pcmcia/pxa2xx_lx.c
diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig
index 2764735..7a0948c 100644
--- a/drivers/pcmcia/Kconfig
+++ b/drivers/pcmcia/Kconfig
@@ -217,7 +217,7 @@ config PCMCIA_PXA2XX
depends on ARM && ARCH_PXA && PCMCIA
depends on (ARCH_LUBBOCK || MACH_MAINSTONE || PXA_SHARPSL \
|| MACH_ARMCORE || ARCH_PXA_PALM || TRIZEPS_PCMCIA \
- || ARCH_VIPER || ARCH_PXA_ESERIES)
+ || ARCH_VIPER || MACH_NETBOOKPRO || ARCH_PXA_ESERIES)
help
Say Y here to include support for the PXA2xx PCMCIA controller
diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile
index bbac463..d8e8351 100644
--- a/drivers/pcmcia/Makefile
+++ b/drivers/pcmcia/Makefile
@@ -66,6 +66,7 @@ pxa2xx_lubbock_cs-y += pxa2xx_lubbock.o sa1111_generic.o
pxa2xx_cm_x2xx_cs-y += pxa2xx_cm_x2xx.o pxa2xx_cm_x255.o pxa2xx_cm_x270.o
pxa2xx-obj-$(CONFIG_ARCH_LUBBOCK) += pxa2xx_lubbock_cs.o
pxa2xx-obj-$(CONFIG_MACH_MAINSTONE) += pxa2xx_mainstone.o
+pxa2xx-obj-$(CONFIG_MACH_NETBOOKPRO) += pxa2xx_lx.o
pxa2xx-obj-$(CONFIG_PXA_SHARPSL) += pxa2xx_sharpsl.o
pxa2xx-obj-$(CONFIG_MACH_ARMCORE) += pxa2xx_cm_x2xx_cs.o
pxa2xx-obj-$(CONFIG_ARCH_VIPER) += pxa2xx_viper.o
diff --git a/drivers/pcmcia/pxa2xx_lx.c b/drivers/pcmcia/pxa2xx_lx.c
new file mode 100644
index 0000000..5b4264b
--- /dev/null
+++ b/drivers/pcmcia/pxa2xx_lx.c
@@ -0,0 +1,227 @@
+/*
+ * linux/drivers/pcmica/pxa2xx_lx.c
+ *
+ * Based on: linux/drivers/pcmcia/pxa2xx_mainstone.c
+ *
+ * NetBookPro PCMCIA specific routines.
+ *
+ * Modifications for LX by:
+ * Author: Ben Dooks
+ * Modified: Jun 08, 2004
+ * Copyright: Simtec Electronics <linux at simtec.co.uk>
+ *
+ * Original:
+ * Created: May 12, 2004
+ * Author: Nicolas Pitre
+ * Copyright: MontaVista Software Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+
+#include <pcmcia/ss.h>
+
+#include <asm/mach-types.h>
+#include <mach/lx.h>
+
+#include "soc_common.h"
+
+/* GPIO Lines
+ * Socket: 0 1
+ * CardDetect 75 73
+ * StatusChange 76 72
+ * Ready/IRQ 77 71
+ * Reset 74 70
+ */
+struct our_socket {
+ unsigned int detect; /* gpio for detect */
+ unsigned int status; /* gpio for status change */
+ unsigned int ready; /* gpio for ready/irq */
+ unsigned int reset; /* gpio for card reset */
+};
+
+static struct our_socket sockets[2] = {
+ [0] = {
+ .detect = GPIO_LX_CF_DETECT,
+ .status = GPIO_LX_CF_STATUS,
+ .ready = GPIO_LX_CF_RDY,
+ .reset = GPIO_LX_CF_RST,
+ },
+ [1] = {
+ .detect = GPIO_LX_PCMCIA_DETECT,
+ .status = GPIO_LX_PCMCIA_STATUS,
+ .ready = GPIO_LX_PCMCIA_RDY,
+ .reset = GPIO_LX_PCMCIA_RST,
+ },
+};
+
+static struct pcmcia_irqs irqs[] = {
+ { 0, IRQ_GPIO(GPIO_LX_CF_DETECT), "CF Change Detect" },
+ { 0, IRQ_GPIO(GPIO_LX_CF_STATUS), "CF Status Change" },
+ { 1, IRQ_GPIO(GPIO_LX_PCMCIA_DETECT), "PCMCIA Change Detect" },
+ { 1, IRQ_GPIO(GPIO_LX_PCMCIA_STATUS), "PCMCIA Status Change" },
+};
+
+static int lx_req_gpio(int gpio, const char *name, int dir, int val)
+{
+ int err = gpio_request(gpio, name);
+ if (err == 0) {
+ if (dir)
+ err = gpio_direction_output(gpio, val);
+ else
+ err = gpio_direction_input(gpio);
+ if (err)
+ gpio_free(gpio);
+ }
+ return err;
+}
+
+static int lx_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
+{
+ struct our_socket *oursock = &sockets[skt->nr];
+ int err;
+
+ err = lx_req_gpio(oursock->detect, "PCMCIA detect", 0, 0);
+ if (err)
+ goto err_det;
+ err = lx_req_gpio(oursock->ready, "PCMCIA IRQ", 0, 0);
+ if (err)
+ goto err_rdy;
+ err = lx_req_gpio(oursock->reset, "PCMCIA reset", 1, 1);
+ if (err)
+ goto err_rst;
+ err = lx_req_gpio(oursock->status, "PCMCIA status", 0, 0);
+ if (err)
+ goto err_stat;
+
+ skt->irq = gpio_to_irq(oursock->ready);
+ return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
+
+ err_stat:
+ gpio_free(oursock->reset);
+ err_rst:
+ gpio_free(oursock->ready);
+ err_rdy:
+ gpio_free(oursock->detect);
+ err_det:
+ return err;
+}
+
+static void lx_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
+{
+ struct our_socket *oursock = &sockets[skt->nr];
+ soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
+ gpio_free(oursock->status);
+ gpio_free(oursock->detect);
+ gpio_free(oursock->ready);
+ gpio_free(oursock->reset);
+}
+
+static void lx_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
+ struct pcmcia_state *state)
+{
+ struct our_socket *oursock = &sockets[skt->nr];
+
+ state->detect = gpio_get_value(oursock->detect) ? 0 : 1;
+ state->ready = gpio_get_value(oursock->ready) ? 1 : 0;
+
+ /* none of these are available on this implementation */
+ state->bvd1 = 1;
+ state->bvd2 = 1;
+ state->vs_3v = 1;
+ state->vs_Xv = 0;
+ state->wrprot = 0;
+
+ pr_debug("skt[%d]: detect=%d, ready=%d\n",
+ skt->nr, state->detect, state->ready);
+}
+
+static int lx_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
+ const socket_state_t *state)
+{
+ struct our_socket *oursock = &sockets[skt->nr];
+ int ret = 0;
+
+ pr_debug("skt[%d]: configure\n", skt->nr);
+
+ /* we currently haven't got the code sorted out to
+ * send the PCon chip any configuration info for the
+ * socket
+ */
+ if (state->flags & SS_RESET) {
+ /* assert reset for socket */
+ pr_debug("skt[%d]: reset on\n", skt->nr);
+ gpio_set_value(oursock->reset, 0);
+ } else {
+ /* de-assert reset */
+
+ pr_debug("skt[%d]: reset off\n", skt->nr);
+ gpio_set_value(oursock->reset, 1);
+ }
+
+ return ret;
+}
+
+static void lx_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
+{
+ soc_pcmcia_enable_irqs(skt, irqs, ARRAY_SIZE(irqs));
+}
+
+static void lx_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
+{
+ soc_pcmcia_disable_irqs(skt, irqs, ARRAY_SIZE(irqs));
+}
+
+static struct pcmcia_low_level lx_pcmcia_ops = {
+ .owner = THIS_MODULE,
+ .hw_init = lx_pcmcia_hw_init,
+ .hw_shutdown = lx_pcmcia_hw_shutdown,
+ .socket_state = lx_pcmcia_socket_state,
+ .configure_socket = lx_pcmcia_configure_socket,
+ .socket_init = lx_pcmcia_socket_init,
+ .socket_suspend = lx_pcmcia_socket_suspend,
+ .nr = 2,
+};
+
+static struct platform_device *lx_pcmcia_device;
+
+static int __init lx_pcmcia_init(void)
+{
+ int ret;
+
+ if (!machine_is_netbookpro())
+ return -ENODEV;
+
+ printk(KERN_INFO "LX: installing PCMCIA driver device\n");
+
+ lx_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
+ if (!lx_pcmcia_device)
+ return -ENOMEM;
+
+ lx_pcmcia_device->dev.platform_data = &lx_pcmcia_ops;
+
+ ret = platform_device_add(lx_pcmcia_device);
+ if (ret)
+ platform_device_put(lx_pcmcia_device);
+
+ return ret;
+}
+
+static void __exit lx_pcmcia_exit(void)
+{
+ platform_device_unregister(lx_pcmcia_device);
+}
+
+module_init(lx_pcmcia_init);
+module_exit(lx_pcmcia_exit);
+
+MODULE_LICENSE("GPL");
--
1.6.0.6
More information about the linux-pcmcia
mailing list