aboutsummaryrefslogtreecommitdiff
path: root/drivers/pcmcia
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@g5.osdl.org>2006-04-02 12:49:59 -0700
committerLinus Torvalds <torvalds@g5.osdl.org>2006-04-02 12:49:59 -0700
commit86dca4f8e6ab1fd8a3fb5838163fc9d7990f416e (patch)
tree3d32116afc3be0d323ef93f260592bcea11db329 /drivers/pcmcia
parent9c8680e2cfbb60d5075f8caaf9d98276120bcc78 (diff)
parent553ee5dc1a7a1fb04a6286b0c779481f7035bbd1 (diff)
Merge master.kernel.org:/pub/scm/linux/kernel/git/brodo/pcmcia-2.6
* master.kernel.org:/pub/scm/linux/kernel/git/brodo/pcmcia-2.6: (33 commits) [PATCH] pcmcia: declare pccard_iodyn_ops (fix m8xx_pcmcia.c compilation error) [PATCH] pcmcia: fix pcmcia_device_remove oops [PATCH] pcmcia: Add support for Possio GCC AKA PCMCIA Siemens MC45 [PATCH] pcmcia: pseudo device handling update [PATCH] pcmcia: convert DEV_OK to pcmcia_dev_present [PATCH] pcmcia: use bitfield instead of p_state and state [PATCH] pcmcia: remove unused p_dev->state flags [PATCH] pcmcia: make pcmcia_release_{io,irq} static [PATCH] pcmcia: add return value to _config() functions [PATCH] pcmcia: remove dev_link_t and client_handle_t indirection [PATCH] pcmcia: embed dev_link_t into struct pcmcia_device [PATCH] pcmcia: rename pcmcia_device.state [PATCH] pcmcia: remove unneeded Vcc pseudo setting [PATCH] pcmcia: remove export of pcmcia_release_configuration [PATCH] pcmcia: default suspend and resume handling [PATCH] pcmcia: convert remaining users of pcmcia_release_io and _irq [PATCH] pcmcia: add pcmcia_disable_device [PATCH] serial_cs: add Merlin U630 IDs [PATCH] pcmcia: AT91RM9200 Compact Flash driver [PATCH] pcmcia: socket.functions starts with 1 ...
Diffstat (limited to 'drivers/pcmcia')
-rw-r--r--drivers/pcmcia/Kconfig7
-rw-r--r--drivers/pcmcia/Makefile3
-rw-r--r--drivers/pcmcia/at91_cf.c365
-rw-r--r--drivers/pcmcia/cistpl.c1
-rw-r--r--drivers/pcmcia/cs.c43
-rw-r--r--drivers/pcmcia/cs_internal.h19
-rw-r--r--drivers/pcmcia/ds.c249
-rw-r--r--drivers/pcmcia/ds_internal.h4
-rw-r--r--drivers/pcmcia/i82092.c1
-rw-r--r--drivers/pcmcia/i82365.c1
-rw-r--r--drivers/pcmcia/pcmcia_compat.c65
-rw-r--r--drivers/pcmcia/pcmcia_ioctl.c81
-rw-r--r--drivers/pcmcia/pcmcia_resource.c228
-rw-r--r--drivers/pcmcia/pd6729.c1
-rw-r--r--drivers/pcmcia/rsrc_mgr.c5
-rw-r--r--drivers/pcmcia/rsrc_nonstatic.c41
-rw-r--r--drivers/pcmcia/sa1100_cerf.c1
-rw-r--r--drivers/pcmcia/socket_sysfs.c10
-rw-r--r--drivers/pcmcia/ti113x.h1
19 files changed, 767 insertions, 359 deletions
diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig
index 1f4ad0e7836..cba6c9eef28 100644
--- a/drivers/pcmcia/Kconfig
+++ b/drivers/pcmcia/Kconfig
@@ -263,6 +263,13 @@ config OMAP_CF
Say Y here to support the CompactFlash controller on OMAP.
Note that this doesn't support "True IDE" mode.
+config AT91_CF
+ tristate "AT91 CompactFlash Controller"
+ depends on PCMCIA && ARCH_AT91RM9200
+ help
+ Say Y here to support the CompactFlash controller on AT91 chips.
+ Or choose M to compile the driver as a module named "at91_cf".
+
config PCCARD_NONSTATIC
tristate
diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile
index bcecf5133b7..4276965517f 100644
--- a/drivers/pcmcia/Makefile
+++ b/drivers/pcmcia/Makefile
@@ -10,7 +10,7 @@ pcmcia_core-y += cs.o cistpl.o rsrc_mgr.o socket_sysfs.o
pcmcia_core-$(CONFIG_CARDBUS) += cardbus.o
obj-$(CONFIG_PCCARD) += pcmcia_core.o
-pcmcia-y += ds.o pcmcia_compat.o pcmcia_resource.o
+pcmcia-y += ds.o pcmcia_resource.o
pcmcia-$(CONFIG_PCMCIA_IOCTL) += pcmcia_ioctl.o
obj-$(CONFIG_PCMCIA) += pcmcia.o
@@ -36,6 +36,7 @@ obj-$(CONFIG_PCMCIA_AU1X00) += au1x00_ss.o
obj-$(CONFIG_PCMCIA_VRC4171) += vrc4171_card.o
obj-$(CONFIG_PCMCIA_VRC4173) += vrc4173_cardu.o
obj-$(CONFIG_OMAP_CF) += omap_cf.o
+obj-$(CONFIG_AT91_CF) += at91_cf.o
sa11xx_core-y += soc_common.o sa11xx_base.o
pxa2xx_core-y += soc_common.o pxa2xx_base.o
diff --git a/drivers/pcmcia/at91_cf.c b/drivers/pcmcia/at91_cf.c
new file mode 100644
index 00000000000..67cc5f7d0c9
--- /dev/null
+++ b/drivers/pcmcia/at91_cf.c
@@ -0,0 +1,365 @@
+/*
+ * at91_cf.c -- AT91 CompactFlash controller driver
+ *
+ * Copyright (C) 2005 David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+
+#include <pcmcia/ss.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/sizes.h>
+
+#include <asm/arch/at91rm9200.h>
+#include <asm/arch/board.h>
+#include <asm/arch/gpio.h>
+
+
+#define CF_SIZE 0x30000000 /* CS5+CS6: unavailable */
+
+/*
+ * A0..A10 work in each range; A23 indicates I/O space; A25 is CFRNW;
+ * some other bit in {A24,A22..A11} is nREG to flag memory access
+ * (vs attributes). So more than 2KB/region would just be waste.
+ */
+#define CF_ATTR_PHYS (AT91_CF_BASE)
+#define CF_IO_PHYS (AT91_CF_BASE + (1 << 23))
+#define CF_MEM_PHYS (AT91_CF_BASE + 0x017ff800)
+
+/*--------------------------------------------------------------------------*/
+
+static const char driver_name[] = "at91_cf";
+
+struct at91_cf_socket {
+ struct pcmcia_socket socket;
+
+ unsigned present:1;
+
+ struct platform_device *pdev;
+ struct at91_cf_data *board;
+};
+
+#define SZ_2K (2 * SZ_1K)
+
+static inline int at91_cf_present(struct at91_cf_socket *cf)
+{
+ return !at91_get_gpio_value(cf->board->det_pin);
+}
+
+/*--------------------------------------------------------------------------*/
+
+static int at91_cf_ss_init(struct pcmcia_socket *s)
+{
+ return 0;
+}
+
+static irqreturn_t at91_cf_irq(int irq, void *_cf, struct pt_regs *r)
+{
+ struct at91_cf_socket *cf = (struct at91_cf_socket *) _cf;
+
+ if (irq == cf->board->det_pin) {
+ unsigned present = at91_cf_present(cf);
+
+ /* kick pccard as needed */
+ if (present != cf->present) {
+ cf->present = present;
+ pr_debug("%s: card %s\n", driver_name, present ? "present" : "gone");
+ pcmcia_parse_events(&cf->socket, SS_DETECT);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int at91_cf_get_status(struct pcmcia_socket *s, u_int *sp)
+{
+ struct at91_cf_socket *cf;
+
+ if (!sp)
+ return -EINVAL;
+
+ cf = container_of(s, struct at91_cf_socket, socket);
+
+ /* NOTE: we assume 3VCARD, not XVCARD... */
+ if (at91_cf_present(cf)) {
+ int rdy = cf->board->irq_pin; /* RDY/nIRQ */
+ int vcc = cf->board->vcc_pin;
+
+ *sp = SS_DETECT | SS_3VCARD;
+ if (!rdy || at91_get_gpio_value(rdy))
+ *sp |= SS_READY;
+ if (!vcc || at91_get_gpio_value(vcc))
+ *sp |= SS_POWERON;
+ } else
+ *sp = 0;
+
+ return 0;
+}
+
+static int at91_cf_set_socket(struct pcmcia_socket *sock, struct socket_state_t *s)
+{
+ struct at91_cf_socket *cf;
+
+ cf = container_of(sock, struct at91_cf_socket, socket);
+
+ /* switch Vcc if needed and possible */
+ if (cf->board->vcc_pin) {
+ switch (s->Vcc) {
+ case 0:
+ at91_set_gpio_value(cf->board->vcc_pin, 0);
+ break;
+ case 33:
+ at91_set_gpio_value(cf->board->vcc_pin, 1);
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ /* toggle reset if needed */
+ at91_set_gpio_value(cf->board->rst_pin, s->flags & SS_RESET);
+
+ pr_debug("%s: Vcc %d, io_irq %d, flags %04x csc %04x\n",
+ driver_name, s->Vcc, s->io_irq, s->flags, s->csc_mask);
+
+ return 0;
+}
+
+static int at91_cf_ss_suspend(struct pcmcia_socket *s)
+{
+ return at91_cf_set_socket(s, &dead_socket);
+}
+
+/* we already mapped the I/O region */
+static int at91_cf_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io)
+{
+ struct at91_cf_socket *cf;
+ u32 csr;
+
+ cf = container_of(s, struct at91_cf_socket, socket);
+ io->flags &= (MAP_ACTIVE | MAP_16BIT | MAP_AUTOSZ);
+
+ /*
+ * Use 16 bit accesses unless/until we need 8-bit i/o space.
+ * Always set CSR4 ... PCMCIA won't always unmap things.
+ */
+ csr = at91_sys_read(AT91_SMC_CSR(4)) & ~AT91_SMC_DBW;
+
+ /*
+ * NOTE: this CF controller ignores IOIS16, so we can't really do
+ * MAP_AUTOSZ. The 16bit mode allows single byte access on either
+ * D0-D7 (even addr) or D8-D15 (odd), so it's close enough for many
+ * purposes (and handles ide-cs).
+ *
+ * The 8bit mode is needed for odd byte access on D0-D7. It seems
+ * some cards only like that way to get at the odd byte, despite
+ * CF 3.0 spec table 35 also giving the D8-D15 option.
+ */
+ if (!(io->flags & (MAP_16BIT|MAP_AUTOSZ))) {
+ csr |= AT91_SMC_DBW_8;
+ pr_debug("%s: 8bit i/o bus\n", driver_name);
+ } else {
+ csr |= AT91_SMC_DBW_16;
+ pr_debug("%s: 16bit i/o bus\n", driver_name);
+ }
+ at91_sys_write(AT91_SMC_CSR(4), csr);
+
+ io->start = cf->socket.io_offset;
+ io->stop = io->start + SZ_2K - 1;
+
+ return 0;
+}
+
+/* pcmcia layer maps/unmaps mem regions */
+static int at91_cf_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *map)
+{
+ struct at91_cf_socket *cf;
+
+ if (map->card_start)
+ return -EINVAL;
+
+ cf = container_of(s, struct at91_cf_socket, socket);
+
+ map->flags &= MAP_ACTIVE|MAP_ATTRIB|MAP_16BIT;
+ if (map->flags & MAP_ATTRIB)
+ map->static_start = CF_ATTR_PHYS;
+ else
+ map->static_start = CF_MEM_PHYS;
+
+ return 0;
+}
+
+static struct pccard_operations at91_cf_ops = {
+ .init = at91_cf_ss_init,
+ .suspend = at91_cf_ss_suspend,
+ .get_status = at91_cf_get_status,
+ .set_socket = at91_cf_set_socket,
+ .set_io_map = at91_cf_set_io_map,
+ .set_mem_map = at91_cf_set_mem_map,
+};
+
+/*--------------------------------------------------------------------------*/
+
+static int __init at91_cf_probe(struct device *dev)
+{
+ struct at91_cf_socket *cf;
+ struct at91_cf_data *board = dev->platform_data;
+ struct platform_device *pdev = to_platform_device(dev);
+ unsigned int csa;
+ int status;
+
+ if (!board || !board->det_pin || !board->rst_pin)
+ return -ENODEV;
+
+ cf = kcalloc(1, sizeof *cf, GFP_KERNEL);
+ if (!cf)
+ return -ENOMEM;
+
+ cf->board = board;
+ cf->pdev = pdev;
+ dev_set_drvdata(dev, cf);
+
+ /* CF takes over CS4, CS5, CS6 */
+ csa = at91_sys_read(AT91_EBI_CSA);
+ at91_sys_write(AT91_EBI_CSA, csa | AT91_EBI_CS4A_SMC_COMPACTFLASH);
+
+ /* force poweron defaults for these pins ... */
+ (void) at91_set_A_periph(AT91_PIN_PC9, 0); /* A25/CFRNW */
+ (void) at91_set_A_periph(AT91_PIN_PC10, 0); /* NCS4/CFCS */
+ (void) at91_set_A_periph(AT91_PIN_PC11, 0); /* NCS5/CFCE1 */
+ (void) at91_set_A_periph(AT91_PIN_PC12, 0); /* NCS6/CFCE2 */
+
+ /* nWAIT is _not_ a default setting */
+ (void) at91_set_A_periph(AT91_PIN_PC6, 1); /* nWAIT */
+
+ /*
+ * Static memory controller timing adjustments.
+ * REVISIT: these timings are in terms of MCK cycles, so
+ * when MCK changes (cpufreq etc) so must these values...
+ */
+ at91_sys_write(AT91_SMC_CSR(4), AT91_SMC_ACSS_STD | AT91_SMC_DBW_16 | AT91_SMC_BAT | AT91_SMC_WSEN
+ | AT91_SMC_NWS_(32) /* wait states */
+ | AT91_SMC_RWSETUP_(6) /* setup time */
+ | AT91_SMC_RWHOLD_(4) /* hold time */
+ );
+
+ /* must be a GPIO; ergo must trigger on both edges */
+ status = request_irq(board->det_pin, at91_cf_irq,
+ SA_SAMPLE_RANDOM, driver_name, cf);
+ if (status < 0)
+ goto fail0;
+
+ /*
+ * The card driver will request this irq later as needed.
+ * but it causes lots of "irqNN: nobody cared" messages
+ * unless we report that we handle everything (sigh).
+ * (Note: DK board doesn't wire the IRQ pin...)
+ */
+ if (board->irq_pin) {
+ status = request_irq(board->irq_pin, at91_cf_irq,
+ SA_SHIRQ, driver_name, cf);
+ if (status < 0)
+ goto fail0a;
+ cf->socket.pci_irq = board->irq_pin;
+ }
+ else
+ cf->socket.pci_irq = NR_IRQS + 1;
+
+ /* pcmcia layer only remaps "real" memory not iospace */
+ cf->socket.io_offset = (unsigned long) ioremap(CF_IO_PHYS, SZ_2K);
+ if (!cf->socket.io_offset)
+ goto fail1;
+
+ /* reserve CS4, CS5, and CS6 regions; but use just CS4 */
+ if (!request_mem_region(AT91_CF_BASE, CF_SIZE, driver_name))
+ goto fail1;
+
+ pr_info("%s: irqs det #%d, io #%d\n", driver_name,
+ board->det_pin, board->irq_pin);
+
+ cf->socket.owner = THIS_MODULE;
+ cf->socket.dev.dev = dev;
+ cf->socket.ops = &at91_cf_ops;
+ cf->socket.resource_ops = &pccard_static_ops;
+ cf->socket.features = SS_CAP_PCCARD | SS_CAP_STATIC_MAP
+ | SS_CAP_MEM_ALIGN;
+ cf->socket.map_size = SZ_2K;
+ cf->socket.io[0].NumPorts = SZ_2K;
+
+ status = pcmcia_register_socket(&cf->socket);
+ if (status < 0)
+ goto fail2;
+
+ return 0;
+
+fail2:
+ iounmap((void __iomem *) cf->socket.io_offset);
+ release_mem_region(AT91_CF_BASE, CF_SIZE);
+fail1:
+ if (board->irq_pin)
+ free_irq(board->irq_pin, cf);
+fail0a:
+ free_irq(board->det_pin, cf);
+fail0:
+ at91_sys_write(AT91_EBI_CSA, csa);
+ kfree(cf);
+ return status;
+}
+
+static int __exit at91_cf_remove(struct device *dev)
+{
+ struct at91_cf_socket *cf = dev_get_drvdata(dev);
+ unsigned int csa;
+
+ pcmcia_unregister_socket(&cf->socket);
+ free_irq(cf->board->irq_pin, cf);
+ free_irq(cf->board->det_pin, cf);
+ iounmap((void __iomem *) cf->socket.io_offset);
+ release_mem_region(AT91_CF_BASE, CF_SIZE);
+
+ csa = at91_sys_read(AT91_EBI_CSA);
+ at91_sys_write(AT91_EBI_CSA, csa & ~AT91_EBI_CS4A);
+
+ kfree(cf);
+ return 0;
+}
+
+static struct device_driver at91_cf_driver = {
+ .name = (char *) driver_name,
+ .bus = &platform_bus_type,
+ .probe = at91_cf_probe,
+ .remove = __exit_p(at91_cf_remove),
+ .suspend = pcmcia_socket_dev_suspend,
+ .resume = pcmcia_socket_dev_resume,
+};
+
+/*--------------------------------------------------------------------------*/
+
+static int __init at91_cf_init(void)
+{
+ return driver_register(&at91_cf_driver);
+}
+module_init(at91_cf_init);
+
+static void __exit at91_cf_exit(void)
+{
+ driver_unregister(&at91_cf_driver);
+}
+module_exit(at91_cf_exit);
+
+MODULE_DESCRIPTION("AT91 Compact Flash Driver");
+MODULE_AUTHOR("David Brownell");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c
index 120fa8da639..912c03e5eb0 100644
--- a/drivers/pcmcia/cistpl.c
+++ b/drivers/pcmcia/cistpl.c
@@ -12,7 +12,6 @@
* (C) 1999 David A. Hinds
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c
index 613f2f1fbfd..3162998579c 100644
--- a/drivers/pcmcia/cs.c
+++ b/drivers/pcmcia/cs.c
@@ -16,7 +16,6 @@
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/kernel.h>
-#include <linux/config.h>
#include <linux/string.h>
#include <linux/major.h>
#include <linux/errno.h>
@@ -111,9 +110,9 @@ int pcmcia_socket_dev_suspend(struct device *dev, pm_message_t state)
list_for_each_entry(socket, &pcmcia_socket_list, socket_list) {
if (socket->dev.dev != dev)
continue;
- down(&socket->skt_sem);
+ mutex_lock(&socket->skt_mutex);
socket_suspend(socket);
- up(&socket->skt_sem);
+ mutex_unlock(&socket->skt_mutex);
}
up_read(&pcmcia_socket_list_rwsem);
@@ -129,9 +128,9 @@ int pcmcia_socket_dev_resume(struct device *dev)
list_for_each_entry(socket, &pcmcia_socket_list, socket_list) {
if (socket->dev.dev != dev)
continue;
- down(&socket->skt_sem);
+ mutex_lock(&socket->skt_mutex);
socket_resume(socket);
- up(&socket->skt_sem);
+ mutex_unlock(&socket->skt_mutex);
}
up_read(&pcmcia_socket_list_rwsem);
@@ -237,7 +236,7 @@ int pcmcia_register_socket(struct pcmcia_socket *socket)
init_completion(&socket->socket_released);
init_completion(&socket->thread_done);
init_waitqueue_head(&socket->thread_wait);
- init_MUTEX(&socket->skt_sem);
+ mutex_init(&socket->skt_mutex);
spin_lock_init(&socket->thread_lock);
ret = kernel_thread(pccardd, socket, CLONE_KERNEL);
@@ -406,8 +405,6 @@ static void socket_shutdown(struct pcmcia_socket *s)
cb_free(s);
#endif
s->functions = 0;
- kfree(s->config);
- s->config = NULL;
s->ops->get_status(s, &status);
if (status & SS_POWERON) {
@@ -664,7 +661,7 @@ static int pccardd(void *__skt)
spin_unlock_irqrestore(&skt->thread_lock, flags);
if (events) {
- down(&skt->skt_sem);
+ mutex_lock(&skt->skt_mutex);
if (events & SS_DETECT)
socket_detect_change(skt);
if (events & SS_BATDEAD)
@@ -673,7 +670,7 @@ static int pccardd(void *__skt)
send_event(skt, CS_EVENT_BATTERY_LOW, CS_EVENT_PRI_LOW);
if (events & SS_READY)
send_event(skt, CS_EVENT_READY_CHANGE, CS_EVENT_PRI_LOW);
- up(&skt->skt_sem);
+ mutex_unlock(&skt->skt_mutex);
continue;
}
@@ -717,8 +714,8 @@ int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c)
{
int ret = 0;
- /* s->skt_sem also protects s->callback */
- down(&s->skt_sem);
+ /* s->skt_mutex also protects s->callback */
+ mutex_lock(&s->skt_mutex);
if (c) {
/* registration */
@@ -734,7 +731,7 @@ int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c)
} else
s->callback = NULL;
err:
- up(&s->skt_sem);
+ mutex_unlock(&s->skt_mutex);
return ret;
}
@@ -752,7 +749,7 @@ int pccard_reset_card(struct pcmcia_socket *skt)
cs_dbg(skt, 1, "resetting socket\n");
- down(&skt->skt_sem);
+ mutex_lock(&skt->skt_mutex);
do {
if (!(skt->state & SOCKET_PRESENT)) {
ret = CS_NO_CARD;
@@ -781,7 +778,7 @@ int pccard_reset_card(struct pcmcia_socket *skt)
ret = CS_SUCCESS;
} while (0);
- up(&skt->skt_sem);
+ mutex_unlock(&skt->skt_mutex);
return ret;
} /* reset_card */
@@ -797,7 +794,7 @@ int pcmcia_suspend_card(struct pcmcia_socket *skt)
cs_dbg(skt, 1, "suspending socket\n");
- down(&skt->skt_sem);
+ mutex_lock(&skt->skt_mutex);
do {
if (!(skt->state & SOCKET_PRESENT)) {
ret = CS_NO_CARD;
@@ -814,7 +811,7 @@ int pcmcia_suspend_card(struct pcmcia_socket *skt)
}
ret = socket_suspend(skt);
} while (0);
- up(&skt->skt_sem);
+ mutex_unlock(&skt->skt_mutex);
return ret;
} /* suspend_card */
@@ -827,7 +824,7 @@ int pcmcia_resume_card(struct pcmcia_socket *skt)
cs_dbg(skt, 1, "waking up socket\n");
- down(&skt->skt_sem);
+ mutex_lock(&skt->skt_mutex);
do {
if (!(skt->state & SOCKET_PRESENT)) {
ret = CS_NO_CARD;
@@ -841,7 +838,7 @@ int pcmcia_resume_card(struct pcmcia_socket *skt)
if (!ret && skt->callback)
skt->callback->resume(skt);
} while (0);
- up(&skt->skt_sem);
+ mutex_unlock(&skt->skt_mutex);
return ret;
} /* resume_card */
@@ -855,7 +852,7 @@ int pcmcia_eject_card(struct pcmcia_socket *skt)
cs_dbg(skt, 1, "user eject request\n");
- down(&skt->skt_sem);
+ mutex_lock(&skt->skt_mutex);
do {
if (!(skt->state & SOCKET_PRESENT)) {
ret = -ENODEV;
@@ -871,7 +868,7 @@ int pcmcia_eject_card(struct pcmcia_socket *skt)
socket_remove(skt);
ret = 0;
} while (0);
- up(&skt->skt_sem);
+ mutex_unlock(&skt->skt_mutex);
return ret;
} /* eject_card */
@@ -884,7 +881,7 @@ int pcmcia_insert_card(struct pcmcia_socket *skt)
cs_dbg(skt, 1, "user insert request\n");
- down(&skt->skt_sem);
+ mutex_lock(&skt->skt_mutex);
do {
if (skt->state & SOCKET_PRESENT) {
ret = -EBUSY;
@@ -896,7 +893,7 @@ int pcmcia_insert_card(struct pcmcia_socket *skt)
}
ret = 0;
} while (0);
- up(&skt->skt_sem);
+ mutex_unlock(&skt->skt_mutex);
return ret;
} /* insert_card */
diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h
index 7b37eba35bf..d6164cd583f 100644
--- a/drivers/pcmcia/cs_internal.h
+++ b/drivers/pcmcia/cs_internal.h
@@ -15,7 +15,7 @@
#ifndef _LINUX_CS_INTERNAL_H
#define _LINUX_CS_INTERNAL_H
-#include <linux/config.h>
+#include <linux/kref.h>
/* Flags in client state */
#define CLIENT_CONFIG_LOCKED 0x0001
@@ -23,7 +23,7 @@
#define CLIENT_IO_REQ 0x0004
#define CLIENT_UNBOUND 0x0008
#define CLIENT_STALE 0x0010
-#define CLIENT_WIN_REQ(i) (0x20<<(i))
+#define CLIENT_WIN_REQ(i) (0x1<<(i))
#define CLIENT_CARDBUS 0x8000
#define REGION_MAGIC 0xE3C9
@@ -31,7 +31,7 @@ typedef struct region_t {
u_short region_magic;
u_short state;
dev_info_t dev_info;
- client_handle_t mtd;
+ struct pcmcia_device *mtd;
u_int MediaID;
region_info_t info;
} region_t;
@@ -40,12 +40,12 @@ typedef struct region_t {
/* Each card function gets one of these guys */
typedef struct config_t {
+ struct kref ref;
u_int state;
u_int Attributes;
u_int IntType;
u_int ConfigBase;
u_char Status, Pin, Copy, Option, ExtStatus;
- u_int Present;
u_int CardValues;
io_req_t io;
struct {
@@ -95,12 +95,6 @@ static inline void cs_socket_put(struct pcmcia_socket *skt)
}
}
-#define CHECK_SOCKET(s) \
- (((s) >= sockets) || (socket_table[s]->ops == NULL))
-
-#define SOCKET(h) (h->socket)
-#define CONFIG(h) (&SOCKET(h)->config[(h)->func])
-
/* In cardbus.c */
int cb_alloc(struct pcmcia_socket *s);
void cb_free(struct pcmcia_socket *s);
@@ -133,10 +127,9 @@ extern struct class_interface pccard_sysfs_interface;
extern struct rw_semaphore pcmcia_socket_list_rwsem;
extern struct list_head pcmcia_socket_list;
int pcmcia_get_window(struct pcmcia_socket *s, window_handle_t *handle, int idx, win_req_t *req);
-int pccard_get_configuration_info(struct pcmcia_socket *s, unsigned int function, config_info_t *config);
+int pccard_get_configuration_info(struct pcmcia_socket *s, struct pcmcia_device *p_dev, config_info_t *config);
int pccard_reset_card(struct pcmcia_socket *skt);
-int pccard_get_status(struct pcmcia_socket *s, unsigned int function, cs_status_t *status);
-int pccard_access_configuration_register(struct pcmcia_socket *s, unsigned int function, conf_reg_t *reg);
+int pccard_get_status(struct pcmcia_socket *s, struct pcmcia_device *p_dev, cs_status_t *status);
struct pcmcia_callback{
diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c
index bb96ce1db08..ae10d1eed65 100644
--- a/drivers/pcmcia/ds.c
+++ b/drivers/pcmcia/ds.c
@@ -10,10 +10,9 @@
* are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
*
* (C) 1999 David A. Hinds
- * (C) 2003 - 2005 Dominik Brodowski
+ * (C) 2003 - 2006 Dominik Brodowski
*/
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
@@ -23,6 +22,7 @@
#include <linux/workqueue.h>
#include <linux/crc32.h>
#include <linux/firmware.h>
+#include <linux/kref.h>
#define IN_CARD_SERVICES
#include <pcmcia/cs_types.h>
@@ -343,12 +343,19 @@ void pcmcia_put_dev(struct pcmcia_device *p_dev)
put_device(&p_dev->dev);
}
+static void pcmcia_release_function(struct kref *ref)
+{
+ struct config_t *c = container_of(ref, struct config_t, ref);
+ kfree(c);
+}
+
static void pcmcia_release_dev(struct device *dev)
{
struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
ds_dbg(1, "releasing dev %p\n", p_dev);
pcmcia_put_socket(p_dev->socket);
kfree(p_dev->devname);
+ kref_put(&p_dev->function_config->ref, pcmcia_release_function);
kfree(p_dev);
}
@@ -377,29 +384,12 @@ static int pcmcia_device_probe(struct device * dev)
p_drv = to_pcmcia_drv(dev->driver);
s = p_dev->socket;
- if ((!p_drv->probe) || (!try_module_get(p_drv->owner))) {
+ if ((!p_drv->probe) || (!p_dev->function_config) ||
+ (!try_module_get(p_drv->owner))) {
ret = -EINVAL;
goto put_dev;
}
- p_dev->state &= ~CLIENT_UNBOUND;
-
- /* set up the device configuration, if it hasn't been done before */
- if (!s->functions) {
- cistpl_longlink_mfc_t mfc;
- if (pccard_read_tuple(s, p_dev->func, CISTPL_LONGLINK_MFC,
- &mfc) == CS_SUCCESS)
- s->functions = mfc.nfn;
- else
- s->functions = 1;
- s->config = kzalloc(sizeof(config_t) * s->functions,
- GFP_KERNEL);
- if (!s->config) {
- ret = -ENOMEM;
- goto put_module;
- }
- }
-
ret = p_drv->probe(p_dev);
if (ret)
goto put_module;
@@ -425,15 +415,61 @@ static int pcmcia_device_probe(struct device * dev)
}
+/*
+ * Removes a PCMCIA card from the device tree and socket list.
+ */
+static void pcmcia_card_remove(struct pcmcia_socket *s, struct pcmcia_device *leftover)
+{
+ struct pcmcia_device *p_dev;
+ struct pcmcia_device *tmp;
+ unsigned long flags;
+
+ ds_dbg(2, "unbind_request(%d)\n", s->sock);
+
+
+ if (!leftover)
+ s->device_count = 0;
+ else
+ s->device_count = 1;
+
+ /* unregister all pcmcia_devices registered with this socket, except leftover */
+ list_for_each_entry_safe(p_dev, tmp, &s->devices_list, socket_device_list) {
+ if (p_dev == leftover)
+ continue;
+
+ spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
+ list_del(&p_dev->socket_device_list);
+ p_dev->_removed=1;
+ spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+
+ device_unregister(&p_dev->dev);
+ }
+
+ return;
+}
+
+
static int pcmcia_device_remove(struct device * dev)
{
struct pcmcia_device *p_dev;
struct pcmcia_driver *p_drv;
+ struct pcmcia_device_id *did;
int i;
- /* detach the "instance" */
p_dev = to_pcmcia_dev(dev);
p_drv = to_pcmcia_drv(dev->driver);
+
+ /* If we're removing the primary module driving a
+ * pseudo multi-function card, we need to unbind
+ * all devices
+ */
+ did = (struct pcmcia_device_id *) p_dev->dev.driver_data;
+ if (did && (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) &&
+ (p_dev->socket->device_count != 0) &&
+ (p_dev->device_no == 0))
+ pcmcia_card_remove(p_dev->socket, p_dev);
+
+ /* detach the "instance" */
if (!p_drv)
return 0;
@@ -441,17 +477,16 @@ static int pcmcia_device_remove(struct device * dev)
p_drv->remove(p_dev);
/* check for proper unloading */
- if (p_dev->state & (CLIENT_IRQ_REQ|CLIENT_IO_REQ|CLIENT_CONFIG_LOCKED))
+ if (p_dev->_irq || p_dev->_io || p_dev->_locked)
printk(KERN_INFO "pcmcia: driver %s did not release config properly\n",
p_drv->drv.name);
for (i = 0; i < MAX_WIN; i++)
- if (p_dev->state & CLIENT_WIN_REQ(i))
+ if (p_dev->_win & CLIENT_WIN_REQ(i))
printk(KERN_INFO "pcmcia: driver %s did not release windows properly\n",
p_drv->drv.name);
/* references from pcmcia_probe_device */
- p_dev->state = CLIENT_UNBOUND;
pcmcia_put_dev(p_dev);
module_put(p_drv->owner);
@@ -460,37 +495,6 @@ static int pcmcia_device_remove(struct device * dev)
/*
- * Removes a PCMCIA card from the device tree and socket list.
- */
-static void pcmcia_card_remove(struct pcmcia_socket *s)
-{
- struct pcmcia_device *p_dev;
- unsigned long flags;
-
- ds_dbg(2, "unbind_request(%d)\n", s->sock);
-
- s->device_count = 0;
-
- for (;;) {
- /* unregister all pcmcia_devices registered with this socket*/
- spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
- if (list_empty(&s->devices_list)) {
- spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
- return;
- }
- p_dev = list_entry((&s->devices_list)->next, struct pcmcia_device, socket_device_list);
- list_del(&p_dev->socket_device_list);
- p_dev->state |= CLIENT_STALE;
- spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
-
- device_unregister(&p_dev->dev);
- }
-
- return;
-} /* unbind_request */
-
-
-/*
* pcmcia_device_query -- determine information about a pcmcia device
*/
static int pcmcia_device_query(struct pcmcia_device *p_dev)
@@ -546,7 +550,7 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev)
tmp = vers1->str + vers1->ofs[i];
length = strlen(tmp) + 1;
- if ((length < 3) || (length > 255))
+ if ((length < 2) || (length > 255))
continue;
p_dev->prod_id[i] = kmalloc(sizeof(char) * length,
@@ -571,11 +575,11 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev)
* won't work, this doesn't matter much at the moment: the driver core doesn't
* support it either.
*/
-static DECLARE_MUTEX(device_add_lock);
+static DEFINE_MUTEX(device_add_lock);
struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int function)
{
- struct pcmcia_device *p_dev;
+ struct pcmcia_device *p_dev, *tmp_dev;
unsigned long flags;
int bus_id_len;
@@ -583,7 +587,7 @@ struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int f
if (!s)
return NULL;
- down(&device_add_lock);
+ mutex_lock(&device_add_lock);
/* max of 2 devices per card */
if (s->device_count == 2)
@@ -596,6 +600,8 @@ struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int f
p_dev->socket = s;
p_dev->device_no = (s->device_count++);
p_dev->func = function;
+ if (s->functions <= function)
+ s->functions = function + 1;
p_dev->dev.bus = &pcmcia_bus_type;
p_dev->dev.parent = s->dev.dev;
@@ -608,36 +614,55 @@ struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int f
sprintf (p_dev->devname, "pcmcia%s", p_dev->dev.bus_id);
/* compat */
- p_dev->state = CLIENT_UNBOUND;
+ spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
+
+ /*
+ * p_dev->function_config must be the same for all card functions.
+ * Note that this is serialized by the device_add_lock, so that
+ * only one such struct will be created.
+ */
+ list_for_each_entry(tmp_dev, &s->devices_list, socket_device_list)
+ if (p_dev->func == tmp_dev->func) {
+ p_dev->function_config = tmp_dev->function_config;
+ kref_get(&p_dev->function_config->ref);
+ }
/* Add to the list in pcmcia_bus_socket */
- spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
list_add_tail(&p_dev->socket_device_list, &s->devices_list);
+