diff options
author | Linus Torvalds <torvalds@g5.osdl.org> | 2006-04-02 12:49:59 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-04-02 12:49:59 -0700 |
commit | 86dca4f8e6ab1fd8a3fb5838163fc9d7990f416e (patch) | |
tree | 3d32116afc3be0d323ef93f260592bcea11db329 /drivers/pcmcia | |
parent | 9c8680e2cfbb60d5075f8caaf9d98276120bcc78 (diff) | |
parent | 553ee5dc1a7a1fb04a6286b0c779481f7035bbd1 (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/Kconfig | 7 | ||||
-rw-r--r-- | drivers/pcmcia/Makefile | 3 | ||||
-rw-r--r-- | drivers/pcmcia/at91_cf.c | 365 | ||||
-rw-r--r-- | drivers/pcmcia/cistpl.c | 1 | ||||
-rw-r--r-- | drivers/pcmcia/cs.c | 43 | ||||
-rw-r--r-- | drivers/pcmcia/cs_internal.h | 19 | ||||
-rw-r--r-- | drivers/pcmcia/ds.c | 249 | ||||
-rw-r--r-- | drivers/pcmcia/ds_internal.h | 4 | ||||
-rw-r--r-- | drivers/pcmcia/i82092.c | 1 | ||||
-rw-r--r-- | drivers/pcmcia/i82365.c | 1 | ||||
-rw-r--r-- | drivers/pcmcia/pcmcia_compat.c | 65 | ||||
-rw-r--r-- | drivers/pcmcia/pcmcia_ioctl.c | 81 | ||||
-rw-r--r-- | drivers/pcmcia/pcmcia_resource.c | 228 | ||||
-rw-r--r-- | drivers/pcmcia/pd6729.c | 1 | ||||
-rw-r--r-- | drivers/pcmcia/rsrc_mgr.c | 5 | ||||
-rw-r--r-- | drivers/pcmcia/rsrc_nonstatic.c | 41 | ||||
-rw-r--r-- | drivers/pcmcia/sa1100_cerf.c | 1 | ||||
-rw-r--r-- | drivers/pcmcia/socket_sysfs.c | 10 | ||||
-rw-r--r-- | drivers/pcmcia/ti113x.h | 1 |
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); + |