summaryrefslogtreecommitdiff
path: root/drivers/pcmcia
diff options
context:
space:
mode:
authorIngo Molnar <mingo@elte.hu>2008-10-12 15:17:14 +0200
committerIngo Molnar <mingo@elte.hu>2008-10-12 15:17:14 +0200
commit620f2efcdc5c7a2db68da41bc3df3cf9a718024e (patch)
treeb1a0411e2588953777d0b10245b12044c33cef54 /drivers/pcmcia
parent04944b793e18ece23f63c0252646b310c1845940 (diff)
parentfd048088306656824958e7783ffcee27e241b361 (diff)
Merge branch 'linus' into x86/xsave
Diffstat (limited to 'drivers/pcmcia')
-rw-r--r--drivers/pcmcia/Kconfig3
-rw-r--r--drivers/pcmcia/Makefile16
-rw-r--r--drivers/pcmcia/ds.c23
-rw-r--r--drivers/pcmcia/pxa2xx_base.c44
-rw-r--r--drivers/pcmcia/pxa2xx_cm_x255.c154
-rw-r--r--drivers/pcmcia/pxa2xx_cm_x270.c14
-rw-r--r--drivers/pcmcia/pxa2xx_cm_x2xx.c49
-rw-r--r--drivers/pcmcia/pxa2xx_palmld.c151
-rw-r--r--drivers/pcmcia/pxa2xx_trizeps4.c256
-rw-r--r--drivers/pcmcia/pxa2xx_viper.c179
-rw-r--r--drivers/pcmcia/soc_common.c6
11 files changed, 844 insertions, 51 deletions
diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig
index e0f884034c9f..f57eeae3830a 100644
--- a/drivers/pcmcia/Kconfig
+++ b/drivers/pcmcia/Kconfig
@@ -220,7 +220,8 @@ config PCMCIA_PXA2XX
tristate "PXA2xx support"
depends on ARM && ARCH_PXA && PCMCIA
depends on (ARCH_LUBBOCK || MACH_MAINSTONE || PXA_SHARPSL \
- || MACH_ARMCORE || ARCH_PXA_PALM)
+ || MACH_ARMCORE || ARCH_PXA_PALM || TRIZEPS_PCMCIA \
+ || ARCH_VIPER)
help
Say Y here to include support for the PXA2xx PCMCIA controller
diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile
index 269a9e913ba2..a87902de8d3b 100644
--- a/drivers/pcmcia/Makefile
+++ b/drivers/pcmcia/Makefile
@@ -29,7 +29,6 @@ obj-$(CONFIG_PCMCIA_M8XX) += m8xx_pcmcia.o
obj-$(CONFIG_HD64465_PCMCIA) += hd64465_ss.o
obj-$(CONFIG_PCMCIA_SA1100) += sa11xx_core.o sa1100_cs.o
obj-$(CONFIG_PCMCIA_SA1111) += sa11xx_core.o sa1111_cs.o
-obj-$(CONFIG_PCMCIA_PXA2XX) += pxa2xx_core.o pxa2xx_cs.o
obj-$(CONFIG_M32R_PCC) += m32r_pcc.o
obj-$(CONFIG_M32R_CFC) += m32r_cfc.o
obj-$(CONFIG_PCMCIA_AU1X00) += au1x00_ss.o
@@ -68,9 +67,14 @@ sa1100_cs-$(CONFIG_SA1100_H3600) += sa1100_h3600.o
sa1100_cs-$(CONFIG_SA1100_SHANNON) += sa1100_shannon.o
sa1100_cs-$(CONFIG_SA1100_SIMPAD) += sa1100_simpad.o
-pxa2xx_cs-$(CONFIG_ARCH_LUBBOCK) += pxa2xx_lubbock.o sa1111_generic.o
-pxa2xx_cs-$(CONFIG_MACH_MAINSTONE) += pxa2xx_mainstone.o
-pxa2xx_cs-$(CONFIG_PXA_SHARPSL) += pxa2xx_sharpsl.o
-pxa2xx_cs-$(CONFIG_MACH_ARMCORE) += pxa2xx_cm_x270.o
-pxa2xx_cs-$(CONFIG_MACH_PALMTX) += pxa2xx_palmtx.o
+pxa2xx_lubbock_cs-y += pxa2xx_lubbock.o sa1111_generic.o
+pxa2xx-obj-$(CONFIG_ARCH_LUBBOCK) += pxa2xx_lubbock_cs.o
+pxa2xx-obj-$(CONFIG_MACH_MAINSTONE) += pxa2xx_mainstone.o
+pxa2xx-obj-$(CONFIG_PXA_SHARPSL) += pxa2xx_sharpsl.o
+pxa2xx-obj-$(CONFIG_MACH_ARMCORE) += pxa2xx_cm_x2xx.o pxa2xx_cm_x255.o pxa2xx_cm_x270.o
+pxa2xx-obj-$(CONFIG_ARCH_VIPER) += pxa2xx_viper.o
+pxa2xx-obj-$(CONFIG_TRIZEPS_PCMCIA) += pxa2xx_trizeps.o
+pxa2xx-obj-$(CONFIG_MACH_PALMTX) += pxa2xx_palmtx.o
+pxa2xx-obj-$(CONFIG_MACH_PALMLD) += pxa2xx_palmld.o
+obj-$(CONFIG_PCMCIA_PXA2XX) += pxa2xx_core.o $(pxa2xx-obj-y)
diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c
index 4174d9656e35..34c83d3ca0fa 100644
--- a/drivers/pcmcia/ds.c
+++ b/drivers/pcmcia/ds.c
@@ -427,6 +427,18 @@ static int pcmcia_device_probe(struct device * dev)
p_drv = to_pcmcia_drv(dev->driver);
s = p_dev->socket;
+ /* The PCMCIA code passes the match data in via dev->driver_data
+ * which is an ugly hack. Once the driver probe is called it may
+ * and often will overwrite the match data so we must save it first
+ *
+ * handle pseudo multifunction devices:
+ * there are at most two pseudo multifunction devices.
+ * if we're matching against the first, schedule a
+ * call which will then check whether there are two
+ * pseudo devices, and if not, add the second one.
+ */
+ did = p_dev->dev.driver_data;
+
ds_dbg(1, "trying to bind %s to %s\n", p_dev->dev.bus_id,
p_drv->drv.name);
@@ -455,21 +467,14 @@ static int pcmcia_device_probe(struct device * dev)
goto put_module;
}
- /* handle pseudo multifunction devices:
- * there are at most two pseudo multifunction devices.
- * if we're matching against the first, schedule a
- * call which will then check whether there are two
- * pseudo devices, and if not, add the second one.
- */
- did = p_dev->dev.driver_data;
if (did && (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) &&
(p_dev->socket->device_count == 1) && (p_dev->device_no == 0))
pcmcia_add_device_later(p_dev->socket, 0);
- put_module:
+put_module:
if (ret)
module_put(p_drv->owner);
- put_dev:
+put_dev:
if (ret)
put_device(dev);
return (ret);
diff --git a/drivers/pcmcia/pxa2xx_base.c b/drivers/pcmcia/pxa2xx_base.c
index 1b07af5a2ed3..13f1e0fd3f31 100644
--- a/drivers/pcmcia/pxa2xx_base.c
+++ b/drivers/pcmcia/pxa2xx_base.c
@@ -30,6 +30,7 @@
#include <asm/system.h>
#include <mach/pxa-regs.h>
#include <mach/pxa2xx-regs.h>
+#include <asm/mach-types.h>
#include <pcmcia/cs_types.h>
#include <pcmcia/ss.h>
@@ -166,18 +167,32 @@ pxa2xx_pcmcia_frequency_change(struct soc_pcmcia_socket *skt,
}
#endif
+static void pxa2xx_configure_sockets(struct device *dev)
+{
+ struct pcmcia_low_level *ops = dev->platform_data;
+
+ /*
+ * We have at least one socket, so set MECR:CIT
+ * (Card Is There)
+ */
+ MECR |= MECR_CIT;
+
+ /* Set MECR:NOS (Number Of Sockets) */
+ if (ops->nr > 1 || machine_is_viper())
+ MECR |= MECR_NOS;
+ else
+ MECR &= ~MECR_NOS;
+}
+
int __pxa2xx_drv_pcmcia_probe(struct device *dev)
{
int ret;
struct pcmcia_low_level *ops;
- int first, nr;
if (!dev || !dev->platform_data)
return -ENODEV;
ops = (struct pcmcia_low_level *)dev->platform_data;
- first = ops->first;
- nr = ops->nr;
/* Provide our PXA2xx specific timing routines. */
ops->set_timing = pxa2xx_pcmcia_set_timing;
@@ -185,21 +200,10 @@ int __pxa2xx_drv_pcmcia_probe(struct device *dev)
ops->frequency_change = pxa2xx_pcmcia_frequency_change;
#endif
- ret = soc_common_drv_pcmcia_probe(dev, ops, first, nr);
+ ret = soc_common_drv_pcmcia_probe(dev, ops, ops->first, ops->nr);
- if (ret == 0) {
- /*
- * We have at least one socket, so set MECR:CIT
- * (Card Is There)
- */
- MECR |= MECR_CIT;
-
- /* Set MECR:NOS (Number Of Sockets) */
- if (nr > 1)
- MECR |= MECR_NOS;
- else
- MECR &= ~MECR_NOS;
- }
+ if (!ret)
+ pxa2xx_configure_sockets(dev);
return ret;
}
@@ -223,11 +227,7 @@ static int pxa2xx_drv_pcmcia_suspend(struct platform_device *dev, pm_message_t s
static int pxa2xx_drv_pcmcia_resume(struct platform_device *dev)
{
- struct pcmcia_low_level *ops = dev->dev.platform_data;
- int nr = ops ? ops->nr : 0;
-
- MECR = nr > 1 ? MECR_CIT | MECR_NOS : (nr > 0 ? MECR_CIT : 0);
-
+ pxa2xx_configure_sockets(&dev->dev);
return pcmcia_socket_dev_resume(&dev->dev);
}
diff --git a/drivers/pcmcia/pxa2xx_cm_x255.c b/drivers/pcmcia/pxa2xx_cm_x255.c
new file mode 100644
index 000000000000..7c8bcb476622
--- /dev/null
+++ b/drivers/pcmcia/pxa2xx_cm_x255.c
@@ -0,0 +1,154 @@
+/*
+ * linux/drivers/pcmcia/pxa/pxa_cm_x255.c
+ *
+ * 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.
+ *
+ * Compulab Ltd., 2003, 2007, 2008
+ * Mike Rapoport <mike@compulab.co.il>
+ *
+ */
+
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+
+#include <asm/mach-types.h>
+#include <mach/pxa-regs.h>
+
+#include "soc_common.h"
+
+#define GPIO_PCMCIA_SKTSEL (54)
+#define GPIO_PCMCIA_S0_CD_VALID (16)
+#define GPIO_PCMCIA_S1_CD_VALID (17)
+#define GPIO_PCMCIA_S0_RDYINT (6)
+#define GPIO_PCMCIA_S1_RDYINT (8)
+#define GPIO_PCMCIA_RESET (9)
+
+#define PCMCIA_S0_CD_VALID IRQ_GPIO(GPIO_PCMCIA_S0_CD_VALID)
+#define PCMCIA_S1_CD_VALID IRQ_GPIO(GPIO_PCMCIA_S1_CD_VALID)
+#define PCMCIA_S0_RDYINT IRQ_GPIO(GPIO_PCMCIA_S0_RDYINT)
+#define PCMCIA_S1_RDYINT IRQ_GPIO(GPIO_PCMCIA_S1_RDYINT)
+
+
+static struct pcmcia_irqs irqs[] = {
+ { 0, PCMCIA_S0_CD_VALID, "PCMCIA0 CD" },
+ { 1, PCMCIA_S1_CD_VALID, "PCMCIA1 CD" },
+};
+
+static int cmx255_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
+{
+ int ret = gpio_request(GPIO_PCMCIA_RESET, "PCCard reset");
+ if (ret)
+ return ret;
+ gpio_direction_output(GPIO_PCMCIA_RESET, 0);
+
+ skt->irq = skt->nr == 0 ? PCMCIA_S0_RDYINT : PCMCIA_S1_RDYINT;
+ ret = soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
+ if (!ret)
+ gpio_free(GPIO_PCMCIA_RESET);
+
+ return ret;
+}
+
+static void cmx255_pcmcia_shutdown(struct soc_pcmcia_socket *skt)
+{
+ soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
+ gpio_free(GPIO_PCMCIA_RESET);
+}
+
+
+static void cmx255_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
+ struct pcmcia_state *state)
+{
+ int cd = skt->nr ? GPIO_PCMCIA_S1_CD_VALID : GPIO_PCMCIA_S0_CD_VALID;
+ int rdy = skt->nr ? GPIO_PCMCIA_S0_RDYINT : GPIO_PCMCIA_S1_RDYINT;
+
+ state->detect = !gpio_get_value(cd);
+ state->ready = !!gpio_get_value(rdy);
+ state->bvd1 = 1;
+ state->bvd2 = 1;
+ state->vs_3v = 0;
+ state->vs_Xv = 0;
+ state->wrprot = 0; /* not available */
+}
+
+
+static int cmx255_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
+ const socket_state_t *state)
+{
+ switch (skt->nr) {
+ case 0:
+ if (state->flags & SS_RESET) {
+ gpio_set_value(GPIO_PCMCIA_SKTSEL, 0);
+ udelay(1);
+ gpio_set_value(GPIO_PCMCIA_RESET, 1);
+ udelay(10);
+ gpio_set_value(GPIO_PCMCIA_RESET, 0);
+ }
+ break;
+ case 1:
+ if (state->flags & SS_RESET) {
+ gpio_set_value(GPIO_PCMCIA_SKTSEL, 1);
+ udelay(1);
+ gpio_set_value(GPIO_PCMCIA_RESET, 1);
+ udelay(10);
+ gpio_set_value(GPIO_PCMCIA_RESET, 0);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static void cmx255_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
+{
+}
+
+static void cmx255_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
+{
+}
+
+
+static struct pcmcia_low_level cmx255_pcmcia_ops __initdata = {
+ .owner = THIS_MODULE,
+ .hw_init = cmx255_pcmcia_hw_init,
+ .hw_shutdown = cmx255_pcmcia_shutdown,
+ .socket_state = cmx255_pcmcia_socket_state,
+ .configure_socket = cmx255_pcmcia_configure_socket,
+ .socket_init = cmx255_pcmcia_socket_init,
+ .socket_suspend = cmx255_pcmcia_socket_suspend,
+ .nr = 1,
+};
+
+static struct platform_device *cmx255_pcmcia_device;
+
+int __init cmx255_pcmcia_init(void)
+{
+ int ret;
+
+ cmx255_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
+
+ if (!cmx255_pcmcia_device)
+ return -ENOMEM;
+
+ ret = platform_device_add_data(cmx255_pcmcia_device, &cmx255_pcmcia_ops,
+ sizeof(cmx255_pcmcia_ops));
+
+ if (ret == 0) {
+ printk(KERN_INFO "Registering cm-x255 PCMCIA interface.\n");
+ ret = platform_device_add(cmx255_pcmcia_device);
+ }
+
+ if (ret)
+ platform_device_put(cmx255_pcmcia_device);
+
+ return ret;
+}
+
+void __exit cmx255_pcmcia_exit(void)
+{
+ platform_device_unregister(cmx255_pcmcia_device);
+}
diff --git a/drivers/pcmcia/pxa2xx_cm_x270.c b/drivers/pcmcia/pxa2xx_cm_x270.c
index bcff5cfed051..6c3aac377126 100644
--- a/drivers/pcmcia/pxa2xx_cm_x270.c
+++ b/drivers/pcmcia/pxa2xx_cm_x270.c
@@ -105,13 +105,10 @@ static struct pcmcia_low_level cmx270_pcmcia_ops __initdata = {
static struct platform_device *cmx270_pcmcia_device;
-static int __init cmx270_pcmcia_init(void)
+int __init cmx270_pcmcia_init(void)
{
int ret;
- if (!machine_is_armcore())
- return -ENODEV;
-
cmx270_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
if (!cmx270_pcmcia_device)
@@ -131,14 +128,7 @@ static int __init cmx270_pcmcia_init(void)
return ret;
}
-static void __exit cmx270_pcmcia_exit(void)
+void __exit cmx270_pcmcia_exit(void)
{
platform_device_unregister(cmx270_pcmcia_device);
}
-
-module_init(cmx270_pcmcia_init);
-module_exit(cmx270_pcmcia_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
-MODULE_DESCRIPTION("CM-x270 PCMCIA driver");
diff --git a/drivers/pcmcia/pxa2xx_cm_x2xx.c b/drivers/pcmcia/pxa2xx_cm_x2xx.c
new file mode 100644
index 000000000000..4f09506ad8d4
--- /dev/null
+++ b/drivers/pcmcia/pxa2xx_cm_x2xx.c
@@ -0,0 +1,49 @@
+/*
+ * linux/drivers/pcmcia/pxa/pxa_cm_x2xx.c
+ *
+ * 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.
+ *
+ * Compulab Ltd., 2003, 2007, 2008
+ * Mike Rapoport <mike@compulab.co.il>
+ *
+ */
+
+#include <linux/module.h>
+
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <mach/system.h>
+
+int cmx255_pcmcia_init(void);
+int cmx270_pcmcia_init(void);
+void cmx255_pcmcia_exit(void);
+void cmx270_pcmcia_exit(void);
+
+static int __init cmx2xx_pcmcia_init(void)
+{
+ int ret = -ENODEV;
+
+ if (machine_is_armcore() && cpu_is_pxa25x())
+ ret = cmx255_pcmcia_init();
+ else if (machine_is_armcore() && cpu_is_pxa27x())
+ ret = cmx270_pcmcia_init();
+
+ return ret;
+}
+
+static void __exit cmx2xx_pcmcia_exit(void)
+{
+ if (machine_is_armcore() && cpu_is_pxa25x())
+ cmx255_pcmcia_exit();
+ else if (machine_is_armcore() && cpu_is_pxa27x())
+ cmx270_pcmcia_exit();
+}
+
+module_init(cmx2xx_pcmcia_init);
+module_exit(cmx2xx_pcmcia_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
+MODULE_DESCRIPTION("CM-x2xx PCMCIA driver");
diff --git a/drivers/pcmcia/pxa2xx_palmld.c b/drivers/pcmcia/pxa2xx_palmld.c
new file mode 100644
index 000000000000..1736c67e547e
--- /dev/null
+++ b/drivers/pcmcia/pxa2xx_palmld.c
@@ -0,0 +1,151 @@
+/*
+ * linux/drivers/pcmcia/pxa2xx_palmld.c
+ *
+ * Driver for Palm LifeDrive PCMCIA
+ *
+ * Copyright (C) 2006 Alex Osborne <ato@meshy.org>
+ * Copyright (C) 2007-2008 Marek Vasut <marek.vasut@gmail.com>
+ *
+ * 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/platform_device.h>
+#include <linux/gpio.h>
+
+#include <asm/mach-types.h>
+#include <mach/palmld.h>
+#include "soc_common.h"
+
+static int palmld_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
+{
+ int ret;
+
+ ret = gpio_request(GPIO_NR_PALMLD_PCMCIA_POWER, "PCMCIA PWR");
+ if (ret)
+ goto err1;
+ ret = gpio_direction_output(GPIO_NR_PALMLD_PCMCIA_POWER, 0);
+ if (ret)
+ goto err2;
+
+ ret = gpio_request(GPIO_NR_PALMLD_PCMCIA_RESET, "PCMCIA RST");
+ if (ret)
+ goto err2;
+ ret = gpio_direction_output(GPIO_NR_PALMLD_PCMCIA_RESET, 1);
+ if (ret)
+ goto err3;
+
+ ret = gpio_request(GPIO_NR_PALMLD_PCMCIA_READY, "PCMCIA RDY");
+ if (ret)
+ goto err3;
+ ret = gpio_direction_input(GPIO_NR_PALMLD_PCMCIA_READY);
+ if (ret)
+ goto err4;
+
+ skt->irq = IRQ_GPIO(GPIO_NR_PALMLD_PCMCIA_READY);
+ return 0;
+
+err4:
+ gpio_free(GPIO_NR_PALMLD_PCMCIA_READY);
+err3:
+ gpio_free(GPIO_NR_PALMLD_PCMCIA_RESET);
+err2:
+ gpio_free(GPIO_NR_PALMLD_PCMCIA_POWER);
+err1:
+ return ret;
+}
+
+static void palmld_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
+{
+ gpio_free(GPIO_NR_PALMLD_PCMCIA_READY);
+ gpio_free(GPIO_NR_PALMLD_PCMCIA_RESET);
+ gpio_free(GPIO_NR_PALMLD_PCMCIA_POWER);
+}
+
+static void palmld_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
+ struct pcmcia_state *state)
+{
+ state->detect = 1; /* always inserted */
+ state->ready = !!gpio_get_value(GPIO_NR_PALMLD_PCMCIA_READY);
+ state->bvd1 = 1;
+ state->bvd2 = 1;
+ state->wrprot = 0;
+ state->vs_3v = 1;
+ state->vs_Xv = 0;
+}
+
+static int palmld_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
+ const socket_state_t *state)
+{
+ gpio_set_value(GPIO_NR_PALMLD_PCMCIA_POWER, 1);
+ gpio_set_value(GPIO_NR_PALMLD_PCMCIA_RESET,
+ !!(state->flags & SS_RESET));
+
+ return 0;
+}
+
+static void palmld_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
+{
+}
+
+static void palmld_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
+{
+}
+
+static struct pcmcia_low_level palmld_pcmcia_ops = {
+ .owner = THIS_MODULE,
+
+ .first = 0,
+ .nr = 2,
+
+ .hw_init = palmld_pcmcia_hw_init,
+ .hw_shutdown = palmld_pcmcia_hw_shutdown,
+
+ .socket_state = palmld_pcmcia_socket_state,
+ .configure_socket = palmld_pcmcia_configure_socket,
+
+ .socket_init = palmld_pcmcia_socket_init,
+ .socket_suspend = palmld_pcmcia_socket_suspend,
+};
+
+static struct platform_device *palmld_pcmcia_device;
+
+static int __init palmld_pcmcia_init(void)
+{
+ int ret;
+
+ if (!machine_is_palmld())
+ return -ENODEV;
+
+ palmld_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
+ if (!palmld_pcmcia_device)
+ return -ENOMEM;
+
+ ret = platform_device_add_data(palmld_pcmcia_device, &palmld_pcmcia_ops,
+ sizeof(palmld_pcmcia_ops));
+
+ if (!ret)
+ ret = platform_device_add(palmld_pcmcia_device);
+
+ if (ret)
+ platform_device_put(palmld_pcmcia_device);
+
+ return ret;
+}
+
+static void __exit palmld_pcmcia_exit(void)
+{
+ platform_device_unregister(palmld_pcmcia_device);
+}
+
+module_init(palmld_pcmcia_init);
+module_exit(palmld_pcmcia_exit);
+
+MODULE_AUTHOR("Alex Osborne <ato@meshy.org>,"
+ " Marek Vasut <marek.vasut@gmail.com>");
+MODULE_DESCRIPTION("PCMCIA support for Palm LifeDrive");
+MODULE_ALIAS("platform:pxa2xx-pcmcia");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pcmcia/pxa2xx_trizeps4.c b/drivers/pcmcia/pxa2xx_trizeps4.c
new file mode 100644
index 000000000000..36c7a0b324d2
--- /dev/null
+++ b/drivers/pcmcia/pxa2xx_trizeps4.c
@@ -0,0 +1,256 @@
+/*
+ * linux/drivers/pcmcia/pxa2xx_trizeps4.c
+ *
+ * TRIZEPS PCMCIA specific routines.
+ *
+ * Author: Jürgen Schindele
+ * Created: 20 02, 2006
+ * Copyright: Jürgen Schindele
+ *
+ * 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/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
+#include <asm/mach-types.h>
+#include <asm/irq.h>
+
+#include <mach/hardware.h>
+#include <mach/pxa-regs.h>
+#include <mach/trizeps4.h>
+
+#include "soc_common.h"
+
+extern void board_pcmcia_power(int power);
+
+static struct pcmcia_irqs irqs[] = {
+ { 0, IRQ_GPIO(GPIO_PCD), "cs0_cd" }
+ /* on other baseboards we can have more inputs */
+};
+
+static int trizeps_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
+{
+ int ret, i;
+ /* we dont have voltage/card/ready detection
+ * so we dont need interrupts for it
+ */
+ switch (skt->nr) {
+ case 0:
+ if (gpio_request(GPIO_PRDY, "cf_irq") < 0) {
+ pr_err("%s: sock %d unable to request gpio %d\n", __func__,
+ skt->nr, GPIO_PRDY);
+ return -EBUSY;
+ }
+ if (gpio_direction_input(GPIO_PRDY) < 0) {
+ pr_err("%s: sock %d unable to set input gpio %d\n", __func__,
+ skt->nr, GPIO_PRDY);
+ gpio_free(GPIO_PRDY);
+ return -EINVAL;
+ }
+ skt->irq = IRQ_GPIO(GPIO_PRDY);
+ break;
+
+#ifndef CONFIG_MACH_TRIZEPS_CONXS
+ case 1:
+#endif
+ default:
+ break;
+ }
+ /* release the reset of this card */
+ pr_debug("%s: sock %d irq %d\n", __func__, skt->nr, skt->irq);
+
+ /* supplementory irqs for the socket */
+ for (i = 0; i < ARRAY_SIZE(irqs); i++) {
+ if (irqs[i].sock != skt->nr)
+ continue;
+ if (gpio_request(IRQ_TO_GPIO(irqs[i].irq), irqs[i].str) < 0) {
+ pr_err("%s: sock %d unable to request gpio %d\n",
+ __func__, skt->nr, IRQ_TO_GPIO(irqs[i].irq));
+ ret = -EBUSY;
+ goto error;
+ }
+ if (gpio_direction_input(IRQ_TO_GPIO(irqs[i].irq)) < 0) {
+ pr_err("%s: sock %d unable to set input gpio %d\n",
+ __func__, skt->nr, IRQ_TO_GPIO(irqs[i].irq));
+ ret = -EINVAL;
+ goto error;
+ }
+ }
+ return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
+
+error:
+ for (; i >= 0; i--) {
+ gpio_free(IRQ_TO_GPIO(irqs[i].irq));
+ }
+ return (ret);
+}
+
+static void trizeps_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
+{
+ int i;
+ /* free allocated gpio's */
+ gpio_free(GPIO_PRDY);
+ for (i = 0; i < ARRAY_SIZE(irqs); i++)
+ gpio_free(IRQ_TO_GPIO(irqs[i].irq));
+}
+
+static unsigned long trizeps_pcmcia_status[2];
+
+static void trizeps_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
+ struct pcmcia_state *state)
+{
+ unsigned short status = 0, change;
+ status = CFSR_readw();
+ change = (status ^ trizeps_pcmcia_status[skt->nr]) &
+ ConXS_CFSR_BVD_MASK;
+ if (change) {
+ trizeps_pcmcia_status[skt->nr] = status;
+ if (status & ConXS_CFSR_BVD1) {
+ /* enable_irq empty */
+ } else {
+ /* disable_irq empty */
+ }
+ }
+
+ switch (skt->nr) {
+ case 0:
+ /* just fill in fix states */
+ state->detect = gpio_get_value(GPIO_PCD) ? 0 : 1;
+ state->ready = gpio_get_value(GPIO_PRDY) ? 1 : 0;
+ state->bvd1 = (status & ConXS_CFSR_BVD1) ? 1 : 0;
+ state->bvd2 = (status & ConXS_CFSR_BVD2) ? 1 : 0;
+ state->vs_3v = (status & ConXS_CFSR_VS1) ? 0 : 1;
+ state->vs_Xv = (status & ConXS_CFSR_VS2) ? 0 : 1;
+ state->wrprot = 0; /* not available */
+ break;
+
+#ifndef CONFIG_MACH_TRIZEPS_CONXS
+ /* on ConXS we only have one slot. Second is inactive */
+ case 1:
+ state->detect = 0;
+ state->ready = 0;
+ state->bvd1 = 0;
+ state->bvd2 = 0;
+ state->vs_3v = 0;
+ state->vs_Xv = 0;
+ state->wrprot = 0;
+ break;
+
+#endif
+ }
+}
+
+static int trizeps_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
+ const socket_state_t *state)
+{
+ int ret = 0;
+ unsigned short power = 0;
+
+ /* we do nothing here just check a bit */
+ switch (state->Vcc) {
+ case 0: power &= 0xfc; break;
+ case 33: power |= ConXS_BCR_S0_VCC_3V3; break;
+ case 50:
+ pr_err("%s(): Vcc 5V not supported in socket\n", __func__);
+ break;
+ default:
+ pr_err("%s(): bad Vcc %u\n", __func__, state->Vcc);
+ ret = -1;
+ }
+
+ switch (state->Vpp) {
+ case 0: power &= 0xf3; break;
+ case 33: power |= ConXS_BCR_S0_VPP_3V3; break;
+ case 120:
+ pr_err("%s(): Vpp 12V not supported in socket\n", __func__);
+ break;
+ default:
+ if (state->Vpp != state->Vcc) {
+ pr_err("%s(): bad Vpp %u\n", __func__, state->Vpp);
+ ret = -1;
+ }
+ }
+
+ switch (skt->nr) {
+ case 0: /* we only have 3.3V */
+ board_pcmcia_power(power);
+ break;
+
+#ifndef CONFIG_MACH_TRIZEPS_CONXS
+ /* on ConXS we only have one slot. Second is inactive */
+ case 1:
+#endif
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static void trizeps_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
+{
+ /* default is on */
+ board_pcmcia_power(0x9);
+}
+
+static void trizeps_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
+{
+ board_pcmcia_power(0x0);
+}
+
+static struct pcmcia_low_level trizeps_pcmcia_ops = {
+ .owner = THIS_MODULE,
+ .hw_init = trizeps_pcmcia_hw_init,
+ .hw_shutdown = trizeps_pcmcia_hw_shutdown,
+ .socket_state = trizeps_pcmcia_socket_state,
+ .configure_socket = trizeps_pcmcia_configure_socket,
+ .socket_init = trizeps_pcmcia_socket_init,
+ .socket_suspend = trizeps_pcmcia_socket_suspend,
+#ifdef CONFIG_MACH_TRIZEPS_CONXS
+ .nr = 1,
+#else
+ .nr = 2,
+#endif
+ .first = 0,
+};
+
+static struct platform_device *trizeps_pcmcia_device;
+
+static int __init trizeps_pcmcia_init(void)
+{
+ int ret;
+
+ trizeps_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
+ if (!trizeps_pcmcia_device)
+ return -ENOMEM;
+
+ ret = platform_device_add_data(trizeps_pcmcia_device,
+ &trizeps_pcmcia_ops, sizeof(trizeps_pcmcia_ops));
+
+ if (ret == 0)
+ ret = platform_device_add(trizeps_pcmcia_device);
+
+ if (ret)
+ platform_device_put(trizeps_pcmcia_device);
+
+ return ret;
+}
+
+static void __exit trizeps_pcmcia_exit(void)
+{
+ platform_device_unregister(trizeps_pcmcia_device);
+}
+
+fs_initcall(trizeps_pcmcia_init);
+module_exit(trizeps_pcmcia_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Juergen Schindele");
+MODULE_ALIAS("platform:pxa2xx-pcmcia");
diff --git a/drivers/pcmcia/pxa2xx_viper.c b/drivers/pcmcia/pxa2xx_viper.c
new file mode 100644
index 000000000000..dd10481be7bf
--- /dev/null
+++ b/drivers/pcmcia/pxa2xx_viper.c
@@ -0,0 +1,179 @@
+/*
+ * VIPER PCMCIA support
+ * Copyright 2004 Arcom Control Systems
+ *
+ * Maintained by Marc Zyngier <maz@misterjones.org>
+ * <marc.zyngier@altran.com>
+ *
+ * Based on:
+ * iPAQ h2200 PCMCIA support
+ * Copyright 2004 Koen Kooi <koen@vestingbar.nl>
+ *
+ * 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/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/irq.h>
+
+#include <mach/pxa-regs.h>
+#include <mach/viper.h>
+#include <asm/mach-types.h>
+
+#include "soc_common.h"
+#include "pxa2xx_base.h"
+
+static struct pcmcia_irqs irqs[] = {
+ { 0, gpio_to_irq(VIPER_CF_CD_GPIO), "PCMCIA_CD" }
+};
+
+static int viper_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
+{
+ unsigned long flags;
+
+ skt->irq = gpio_to_irq(VIPER_CF_RDY_GPIO);
+
+ if (gpio_request(VIPER_CF_CD_GPIO, "CF detect"))
+ goto err_request_cd;
+
+ if (gpio_request(VIPER_CF_RDY_GPIO, "CF ready"))
+ goto err_request_rdy;
+
+ if (gpio_request(VIPER_CF_POWER_GPIO, "CF power"))
+ goto err_request_pwr;
+
+ local_irq_save(flags);
+
+ /* GPIO 82 is the CF power enable line. initially off */
+ if (gpio_direction_output(VIPER_CF_POWER_GPIO, 0) ||
+ gpio_direction_input(VIPER_CF_CD_GPIO) ||
+ gpio_direction_input(VIPER_CF_RDY_GPIO)) {
+ local_irq_restore(flags);
+ goto err_dir;
+ }
+
+ local_irq_restore(flags);
+
+ return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
+
+err_dir:
+ gpio_free(VIPER_CF_POWER_GPIO);
+err_request_pwr:
+ gpio_free(VIPER_CF_RDY_GPIO);
+err_request_rdy:
+ gpio_free(VIPER_CF_CD_GPIO);
+err_request_cd:
+ printk(KERN_ERR "viper: Failed to setup PCMCIA GPIOs\n");
+ return -1;
+}
+
+/*
+ * Release all resources.
+ */
+static void viper_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
+{
+ soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
+ gpio_free(VIPER_CF_POWER_GPIO);
+ gpio_free(VIPER_CF_RDY_GPIO);
+ gpio_free(VIPER_CF_CD_GPIO);
+}
+
+static void viper_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
+ struct pcmcia_state *state)
+{
+ state->detect = gpio_get_value(VIPER_CF_CD_GPIO) ? 0 : 1;
+ state->ready = gpio_get_value(VIPER_CF_RDY_GPIO) ? 1 : 0;
+ state->bvd1 = 1;
+ state->bvd2 = 1;
+ state->wrprot = 0;
+ state->vs_3v = 1; /* Can only apply 3.3V */
+ state->vs_Xv = 0;
+}
+
+static int viper_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
+ const socket_state_t *state)
+{
+ /* Silently ignore Vpp, output enable, speaker enable. */
+ viper_cf_rst(state->flags & SS_RESET);
+
+ /* Apply socket voltage */
+ switch (state->Vcc) {
+ case 0:
+ gpio_set_value(VIPER_CF_POWER_GPIO, 0);
+ break;
+ case 33:
+ gpio_set_value(VIPER_CF_POWER_GPIO, 1);
+ break;
+ default:
+ printk(KERN_ERR "%s: Unsupported Vcc:%d\n",
+ __func__, state->Vcc);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void viper_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
+{
+}
+
+static void viper_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
+{
+}
+
+static struct pcmcia_low_level viper_pcmcia_ops __initdata = {
+ .owner = THIS_MODULE,
+ .hw_init = viper_pcmcia_hw_init,
+ .hw_shutdown = viper_pcmcia_hw_shutdown,
+ .socket_state = viper_pcmcia_socket_state,
+ .configure_socket = viper_pcmcia_configure_socket,
+ .socket_init = viper_pcmcia_socket_init,
+ .socket_suspend = viper_pcmcia_socket_suspend,
+ .nr = 1,
+};
+
+static struct platform_device *viper_pcmcia_device;
+
+static int __init viper_pcmcia_init(void)
+{
+ int ret;
+
+ if (!machine_is_viper())
+ return -ENODEV;
+
+ viper_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
+ if (!viper_pcmcia_device)
+ return -ENOMEM;
+
+ ret = platform_device_add_data(viper_pcmcia_device,
+ &viper_pcmcia_ops,
+ sizeof(viper_pcmcia_ops));
+
+ if (!ret)
+ ret = platform_device_add(viper_pcmcia_device);
+
+ if (ret)
+ platform_device_put(viper_pcmcia_device);
+
+ return ret;
+}
+
+static void __exit viper_pcmcia_exit(void)
+{
+ platform_device_unregister(viper_pcmcia_device);
+}
+
+module_init(viper_pcmcia_init);
+module_exit(viper_pcmcia_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/pcmcia/soc_common.c b/drivers/pcmcia/soc_common.c
index c48f3f69bdaf..da3972153226 100644
--- a/drivers/pcmcia/soc_common.c
+++ b/drivers/pcmcia/soc_common.c
@@ -748,7 +748,9 @@ int soc_common_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops
add_timer(&skt->poll_timer);
- device_create_file(&skt->socket.dev, &dev_attr_status);
+ ret = device_create_file(&skt->socket.dev, &dev_attr_status);
+ if (ret)
+ goto out_err_8;
}
dev_set_drvdata(dev, sinfo);
@@ -758,6 +760,8 @@ int soc_common_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops
do {
skt = &sinfo->skt[i];
+ device_remove_file(&skt->socket.dev, &dev_attr_status);
+ out_err_8:
del_timer_sync(&skt->poll_timer);
pcmcia_unregister_socket(&skt->socket);