diff options
Diffstat (limited to 'drivers/pcmcia')
43 files changed, 4257 insertions, 3857 deletions
diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index 9f3adbd9f700..d0f5ad306078 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -49,20 +49,6 @@ config PCMCIA_LOAD_CIS If unsure, say Y. -config PCMCIA_IOCTL - bool "PCMCIA control ioctl (obsolete)" - depends on PCMCIA - default y - help - If you say Y here, the deprecated ioctl interface to the PCMCIA - subsystem will be built. It is needed by cardmgr and cardctl - (pcmcia-cs) to function properly. - - You should use the new pcmciautils package instead (see - <file:Documentation/Changes> for location and details). - - If unsure, say Y. - config CARDBUS bool "32-bit CardBus support" depends on PCI @@ -84,7 +70,7 @@ config YENTA tristate "CardBus yenta-compatible bridge support" depends on PCI select CARDBUS if !EMBEDDED - select PCCARD_NONSTATIC + select PCCARD_NONSTATIC if PCMCIA != n ---help--- This option enables support for CardBus host bridges. Virtually all modern PCMCIA bridges are CardBus compatible. A "bridge" is @@ -161,9 +147,8 @@ config TCIC config PCMCIA_M8XX tristate "MPC8xx PCMCIA support" - depends on PCMCIA && PPC && 8xx - select PCCARD_IODYN - select PCCARD_NONSTATIC + depends on PCCARD && PPC && 8xx + select PCCARD_IODYN if PCMCIA != n help Say Y here to include support for PowerPC 8xx series PCMCIA controller. @@ -174,6 +159,27 @@ config PCMCIA_AU1X00 tristate "Au1x00 pcmcia support" depends on SOC_AU1X00 && PCMCIA +config PCMCIA_ALCHEMY_DEVBOARD + tristate "Alchemy Db/Pb1xxx PCMCIA socket services" + depends on SOC_AU1X00 && PCMCIA + select 64BIT_PHYS_ADDR + help + Enable this driver of you want PCMCIA support on your Alchemy + Db1000, Db/Pb1100, Db/Pb1500, Db/Pb1550, Db/Pb1200 board. + NOT suitable for the PB1000! + + This driver is also available as a module called db1xxx_ss.ko + +config PCMCIA_XXS1500 + tristate "MyCable XXS1500 PCMCIA socket support" + depends on PCMCIA && MIPS_XXS1500 + select 64BIT_PHYS_ADDR + help + Support for the PCMCIA/CF socket interface on MyCable XXS1500 + systems. + + This driver is also available as a module called xxs1500_ss.ko + config PCMCIA_BCM63XX tristate "bcm63xx pcmcia support" depends on BCM63XX && PCMCIA @@ -208,7 +214,8 @@ config PCMCIA_PXA2XX depends on ARM && ARCH_PXA && PCMCIA depends on (ARCH_LUBBOCK || MACH_MAINSTONE || PXA_SHARPSL \ || MACH_ARMCORE || ARCH_PXA_PALM || TRIZEPS_PCMCIA \ - || ARCOM_PCMCIA || ARCH_PXA_ESERIES || MACH_STARGATE2) + || ARCOM_PCMCIA || ARCH_PXA_ESERIES || MACH_STARGATE2 \ + || MACH_VPAC270) select PCMCIA_SOC_COMMON help Say Y here to include support for the PXA2xx PCMCIA controller @@ -238,14 +245,12 @@ config PCMCIA_PROBE config M32R_PCC bool "M32R PCMCIA I/F" depends on M32R && CHIP_M32700 && PCMCIA - select PCCARD_NONSTATIC help Say Y here to use the M32R PCMCIA controller. config M32R_CFC bool "M32R CF I/F Controller" depends on M32R && (PLAT_USRV || PLAT_M32700UT || PLAT_MAPPI2 || PLAT_MAPPI3 || PLAT_OPSPUT) - select PCCARD_NONSTATIC help Say Y here to use the M32R CompactFlash controller. @@ -293,7 +298,7 @@ config ELECTRA_CF PA Semi Electra eval board. config PCCARD_NONSTATIC - tristate + bool config PCCARD_IODYN bool diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile index 83ff802de544..d006e8beab9c 100644 --- a/drivers/pcmcia/Makefile +++ b/drivers/pcmcia/Makefile @@ -2,15 +2,18 @@ # Makefile for the kernel pcmcia subsystem (c/o David Hinds) # -pcmcia_core-y += cs.o cistpl.o rsrc_mgr.o socket_sysfs.o +pcmcia_core-y += cs.o socket_sysfs.o pcmcia_core-$(CONFIG_CARDBUS) += cardbus.o obj-$(CONFIG_PCCARD) += pcmcia_core.o -pcmcia-y += ds.o pcmcia_resource.o +pcmcia-y += ds.o pcmcia_resource.o cistpl.o pcmcia_cis.o pcmcia-$(CONFIG_PCMCIA_IOCTL) += pcmcia_ioctl.o obj-$(CONFIG_PCMCIA) += pcmcia.o -obj-$(CONFIG_PCCARD_NONSTATIC) += rsrc_nonstatic.o +pcmcia_rsrc-y += rsrc_mgr.o +pcmcia_rsrc-$(CONFIG_PCCARD_NONSTATIC) += rsrc_nonstatic.o +pcmcia_rsrc-$(CONFIG_PCCARD_IODYN) += rsrc_iodyn.o +obj-$(CONFIG_PCCARD) += pcmcia_rsrc.o # socket drivers @@ -35,18 +38,10 @@ obj-$(CONFIG_OMAP_CF) += omap_cf.o obj-$(CONFIG_BFIN_CFPCMCIA) += bfin_cf_pcmcia.o obj-$(CONFIG_AT91_CF) += at91_cf.o obj-$(CONFIG_ELECTRA_CF) += electra_cf.o +obj-$(CONFIG_PCMCIA_ALCHEMY_DEVBOARD) += db1xxx_ss.o au1x00_ss-y += au1000_generic.o au1x00_ss-$(CONFIG_MIPS_PB1000) += au1000_pb1x00.o -au1x00_ss-$(CONFIG_MIPS_PB1100) += au1000_pb1x00.o -au1x00_ss-$(CONFIG_MIPS_PB1200) += au1000_db1x00.o -au1x00_ss-$(CONFIG_MIPS_PB1500) += au1000_pb1x00.o -au1x00_ss-$(CONFIG_MIPS_DB1000) += au1000_db1x00.o -au1x00_ss-$(CONFIG_MIPS_DB1100) += au1000_db1x00.o -au1x00_ss-$(CONFIG_MIPS_DB1200) += au1000_db1x00.o -au1x00_ss-$(CONFIG_MIPS_DB1500) += au1000_db1x00.o -au1x00_ss-$(CONFIG_MIPS_DB1550) += au1000_db1x00.o -au1x00_ss-$(CONFIG_MIPS_XXS1500) += au1000_xxs1500.o sa1111_cs-y += sa1111_generic.o sa1111_cs-$(CONFIG_ASSABET_NEPONSET) += sa1100_neponset.o @@ -74,5 +69,8 @@ pxa2xx-obj-$(CONFIG_MACH_PALMTC) += pxa2xx_palmtc.o pxa2xx-obj-$(CONFIG_MACH_PALMLD) += pxa2xx_palmld.o pxa2xx-obj-$(CONFIG_MACH_E740) += pxa2xx_e740.o pxa2xx-obj-$(CONFIG_MACH_STARGATE2) += pxa2xx_stargate2.o +pxa2xx-obj-$(CONFIG_MACH_VPAC270) += pxa2xx_vpac270.o obj-$(CONFIG_PCMCIA_PXA2XX) += pxa2xx_base.o $(pxa2xx-obj-y) + +obj-$(CONFIG_PCMCIA_XXS1500) += xxs1500_ss.o diff --git a/drivers/pcmcia/at91_cf.c b/drivers/pcmcia/at91_cf.c index e1dccedc5960..fb33fa42d249 100644 --- a/drivers/pcmcia/at91_cf.c +++ b/drivers/pcmcia/at91_cf.c @@ -15,6 +15,7 @@ #include <linux/errno.h> #include <linux/init.h> #include <linux/interrupt.h> +#include <linux/slab.h> #include <pcmcia/ss.h> @@ -52,8 +53,6 @@ struct at91_cf_socket { unsigned long phys_baseaddr; }; -#define SZ_2K (2 * SZ_1K) - static inline int at91_cf_present(struct at91_cf_socket *cf) { return !gpio_get_value(cf->board->det_pin); @@ -363,7 +362,6 @@ static int at91_cf_suspend(struct platform_device *pdev, pm_message_t mesg) struct at91_cf_socket *cf = platform_get_drvdata(pdev); struct at91_cf_data *board = cf->board; - pcmcia_socket_dev_suspend(&pdev->dev); if (device_may_wakeup(&pdev->dev)) { enable_irq_wake(board->det_pin); if (board->irq_pin) @@ -383,7 +381,6 @@ static int at91_cf_resume(struct platform_device *pdev) disable_irq_wake(board->irq_pin); } - pcmcia_socket_dev_resume(&pdev->dev); return 0; } diff --git a/drivers/pcmcia/au1000_db1x00.c b/drivers/pcmcia/au1000_db1x00.c deleted file mode 100644 index c78d77fd7e3b..000000000000 --- a/drivers/pcmcia/au1000_db1x00.c +++ /dev/null @@ -1,305 +0,0 @@ -/* - * - * Alchemy Semi Db1x00 boards specific pcmcia routines. - * - * Copyright 2002 MontaVista Software Inc. - * Author: MontaVista Software, Inc. - * ppopov@mvista.com or source@mvista.com - * - * Copyright 2004 Pete Popov, updated the driver to 2.6. - * Followed the sa11xx API and largely copied many of the hardware - * independent functions. - * - * ######################################################################## - * - * This program is free software; you can distribute it and/or modify it - * under the terms of the GNU General Public License (Version 2) as - * published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - * - * ######################################################################## - * - * - */ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/interrupt.h> -#include <linux/device.h> -#include <linux/init.h> - -#include <asm/irq.h> -#include <asm/signal.h> -#include <asm/mach-au1x00/au1000.h> - -#if defined(CONFIG_MIPS_DB1200) - #include <db1200.h> -#elif defined(CONFIG_MIPS_PB1200) - #include <pb1200.h> -#else - #include <asm/mach-db1x00/db1x00.h> - static BCSR * const bcsr = (BCSR *)BCSR_KSEG1_ADDR; -#endif - -#include "au1000_generic.h" - -#if 0 -#define debug(x,args...) printk(KERN_DEBUG "%s: " x, __func__ , ##args) -#else -#define debug(x,args...) -#endif - - -struct au1000_pcmcia_socket au1000_pcmcia_socket[PCMCIA_NUM_SOCKS]; -extern int au1x00_pcmcia_socket_probe(struct device *, struct pcmcia_low_level *, int, int); - -static int db1x00_pcmcia_hw_init(struct au1000_pcmcia_socket *skt) -{ -#ifdef CONFIG_MIPS_DB1550 - skt->irq = skt->nr ? AU1000_GPIO_5 : AU1000_GPIO_3; -#elif defined(CONFIG_MIPS_DB1200) || defined(CONFIG_MIPS_PB1200) - skt->irq = skt->nr ? BOARD_PC1_INT : BOARD_PC0_INT; -#else - skt->irq = skt->nr ? AU1000_GPIO_5 : AU1000_GPIO_2; -#endif - return 0; -} - -static void db1x00_pcmcia_shutdown(struct au1000_pcmcia_socket *skt) -{ - bcsr->pcmcia = 0; /* turn off power */ - au_sync_delay(2); -} - -static void -db1x00_pcmcia_socket_state(struct au1000_pcmcia_socket *skt, struct pcmcia_state *state) -{ - u32 inserted; - unsigned char vs; - - state->ready = 0; - state->vs_Xv = 0; - state->vs_3v = 0; - state->detect = 0; - - switch (skt->nr) { - case 0: - vs = bcsr->status & 0x3; -#if defined(CONFIG_MIPS_DB1200) || defined(CONFIG_MIPS_PB1200) - inserted = BOARD_CARD_INSERTED(0); -#else - inserted = !(bcsr->status & (1<<4)); -#endif - break; - case 1: - vs = (bcsr->status & 0xC)>>2; -#if defined(CONFIG_MIPS_DB1200) || defined(CONFIG_MIPS_PB1200) - inserted = BOARD_CARD_INSERTED(1); -#else - inserted = !(bcsr->status & (1<<5)); -#endif - break; - default:/* should never happen */ - return; - } - - if (inserted) - debug("db1x00 socket %d: inserted %d, vs %d pcmcia %x\n", - skt->nr, inserted, vs, bcsr->pcmcia); - - if (inserted) { - switch (vs) { - case 0: - case 2: - state->vs_3v=1; - break; - case 3: /* 5V */ - break; - default: - /* return without setting 'detect' */ - printk(KERN_ERR "db1x00 bad VS (%d)\n", - vs); - } - state->detect = 1; - state->ready = 1; - } - else { - /* if the card was previously inserted and then ejected, - * we should turn off power to it - */ - if ((skt->nr == 0) && (bcsr->pcmcia & BCSR_PCMCIA_PC0RST)) { - bcsr->pcmcia &= ~(BCSR_PCMCIA_PC0RST | - BCSR_PCMCIA_PC0DRVEN | - BCSR_PCMCIA_PC0VPP | - BCSR_PCMCIA_PC0VCC); - au_sync_delay(10); - } - else if ((skt->nr == 1) && bcsr->pcmcia & BCSR_PCMCIA_PC1RST) { - bcsr->pcmcia &= ~(BCSR_PCMCIA_PC1RST | - BCSR_PCMCIA_PC1DRVEN | - BCSR_PCMCIA_PC1VPP | - BCSR_PCMCIA_PC1VCC); - au_sync_delay(10); - } - } - - state->bvd1=1; - state->bvd2=1; - state->wrprot=0; -} - -static int -db1x00_pcmcia_configure_socket(struct au1000_pcmcia_socket *skt, struct socket_state_t *state) -{ - u16 pwr; - int sock = skt->nr; - - debug("config_skt %d Vcc %dV Vpp %dV, reset %d\n", - sock, state->Vcc, state->Vpp, - state->flags & SS_RESET); - - /* pcmcia reg was set to zero at init time. Be careful when - * initializing a socket not to wipe out the settings of the - * other socket. - */ - pwr = bcsr->pcmcia; - pwr &= ~(0xf << sock*8); /* clear voltage settings */ - - state->Vpp = 0; - switch(state->Vcc){ - case 0: /* Vcc 0 */ - pwr |= SET_VCC_VPP(0,0,sock); - break; - case 50: /* Vcc 5V */ - switch(state->Vpp) { - case 0: - pwr |= SET_VCC_VPP(2,0,sock); - break; - case 50: - pwr |= SET_VCC_VPP(2,1,sock); - break; - case 12: - pwr |= SET_VCC_VPP(2,2,sock); - break; - case 33: - default: - pwr |= SET_VCC_VPP(0,0,sock); - printk("%s: bad Vcc/Vpp (%d:%d)\n", - __func__, - state->Vcc, - state->Vpp); - break; - } - break; - case 33: /* Vcc 3.3V */ - switch(state->Vpp) { - case 0: - pwr |= SET_VCC_VPP(1,0,sock); - break; - case 12: - pwr |= SET_VCC_VPP(1,2,sock); - break; - case 33: - pwr |= SET_VCC_VPP(1,1,sock); - break; - case 50: - default: - pwr |= SET_VCC_VPP(0,0,sock); - printk("%s: bad Vcc/Vpp (%d:%d)\n", - __func__, - state->Vcc, - state->Vpp); - break; - } - break; - default: /* what's this ? */ - pwr |= SET_VCC_VPP(0,0,sock); - printk(KERN_ERR "%s: bad Vcc %d\n", - __func__, state->Vcc); - break; - } - - bcsr->pcmcia = pwr; - au_sync_delay(300); - - if (sock == 0) { - if (!(state->flags & SS_RESET)) { - pwr |= BCSR_PCMCIA_PC0DRVEN; - bcsr->pcmcia = pwr; - au_sync_delay(300); - pwr |= BCSR_PCMCIA_PC0RST; - bcsr->pcmcia = pwr; - au_sync_delay(100); - } - else { - pwr &= ~(BCSR_PCMCIA_PC0RST | BCSR_PCMCIA_PC0DRVEN); - bcsr->pcmcia = pwr; - au_sync_delay(100); - } - } - else { - if (!(state->flags & SS_RESET)) { - pwr |= BCSR_PCMCIA_PC1DRVEN; - bcsr->pcmcia = pwr; - au_sync_delay(300); - pwr |= BCSR_PCMCIA_PC1RST; - bcsr->pcmcia = pwr; - au_sync_delay(100); - } - else { - pwr &= ~(BCSR_PCMCIA_PC1RST | BCSR_PCMCIA_PC1DRVEN); - bcsr->pcmcia = pwr; - au_sync_delay(100); - } - } - return 0; -} - -/* - * Enable card status IRQs on (re-)initialisation. This can - * be called at initialisation, power management event, or - * pcmcia event. - */ -void db1x00_socket_init(struct au1000_pcmcia_socket *skt) -{ - /* nothing to do for now */ -} - -/* - * Disable card status IRQs and PCMCIA bus on suspend. - */ -void db1x00_socket_suspend(struct au1000_pcmcia_socket *skt) -{ - /* nothing to do for now */ -} - -struct pcmcia_low_level db1x00_pcmcia_ops = { - .owner = THIS_MODULE, - - .hw_init = db1x00_pcmcia_hw_init, - .hw_shutdown = db1x00_pcmcia_shutdown, - - .socket_state = db1x00_pcmcia_socket_state, - .configure_socket = db1x00_pcmcia_configure_socket, - - .socket_init = db1x00_socket_init, - .socket_suspend = db1x00_socket_suspend -}; - -int au1x_board_init(struct device *dev) -{ - int ret = -ENODEV; - bcsr->pcmcia = 0; /* turn off power, if it's not already off */ - au_sync_delay(2); - ret = au1x00_pcmcia_socket_probe(dev, &db1x00_pcmcia_ops, 0, 2); - return ret; -} diff --git a/drivers/pcmcia/au1000_generic.c b/drivers/pcmcia/au1000_generic.c index 02088704ac2c..88c4c4098789 100644 --- a/drivers/pcmcia/au1000_generic.c +++ b/drivers/pcmcia/au1000_generic.c @@ -43,6 +43,7 @@ #include <linux/spinlock.h> #include <linux/mutex.h> #include <linux/platform_device.h> +#include <linux/slab.h> #include <asm/io.h> #include <asm/irq.h> @@ -405,18 +406,16 @@ int au1x00_pcmcia_socket_probe(struct device *dev, struct pcmcia_low_level *ops, skt->virt_io = (void *) (ioremap((phys_t)AU1X_SOCK0_IO, 0x1000) - (u32)mips_io_port_base); - skt->phys_attr = AU1X_SOCK0_PSEUDO_PHYS_ATTR; - skt->phys_mem = AU1X_SOCK0_PSEUDO_PHYS_MEM; + skt->phys_attr = AU1X_SOCK0_PHYS_ATTR; + skt->phys_mem = AU1X_SOCK0_PHYS_MEM; } -#ifndef CONFIG_MIPS_XXS1500 else { skt->virt_io = (void *) (ioremap((phys_t)AU1X_SOCK1_IO, 0x1000) - (u32)mips_io_port_base); - skt->phys_attr = AU1X_SOCK1_PSEUDO_PHYS_ATTR; - skt->phys_mem = AU1X_SOCK1_PSEUDO_PHYS_MEM; + skt->phys_attr = AU1X_SOCK1_PHYS_ATTR; + skt->phys_mem = AU1X_SOCK1_PHYS_MEM; } -#endif pcmcia_base_vaddrs[i] = (u32 *)skt->virt_io; ret = ops->hw_init(skt); @@ -512,17 +511,6 @@ static int au1x00_drv_pcmcia_probe(struct platform_device *dev) return ret; } -static int au1x00_drv_pcmcia_suspend(struct platform_device *dev, - pm_message_t state) -{ - return pcmcia_socket_dev_suspend(&dev->dev); -} - -static int au1x00_drv_pcmcia_resume(struct platform_device *dev) -{ - return pcmcia_socket_dev_resume(&dev->dev); -} - static struct platform_driver au1x00_pcmcia_driver = { .driver = { .name = "au1x00-pcmcia", @@ -530,8 +518,6 @@ static struct platform_driver au1x00_pcmcia_driver = { }, .probe = au1x00_drv_pcmcia_probe, .remove = au1x00_drv_pcmcia_remove, - .suspend = au1x00_drv_pcmcia_suspend, - .resume = au1x00_drv_pcmcia_resume, }; diff --git a/drivers/pcmcia/au1000_generic.h b/drivers/pcmcia/au1000_generic.h index 13a4fbc58711..a324d329dea6 100644 --- a/drivers/pcmcia/au1000_generic.h +++ b/drivers/pcmcia/au1000_generic.h @@ -36,30 +36,14 @@ #define AU1X_SOCK0_IO 0xF00000000ULL #define AU1X_SOCK0_PHYS_ATTR 0xF40000000ULL #define AU1X_SOCK0_PHYS_MEM 0xF80000000ULL -/* pseudo 32 bit phys addresses, which get fixed up to the - * real 36 bit address in fixup_bigphys_addr() */ -#define AU1X_SOCK0_PSEUDO_PHYS_ATTR 0xF4000000 -#define AU1X_SOCK0_PSEUDO_PHYS_MEM 0xF8000000 /* pcmcia socket 1 needs external glue logic so the memory map * differs from board to board. */ -#if defined(CONFIG_MIPS_PB1000) || defined(CONFIG_MIPS_PB1100) || \ - defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1550) || \ - defined(CONFIG_MIPS_PB1200) +#if defined(CONFIG_MIPS_PB1000) #define AU1X_SOCK1_IO 0xF08000000ULL #define AU1X_SOCK1_PHYS_ATTR 0xF48000000ULL #define AU1X_SOCK1_PHYS_MEM 0xF88000000ULL -#define AU1X_SOCK1_PSEUDO_PHYS_ATTR 0xF4800000 -#define AU1X_SOCK1_PSEUDO_PHYS_MEM 0xF8800000 -#elif defined(CONFIG_MIPS_DB1000) || defined(CONFIG_MIPS_DB1100) || \ - defined(CONFIG_MIPS_DB1500) || defined(CONFIG_MIPS_DB1550) || \ - defined(CONFIG_MIPS_DB1200) -#define AU1X_SOCK1_IO 0xF04000000ULL -#define AU1X_SOCK1_PHYS_ATTR 0xF44000000ULL -#define AU1X_SOCK1_PHYS_MEM 0xF84000000ULL -#define AU1X_SOCK1_PSEUDO_PHYS_ATTR 0xF4400000 -#define AU1X_SOCK1_PSEUDO_PHYS_MEM 0xF8400000 #endif struct pcmcia_state { diff --git a/drivers/pcmcia/au1000_pb1x00.c b/drivers/pcmcia/au1000_pb1x00.c index b1984ed72d1d..5a979cb8f3e6 100644 --- a/drivers/pcmcia/au1000_pb1x00.c +++ b/drivers/pcmcia/au1000_pb1x00.c @@ -1,6 +1,6 @@ /* * - * Alchemy Semi Pb1x00 boards specific pcmcia routines. + * Alchemy Semi Pb1000 boards specific pcmcia routines. * * Copyright 2002 MontaVista Software Inc. * Author: MontaVista Software, Inc. @@ -46,20 +46,11 @@ #define debug(fmt, arg...) do { } while (0) -#ifdef CONFIG_MIPS_PB1000 #include <asm/pb1000.h> #define PCMCIA_IRQ AU1000_GPIO_15 -#elif defined (CONFIG_MIPS_PB1500) -#include <asm/pb1500.h> -#define PCMCIA_IRQ AU1500_GPIO_203 -#elif defined (CONFIG_MIPS_PB1100) -#include <asm/pb1100.h> -#define PCMCIA_IRQ AU1000_GPIO_11 -#endif static int pb1x00_pcmcia_init(struct pcmcia_init *init) { -#ifdef CONFIG_MIPS_PB1000 u16 pcr; pcr = PCR_SLOT_0_RST | PCR_SLOT_1_RST; @@ -74,21 +65,10 @@ static int pb1x00_pcmcia_init(struct pcmcia_init *init) au_sync_delay(20); return PCMCIA_NUM_SOCKS; - -#else /* fixme -- take care of the Pb1500 at some point */ - - u16 pcr; - pcr = au_readw(PCMCIA_BOARD_REG) & ~0xf; /* turn off power */ - pcr &= ~(PC_DEASSERT_RST | PC_DRV_EN); - au_writew(pcr, PCMCIA_BOARD_REG); - au_sync_delay(500); - return PCMCIA_NUM_SOCKS; -#endif } static int pb1x00_pcmcia_shutdown(void) { -#ifdef CONFIG_MIPS_PB1000 u16 pcr; pcr = PCR_SLOT_0_RST | PCR_SLOT_1_RST; pcr |= SET_VCC_VPP(VCC_HIZ,VPP_HIZ,0); @@ -96,14 +76,6 @@ static int pb1x00_pcmcia_shutdown(void) au_writel(pcr, PB1000_PCR); au_sync_delay(20); return 0; -#else - u16 pcr; - pcr = au_readw(PCMCIA_BOARD_REG) & ~0xf; /* turn off power */ - pcr &= ~(PC_DEASSERT_RST | PC_DRV_EN); - au_writew(pcr, PCMCIA_BOARD_REG); - au_sync_delay(2); - return 0; -#endif } static int @@ -112,21 +84,11 @@ pb1x00_pcmcia_socket_state(unsigned sock, struct pcmcia_state *state) u32 inserted0, inserted1; u16 vs0, vs1; -#ifdef CONFIG_MIPS_PB1000 vs0 = vs1 = (u16)au_readl(PB1000_ACR1); inserted0 = !(vs0 & (ACR1_SLOT_0_CD1 | ACR1_SLOT_0_CD2)); inserted1 = !(vs1 & (ACR1_SLOT_1_CD1 | ACR1_SLOT_1_CD2)); vs0 = (vs0 >> 4) & 0x3; vs1 = (vs1 >> 12) & 0x3; -#else - vs0 = (au_readw(BOARD_STATUS_REG) >> 4) & 0x3; -#ifdef CONFIG_MIPS_PB1500 - inserted0 = !((au_readl(GPIO2_PINSTATE) >> 1) & 0x1); /* gpio 201 */ -#else /* Pb1100 */ - inserted0 = !((au_readl(SYS_PINSTATERD) >> 9) & 0x1); /* gpio 9 */ -#endif - inserted1 = 0; -#endif state->ready = 0; state->vs_Xv = 0; @@ -203,7 +165,6 @@ pb1x00_pcmcia_configure_socket(const struct pcmcia_configure *configure) if(configure->sock > PCMCIA_MAX_SOCK) return -1; -#ifdef CONFIG_MIPS_PB1000 pcr = au_readl(PB1000_PCR); if (configure->sock == 0) { @@ -323,84 +284,6 @@ pb1x00_pcmcia_configure_socket(const struct pcmcia_configure *configure) au_writel(pcr, PB1000_PCR); au_sync_delay(300); -#else - - pcr = au_readw(PCMCIA_BOARD_REG) & ~0xf; - - debug("Vcc %dV Vpp %dV, pcr %x, reset %d\n", - configure->vcc, configure->vpp, pcr, configure->reset); - - - switch(configure->vcc){ - case 0: /* Vcc 0 */ - pcr |= SET_VCC_VPP(0,0); - break; - case 50: /* Vcc 5V */ - switch(configure->vpp) { - case 0: - pcr |= SET_VCC_VPP(2,0); - break; - case 50: - pcr |= SET_VCC_VPP(2,1); - break; - case 12: - pcr |= SET_VCC_VPP(2,2); - break; - case 33: - default: - pcr |= SET_VCC_VPP(0,0); - printk("%s: bad Vcc/Vpp (%d:%d)\n", - __func__, - configure->vcc, - configure->vpp); - break; - } - break; - case 33: /* Vcc 3.3V */ - switch(configure->vpp) { - case 0: - pcr |= SET_VCC_VPP(1,0); - break; - case 12: - pcr |= SET_VCC_VPP(1,2); - break; - case 33: - pcr |= SET_VCC_VPP(1,1); - break; - case 50: - default: - pcr |= SET_VCC_VPP(0,0); - printk("%s: bad Vcc/Vpp (%d:%d)\n", - __func__, - configure->vcc, - configure->vpp); - break; - } - break; - default: /* what's this ? */ - pcr |= SET_VCC_VPP(0,0); - printk(KERN_ERR "%s: bad Vcc %d\n", - __func__, configure->vcc); - break; - } - - au_writew(pcr, PCMCIA_BOARD_REG); - au_sync_delay(300); - - if (!configure->reset) { - pcr |= PC_DRV_EN; - au_writew(pcr, PCMCIA_BOARD_REG); - au_sync_delay(100); - pcr |= PC_DEASSERT_RST; - au_writew(pcr, PCMCIA_BOARD_REG); - au_sync_delay(100); - } - else { - pcr &= ~(PC_DEASSERT_RST | PC_DRV_EN); - au_writew(pcr, PCMCIA_BOARD_REG); - au_sync_delay(100); - } -#endif return 0; } diff --git a/drivers/pcmcia/au1000_xxs1500.c b/drivers/pcmcia/au1000_xxs1500.c deleted file mode 100644 index b43d47b50819..000000000000 --- a/drivers/pcmcia/au1000_xxs1500.c +++ /dev/null @@ -1,188 +0,0 @@ -/* - * - * MyCable board specific pcmcia routines. - * - * Copyright 2003 MontaVista Software Inc. - * Author: Pete Popov, MontaVista Software, Inc. - * ppopov@mvista.com or source@mvista.com - * - * ######################################################################## - * - * This program is free software; you can distribute it and/or modify it - * under the terms of the GNU General Public License (Version 2) as - * published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - * - * ######################################################################## - * - * - */ -#include <linux/module.h> -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/ioport.h> -#include <linux/kernel.h> -#include <linux/timer.h> -#include <linux/mm.h> -#include <linux/proc_fs.h> -#include <linux/types.h> - -#include <pcmcia/cs_types.h> -#include <pcmcia/cs.h> -#include <pcmcia/ss.h> -#include <pcmcia/cistpl.h> -#include <pcmcia/bus_ops.h> - -#include <asm/io.h> -#include <asm/irq.h> -#include <asm/system.h> - -#include <asm/au1000.h> -#include <asm/au1000_pcmcia.h> - -#define PCMCIA_MAX_SOCK 0 -#define PCMCIA_NUM_SOCKS (PCMCIA_MAX_SOCK + 1) -#define PCMCIA_IRQ AU1000_GPIO_4 - -#if 0 -#define DEBUG(x, args...) printk(__func__ ": " x, ##args) -#else -#define DEBUG(x,args...) -#endif - -static int xxs1500_pcmcia_init(struct pcmcia_init *init) -{ - return PCMCIA_NUM_SOCKS; -} - -static int xxs1500_pcmcia_shutdown(void) -{ - /* turn off power */ - au_writel(au_readl(GPIO2_PINSTATE) | (1<<14)|(1<<30), - GPIO2_OUTPUT); - au_sync_delay(100); - - /* assert reset */ - au_writel(au_readl(GPIO2_PINSTATE) | (1<<4)|(1<<20), - GPIO2_OUTPUT); - au_sync_delay(100); - return 0; -} - - -static int -xxs1500_pcmcia_socket_state(unsigned sock, struct pcmcia_state *state) -{ - u32 inserted; u32 vs; - unsigned long gpio, gpio2; - - if(sock > PCMCIA_MAX_SOCK) return -1; - - gpio = au_readl(SYS_PINSTATERD); - gpio2 = au_readl(GPIO2_PINSTATE); - - vs = gpio2 & ((1<<8) | (1<<9)); - inserted = (!(gpio & 0x1) && !(gpio & 0x2)); - - state->ready = 0; - state->vs_Xv = 0; - state->vs_3v = 0; - state->detect = 0; - - if (inserted) { - switch (vs) { - case 0: - case 1: - case 2: - state->vs_3v=1; - break; - case 3: /* 5V */ - default: - /* return without setting 'detect' */ - printk(KERN_ERR "au1x00_cs: unsupported VS\n", - vs); - return; - } - state->detect = 1; - } - - if (state->detect) { - state->ready = 1; - } - - state->bvd1= gpio2 & (1<<10); - state->bvd2 = gpio2 & (1<<11); - state->wrprot=0; - return 1; -} - - -static int xxs1500_pcmcia_get_irq_info(struct pcmcia_irq_info *info) -{ - - if(info->sock > PCMCIA_MAX_SOCK) return -1; - info->irq = PCMCIA_IRQ; - return 0; -} - - -static int -xxs1500_pcmcia_configure_socket(const struct pcmcia_configure *configure) -{ - - if(configure->sock > PCMCIA_MAX_SOCK) return -1; - - DEBUG("Vcc %dV Vpp %dV, reset %d\n", - configure->vcc, configure->vpp, configure->reset); - - switch(configure->vcc){ - case 33: /* Vcc 3.3V */ - /* turn on power */ - DEBUG("turn on power\n"); - au_writel((au_readl(GPIO2_PINSTATE) & ~(1<<14))|(1<<30), - GPIO2_OUTPUT); - au_sync_delay(100); - break; - case 50: /* Vcc 5V */ - default: /* what's this ? */ - printk(KERN_ERR "au1x00_cs: unsupported VCC\n"); - case 0: /* Vcc 0 */ - /* turn off power */ - au_sync_delay(100); - au_writel(au_readl(GPIO2_PINSTATE) | (1<<14)|(1<<30), - GPIO2_OUTPUT); - break; - } - - if (!configure->reset) { - DEBUG("deassert reset\n"); - au_writel((au_readl(GPIO2_PINSTATE) & ~(1<<4))|(1<<20), - GPIO2_OUTPUT); - au_sync_delay(100); - au_writel((au_readl(GPIO2_PINSTATE) & ~(1<<5))|(1<<21), - GPIO2_OUTPUT); - } - else { - DEBUG("assert reset\n"); - au_writel(au_readl(GPIO2_PINSTATE) | (1<<4)|(1<<20), - GPIO2_OUTPUT); - } - au_sync_delay(100); - return 0; -} - -struct pcmcia_low_level xxs1500_pcmcia_ops = { - xxs1500_pcmcia_init, - xxs1500_pcmcia_shutdown, - xxs1500_pcmcia_socket_state, - xxs1500_pcmcia_get_irq_info, - xxs1500_pcmcia_configure_socket -}; diff --git a/drivers/pcmcia/bcm63xx_pcmcia.c b/drivers/pcmcia/bcm63xx_pcmcia.c index bc88a3b19bb3..693577e0fefc 100644 --- a/drivers/pcmcia/bcm63xx_pcmcia.c +++ b/drivers/pcmcia/bcm63xx_pcmcia.c @@ -11,6 +11,7 @@ #include <linux/ioport.h> #include <linux/timer.h> #include <linux/platform_device.h> +#include <linux/slab.h> #include <linux/delay.h> #include <linux/pci.h> #include <linux/gpio.h> diff --git a/drivers/pcmcia/bfin_cf_pcmcia.c b/drivers/pcmcia/bfin_cf_pcmcia.c index 300b368605c9..eae9cbe37a3e 100644 --- a/drivers/pcmcia/bfin_cf_pcmcia.c +++ b/drivers/pcmcia/bfin_cf_pcmcia.c @@ -31,6 +31,7 @@ #include <linux/platform_device.h> #include <linux/errno.h> #include <linux/init.h> +#include <linux/slab.h> #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/irq.h> @@ -112,7 +113,7 @@ static int bfin_cf_get_status(struct pcmcia_socket *s, u_int *sp) if (bfin_cf_present(cf->cd_pfx)) { *sp = SS_READY | SS_DETECT | SS_POWERON | SS_3VCARD; - s->irq.AssignedIRQ = 0; + s->pcmcia_irq = 0; s->pci_irq = cf->irq; } else @@ -205,7 +206,7 @@ static int __devinit bfin_cf_probe(struct platform_device *pdev) dev_info(&pdev->dev, "Blackfin CompactFlash/PCMCIA Socket Driver\n"); irq = platform_get_irq(pdev, 0); - if (!irq) + if (irq <= 0) return -EINVAL; cd_pfx = platform_get_irq(pdev, 1); /*Card Detect GPIO PIN */ @@ -300,16 +301,6 @@ static int __devexit bfin_cf_remove(struct platform_device *pdev) return 0; } -static int bfin_cf_suspend(struct platform_device *pdev, pm_message_t mesg) -{ - return pcmcia_socket_dev_suspend(&pdev->dev); -} - -static int bfin_cf_resume(struct platform_device *pdev) -{ - return pcmcia_socket_dev_resume(&pdev->dev); -} - static struct platform_driver bfin_cf_driver = { .driver = { .name = (char *)driver_name, @@ -317,8 +308,6 @@ static struct platform_driver bfin_cf_driver = { }, .probe = bfin_cf_probe, .remove = __devexit_p(bfin_cf_remove), - .suspend = bfin_cf_suspend, - .resume = bfin_cf_resume, }; static int __init bfin_cf_init(void) diff --git a/drivers/pcmcia/cardbus.c b/drivers/pcmcia/cardbus.c index d99f846451a3..9a58862f1401 100644 --- a/drivers/pcmcia/cardbus.c +++ b/drivers/pcmcia/cardbus.c @@ -20,170 +20,12 @@ */ -#include <linux/module.h> #include <linux/kernel.h> -#include <linux/string.h> -#include <linux/slab.h> -#include <linux/mm.h> +#include <linux/module.h> #include <linux/pci.h> -#include <linux/ioport.h> -#include <linux/io.h> -#include <asm/irq.h> -#include <pcmcia/cs_types.h> #include <pcmcia/ss.h> -#include <pcmcia/cs.h> -#include <pcmcia/cistpl.h> -#include "cs_internal.h" - -/*====================================================================*/ - -/* Offsets in the Expansion ROM Image Header */ -#define ROM_SIGNATURE 0x0000 /* 2 bytes */ -#define ROM_DATA_PTR 0x0018 /* 2 bytes */ - -/* Offsets in the CardBus PC Card Data Structure */ -#define PCDATA_SIGNATURE 0x0000 /* 4 bytes */ -#define PCDATA_VPD_PTR 0x0008 /* 2 bytes */ -#define PCDATA_LENGTH 0x000a /* 2 bytes */ -#define PCDATA_REVISION 0x000c -#define PCDATA_IMAGE_SZ 0x0010 /* 2 bytes */ -#define PCDATA_ROM_LEVEL 0x0012 /* 2 bytes */ -#define PCDATA_CODE_TYPE 0x0014 -#define PCDATA_INDICATOR 0x0015 - -/*===================================================================== - - Expansion ROM's have a special layout, and pointers specify an - image number and an offset within that image. xlate_rom_addr() - converts an image/offset address to an absolute offset from the - ROM's base address. - -=====================================================================*/ - -static u_int xlate_rom_addr(void __iomem *b, u_int addr) -{ - u_int img = 0, ofs = 0, sz; - u_short data; - while ((readb(b) == 0x55) && (readb(b + 1) == 0xaa)) { - if (img == (addr >> 28)) - return (addr & 0x0fffffff) + ofs; - data = readb(b + ROM_DATA_PTR) + (readb(b + ROM_DATA_PTR + 1) << 8); - sz = 512 * (readb(b + data + PCDATA_IMAGE_SZ) + - (readb(b + data + PCDATA_IMAGE_SZ + 1) << 8)); - if ((sz == 0) || (readb(b + data + PCDATA_INDICATOR) & 0x80)) - break; - b += sz; - ofs += sz; - img++; - } - return 0; -} - -/*===================================================================== - - These are similar to setup_cis_mem and release_cis_mem for 16-bit - cards. The "result" that is used externally is the cb_cis_virt - pointer in the struct pcmcia_socket structure. - -=====================================================================*/ - -static void cb_release_cis_mem(struct pcmcia_socket *s) -{ - if (s->cb_cis_virt) { - dev_dbg(&s->dev, "cb_release_cis_mem()\n"); - iounmap(s->cb_cis_virt); - s->cb_cis_virt = NULL; - s->cb_cis_res = NULL; - } -} - -static int cb_setup_cis_mem(struct pcmcia_socket *s, struct resource *res) -{ - unsigned int start, size; - - if (res == s->cb_cis_res) - return 0; - - if (s->cb_cis_res) - cb_release_cis_mem(s); - - start = res->start; - size = res->end - start + 1; - s->cb_cis_virt = ioremap(start, size); - - if (!s->cb_cis_virt) - return -1; - - s->cb_cis_res = res; - - return 0; -} - -/*===================================================================== - - This is used by the CIS processing code to read CIS information - from a CardBus device. - -=====================================================================*/ - -int read_cb_mem(struct pcmcia_socket *s, int space, u_int addr, u_int len, - void *ptr) -{ - struct pci_dev *dev; - struct resource *res; - - dev_dbg(&s->dev, "read_cb_mem(%d, %#x, %u)\n", space, addr, len); - dev = pci_get_slot(s->cb_dev->subordinate, 0); - if (!dev) - goto fail; - - /* Config space? */ - if (space == 0) { - if (addr + len > 0x100) - goto failput; - for (; len; addr++, ptr++, len--) - pci_read_config_byte(dev, addr, ptr); - return 0; - } - - res = dev->resource + space - 1; - - pci_dev_put(dev); - - if (!res->flags) - goto fail; - - if (cb_setup_cis_mem(s, res) != 0) - goto fail; - - if (space == 7) { - addr = xlate_rom_addr(s->cb_cis_virt, addr); - if (addr == 0) - goto fail; - } - - if (addr + len > res->end - res->start) - goto fail; - - memcpy_fromio(ptr, s->cb_cis_virt + addr, len); - return 0; - -failput: - pci_dev_put(dev); -fail: - memset(ptr, 0xff, len); - return -1; -} - -/*===================================================================== - - cb_alloc() and cb_free() allocate and free the kernel data - structures for a Cardbus device, and handle the lowest level PCI - device setup issues. - -=====================================================================*/ static void cardbus_config_irq_and_cls(struct pci_bus *bus, int irq) { @@ -215,6 +57,13 @@ static void cardbus_config_irq_and_cls(struct pci_bus *bus, int irq) } } +/** + * cb_alloc() - add CardBus device + * @s: the pcmcia_socket where the CardBus device is located + * + * cb_alloc() allocates the kernel data structures for a Cardbus device + * and handles the lowest level PCI device setup issues. + */ int __ref cb_alloc(struct pcmcia_socket *s) { struct pci_bus *bus = s->cb_dev->subordinate; @@ -222,7 +71,7 @@ int __ref cb_alloc(struct pcmcia_socket *s) unsigned int max, pass; s->functions = pci_scan_slot(bus, PCI_DEVFN(0, 0)); - pci_fixup_cardbus(bus); + pci_fixup_cardbus(bus); max = bus->secondary; for (pass = 0; pass < 2; pass++) @@ -245,16 +94,19 @@ int __ref cb_alloc(struct pcmcia_socket *s) pci_enable_bridges(bus); pci_bus_add_devices(bus); - s->irq.AssignedIRQ = s->pci_irq; return 0; } +/** + * cb_free() - remove CardBus device + * @s: the pcmcia_socket where the CardBus device was located + * + * cb_free() handles the lowest level PCI device cleanup. + */ void cb_free(struct pcmcia_socket *s) { struct pci_dev *bridge = s->cb_dev; - cb_release_cis_mem(s); - if (bridge) pci_remove_behind_bridge(bridge); } diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c index 25b1cd219e37..8844bc3e3118 100644 --- a/drivers/pcmcia/cistpl.c +++ b/drivers/pcmcia/cistpl.c @@ -54,43 +54,44 @@ static const u_int exponent[] = { /* Upper limit on reasonable # of tuples */ #define MAX_TUPLES 200 -/*====================================================================*/ - -/* Parameters that can be set with 'insmod' */ - /* 16-bit CIS? */ static int cis_width; module_param(cis_width, int, 0444); void release_cis_mem(struct pcmcia_socket *s) { - if (s->cis_mem.flags & MAP_ACTIVE) { - s->cis_mem.flags &= ~MAP_ACTIVE; - s->ops->set_mem_map(s, &s->cis_mem); - if (s->cis_mem.res) { - release_resource(s->cis_mem.res); - kfree(s->cis_mem.res); - s->cis_mem.res = NULL; + mutex_lock(&s->ops_mutex); + if (s->cis_mem.flags & MAP_ACTIVE) { + s->cis_mem.flags &= ~MAP_ACTIVE; + s->ops->set_mem_map(s, &s->cis_mem); + if (s->cis_mem.res) { + release_resource(s->cis_mem.res); + kfree(s->cis_mem.res); + s->cis_mem.res = NULL; + } + iounmap(s->cis_virt); + s->cis_virt = NULL; } - iounmap(s->cis_virt); - s->cis_virt = NULL; - } + mutex_unlock(&s->ops_mutex); } -EXPORT_SYMBOL(release_cis_mem); -/* - * Map the card memory at "card_offset" into virtual space. +/** + * set_cis_map() - map the card memory at "card_offset" into virtual space. + * * If flags & MAP_ATTRIB, map the attribute space, otherwise * map the memory space. + * + * Must be called with ops_mutex held. */ -static void __iomem * -set_cis_map(struct pcmcia_socket *s, unsigned int card_offset, unsigned int flags) +static void __iomem *set_cis_map(struct pcmcia_socket *s, + unsigned int card_offset, unsigned int flags) { pccard_mem_map *mem = &s->cis_mem; int ret; if (!(s->features & SS_CAP_STATIC_MAP) && (mem->res == NULL)) { - mem->res = pcmcia_find_mem_region(0, s->map_size, s->map_size, 0, s); + mem->res = pcmcia_find_mem_region(0, s->map_size, + s->map_size, 0, s); if (mem->res == NULL) { dev_printk(KERN_NOTICE, &s->dev, "cs: unable to map card memory!\n"); @@ -121,176 +122,194 @@ set_cis_map(struct pcmcia_socket *s, unsigned int card_offset, unsigned int flag return s->cis_virt; } -/*====================================================================== - - Low-level functions to read and write CIS memory. I think the - write routine is only useful for writing one-byte registers. - -======================================================================*/ /* Bits in attr field */ #define IS_ATTR 1 #define IS_INDIRECT 8 +/** + * pcmcia_read_cis_mem() - low-level function to read CIS memory + * + * must be called with ops_mutex held + */ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, u_int len, void *ptr) { - void __iomem *sys, *end; - unsigned char *buf = ptr; - - dev_dbg(&s->dev, "pcmcia_read_cis_mem(%d, %#x, %u)\n", attr, addr, len); - - if (attr & IS_INDIRECT) { - /* Indirect accesses use a bunch of special registers at fixed - locations in common memory */ - u_char flags = ICTRL0_COMMON|ICTRL0_AUTOINC|ICTRL0_BYTEGRAN; - if (attr & IS_ATTR) { - addr *= 2; - flags = ICTRL0_AUTOINC; - } + void __iomem *sys, *end; + unsigned char *buf = ptr; + + dev_dbg(&s->dev, "pcmcia_read_cis_mem(%d, %#x, %u)\n", attr, addr, len); + + if (attr & IS_INDIRECT) { + /* Indirect accesses use a bunch of special registers at fixed + locations in common memory */ + u_char flags = ICTRL0_COMMON|ICTRL0_AUTOINC|ICTRL0_BYTEGRAN; + if (attr & IS_ATTR) { + addr *= 2; + flags = ICTRL0_AUTOINC; + } - sys = set_cis_map(s, 0, MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0)); - if (!sys) { - memset(ptr, 0xff, len); - return -1; - } + sys = set_cis_map(s, 0, MAP_ACTIVE | + ((cis_width) ? MAP_16BIT : 0)); + if (!sys) { + dev_dbg(&s->dev, "could not map memory\n"); + memset(ptr, 0xff, len); + return -1; + } - writeb(flags, sys+CISREG_ICTRL0); - writeb(addr & 0xff, sys+CISREG_IADDR0); - writeb((addr>>8) & 0xff, sys+CISREG_IADDR1); - writeb((addr>>16) & 0xff, sys+CISREG_IADDR2); - writeb((addr>>24) & 0xff, sys+CISREG_IADDR3); - for ( ; len > 0; len--, buf++) - *buf = readb(sys+CISREG_IDATA0); - } else { - u_int inc = 1, card_offset, flags; - - flags = MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0); - if (attr) { - flags |= MAP_ATTRIB; - inc++; - addr *= 2; - } + writeb(flags, sys+CISREG_ICTRL0); + writeb(addr & 0xff, sys+CISREG_IADDR0); + writeb((addr>>8) & 0xff, sys+CISREG_IADDR1); + writeb((addr>>16) & 0xff, sys+CISREG_IADDR2); + writeb((addr>>24) & 0xff, sys+CISREG_IADDR3); + for ( ; len > 0; len--, buf++) + *buf = readb(sys+CISREG_IDATA0); + } else { + u_int inc = 1, card_offset, flags; - card_offset = addr & ~(s->map_size-1); - while (len) { - sys = set_cis_map(s, card_offset, flags); - if (!sys) { - memset(ptr, 0xff, len); - return -1; - } - end = sys + s->map_size; - sys = sys + (addr & (s->map_size-1)); - for ( ; len > 0; len--, buf++, sys += inc) { - if (sys == end) - break; - *buf = readb(sys); - } - card_offset += s->map_size; - addr = 0; + if (addr > CISTPL_MAX_CIS_SIZE) + dev_dbg(&s->dev, + "attempt to read CIS mem at addr %#x", addr); + + flags = MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0); + if (attr) { + flags |= MAP_ATTRIB; + inc++; + addr *= 2; + } + + card_offset = addr & ~(s->map_size-1); + while (len) { + sys = set_cis_map(s, card_offset, flags); + if (!sys) { + dev_dbg(&s->dev, "could not map memory\n"); + memset(ptr, 0xff, len); + return -1; + } + end = sys + s->map_size; + sys = sys + (addr & (s->map_size-1)); + for ( ; len > 0; len--, buf++, sys += inc) { + if (sys == end) + break; + *buf = readb(sys); + } + card_offset += s->map_size; + addr = 0; + } } - } - dev_dbg(&s->dev, " %#2.2x %#2.2x %#2.2x %#2.2x ...\n", - *(u_char *)(ptr+0), *(u_char *)(ptr+1), - *(u_char *)(ptr+2), *(u_char *)(ptr+3)); - return 0; + dev_dbg(&s->dev, " %#2.2x %#2.2x %#2.2x %#2.2x ...\n", + *(u_char *)(ptr+0), *(u_char *)(ptr+1), + *(u_char *)(ptr+2), *(u_char *)(ptr+3)); + return 0; } -EXPORT_SYMBOL(pcmcia_read_cis_mem); +/** + * pcmcia_write_cis_mem() - low-level function to write CIS memory + * + * Probably only useful for writing one-byte registers. Must be called + * with ops_mutex held. + */ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, u_int len, void *ptr) { - void __iomem *sys, *end; - unsigned char *buf = ptr; - - dev_dbg(&s->dev, "pcmcia_write_cis_mem(%d, %#x, %u)\n", attr, addr, len); - - if (attr & IS_INDIRECT) { - /* Indirect accesses use a bunch of special registers at fixed - locations in common memory */ - u_char flags = ICTRL0_COMMON|ICTRL0_AUTOINC|ICTRL0_BYTEGRAN; - if (attr & IS_ATTR) { - addr *= 2; - flags = ICTRL0_AUTOINC; - } + void __iomem *sys, *end; + unsigned char *buf = ptr; + + dev_dbg(&s->dev, + "pcmcia_write_cis_mem(%d, %#x, %u)\n", attr, addr, len); + + if (attr & IS_INDIRECT) { + /* Indirect accesses use a bunch of special registers at fixed + locations in common memory */ + u_char flags = ICTRL0_COMMON|ICTRL0_AUTOINC|ICTRL0_BYTEGRAN; + if (attr & IS_ATTR) { + addr *= 2; + flags = ICTRL0_AUTOINC; + } - sys = set_cis_map(s, 0, MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0)); - if (!sys) - return; /* FIXME: Error */ - - writeb(flags, sys+CISREG_ICTRL0); - writeb(addr & 0xff, sys+CISREG_IADDR0); - writeb((addr>>8) & 0xff, sys+CISREG_IADDR1); - writeb((addr>>16) & 0xff, sys+CISREG_IADDR2); - writeb((addr>>24) & 0xff, sys+CISREG_IADDR3); - for ( ; len > 0; len--, buf++) - writeb(*buf, sys+CISREG_IDATA0); - } else { - u_int inc = 1, card_offset, flags; - - flags = MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0); - if (attr & IS_ATTR) { - flags |= MAP_ATTRIB; - inc++; - addr *= 2; - } + sys = set_cis_map(s, 0, MAP_ACTIVE | + ((cis_width) ? MAP_16BIT : 0)); + if (!sys) { + dev_dbg(&s->dev, "could not map memory\n"); + return; /* FIXME: Error */ + } - card_offset = addr & ~(s->map_size-1); - while (len) { - sys = set_cis_map(s, card_offset, flags); - if (!sys) - return; /* FIXME: error */ - - end = sys + s->map_size; - sys = sys + (addr & (s->map_size-1)); - for ( ; len > 0; len--, buf++, sys += inc) { - if (sys == end) - break; - writeb(*buf, sys); - } - card_offset += s->map_size; - addr = 0; + writeb(flags, sys+CISREG_ICTRL0); + writeb(addr & 0xff, sys+CISREG_IADDR0); + writeb((addr>>8) & 0xff, sys+CISREG_IADDR1); + writeb((addr>>16) & 0xff, sys+CISREG_IADDR2); + writeb((addr>>24) & 0xff, sys+CISREG_IADDR3); + for ( ; len > 0; len--, buf++) + writeb(*buf, sys+CISREG_IDATA0); + } else { + u_int inc = 1, card_offset, flags; + + flags = MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0); + if (attr & IS_ATTR) { + flags |= MAP_ATTRIB; + inc++; + addr *= 2; + } + + card_offset = addr & ~(s->map_size-1); + while (len) { + sys = set_cis_map(s, card_offset, flags); + if (!sys) { + dev_dbg(&s->dev, "could not map memory\n"); + return; /* FIXME: error */ + } + + end = sys + s->map_size; + sys = sys + (addr & (s->map_size-1)); + for ( ; len > 0; len--, buf++, sys += inc) { + if (sys == end) + break; + writeb(*buf, sys); + } + card_offset += s->map_size; + addr = 0; + } } - } } -EXPORT_SYMBOL(pcmcia_write_cis_mem); -/*====================================================================== +/** + * read_cis_cache() - read CIS memory or its associated cache + * + * This is a wrapper around read_cis_mem, with the same interface, + * but which caches information, for cards whose CIS may not be + * readable all the time. + */ +static int read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr, + size_t len, void *ptr) +{ + struct cis_cache_entry *cis; + int ret = 0; - This is a wrapper around read_cis_mem, with the same interface, - but which caches information, for cards whose CIS may not be - readable all the time. + if (s->state & SOCKET_CARDBUS) + return -EINVAL; -======================================================================*/ + mutex_lock(&s->ops_mutex); + if (s->fake_cis) { + if (s->fake_cis_len >= addr+len) + memcpy(ptr, s->fake_cis+addr, len); + else { + memset(ptr, 0xff, len); + ret = -EINVAL; + } + mutex_unlock(&s->ops_mutex); + return ret; + } -static void read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr, - size_t len, void *ptr) -{ - struct cis_cache_entry *cis; - int ret; - - if (s->fake_cis) { - if (s->fake_cis_len >= addr+len) - memcpy(ptr, s->fake_cis+addr, len); - else - memset(ptr, 0xff, len); - return; - } - - list_for_each_entry(cis, &s->cis_cache, node) { - if (cis->addr == addr && cis->len == len && cis->attr == attr) { - memcpy(ptr, cis->cache, len); - return; + list_for_each_entry(cis, &s->cis_cache, node) { + if (cis->addr == addr && cis->len == len && cis->attr == attr) { + memcpy(ptr, cis->cache, len); + mutex_unlock(&s->ops_mutex); + return 0; + } } - } -#ifdef CONFIG_CARDBUS - if (s->state & SOCKET_CARDBUS) - ret = read_cb_mem(s, attr, addr, len, ptr); - else -#endif ret = pcmcia_read_cis_mem(s, attr, addr, len, ptr); if (ret == 0) { @@ -304,6 +323,9 @@ static void read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr, list_add(&cis->node, &s->cis_cache); } } + mutex_unlock(&s->ops_mutex); + + return ret; } static void @@ -311,44 +333,46 @@ remove_cis_cache(struct pcmcia_socket *s, int attr, u_int addr, u_int len) { struct cis_cache_entry *cis; + mutex_lock(&s->ops_mutex); list_for_each_entry(cis, &s->cis_cache, node) if (cis->addr == addr && cis->len == len && cis->attr == attr) { list_del(&cis->node); kfree(cis); break; } + mutex_unlock(&s->ops_mutex); } +/** + * destroy_cis_cache() - destroy the CIS cache + * @s: pcmcia_socket for which CIS cache shall be destroyed + * + * This destroys the CIS cache but keeps any fake CIS alive. Must be + * called with ops_mutex held. + */ void destroy_cis_cache(struct pcmcia_socket *s) { struct list_head *l, *n; + struct cis_cache_entry *cis; list_for_each_safe(l, n, &s->cis_cache) { - struct cis_cache_entry *cis = list_entry(l, struct cis_cache_entry, node); - + cis = list_entry(l, struct cis_cache_entry, node); list_del(&cis->node); kfree(cis); } - - /* - * If there was a fake CIS, destroy that as well. - */ - kfree(s->fake_cis); - s->fake_cis = NULL; } -EXPORT_SYMBOL(destroy_cis_cache); - -/*====================================================================== - - This verifies if the CIS of a card matches what is in the CIS - cache. - -======================================================================*/ +/** + * verify_cis_cache() - does the CIS match what is in the CIS cache? + */ int verify_cis_cache(struct pcmcia_socket *s) { struct cis_cache_entry *cis; char *buf; + int ret; + + if (s->state & SOCKET_CARDBUS) + return -EINVAL; buf = kmalloc(256, GFP_KERNEL); if (buf == NULL) { @@ -356,34 +380,31 @@ int verify_cis_cache(struct pcmcia_socket *s) "no memory for verifying CIS\n"); return -ENOMEM; } + mutex_lock(&s->ops_mutex); list_for_each_entry(cis, &s->cis_cache, node) { int len = cis->len; if (len > 256) len = 256; -#ifdef CONFIG_CARDBUS - if (s->state & SOCKET_CARDBUS) - read_cb_mem(s, cis->attr, cis->addr, len, buf); - else -#endif - pcmcia_read_cis_mem(s, cis->attr, cis->addr, len, buf); - - if (memcmp(buf, cis->cache, len) != 0) { + + ret = pcmcia_read_cis_mem(s, cis->attr, cis->addr, len, buf); + if (ret || memcmp(buf, cis->cache, len) != 0) { kfree(buf); + mutex_unlock(&s->ops_mutex); return -1; } } kfree(buf); + mutex_unlock(&s->ops_mutex); return 0; } -/*====================================================================== - - For really bad cards, we provide a facility for uploading a - replacement CIS. - -======================================================================*/ - +/** + * pcmcia_replace_cis() - use a replacement CIS instead of the card's CIS + * + * For really bad cards, we provide a facility for uploading a + * replacement CIS. + */ int pcmcia_replace_cis(struct pcmcia_socket *s, const u8 *data, const size_t len) { @@ -391,29 +412,28 @@ int pcmcia_replace_cis(struct pcmcia_socket *s, dev_printk(KERN_WARNING, &s->dev, "replacement CIS too big\n"); return -EINVAL; } + mutex_lock(&s->ops_mutex); kfree(s->fake_cis); s->fake_cis = kmalloc(len, GFP_KERNEL); if (s->fake_cis == NULL) { dev_printk(KERN_WARNING, &s->dev, "no memory to replace CIS\n"); + mutex_unlock(&s->ops_mutex); return -ENOMEM; } s->fake_cis_len = len; memcpy(s->fake_cis, data, len); + dev_info(&s->dev, "Using replacement CIS\n"); + mutex_unlock(&s->ops_mutex); return 0; } -EXPORT_SYMBOL(pcmcia_replace_cis); - -/*====================================================================== - - The high-level CIS tuple services -======================================================================*/ +/* The high-level CIS tuple services */ typedef struct tuple_flags { - u_int link_space:4; - u_int has_link:1; - u_int mfc_fn:3; - u_int space:4; + u_int link_space:4; + u_int has_link:1; + u_int mfc_fn:3; + u_int space:4; } tuple_flags; #define LINK_SPACE(f) (((tuple_flags *)(&(f)))->link_space) @@ -421,1236 +441,1158 @@ typedef struct tuple_flags { #define MFC_FN(f) (((tuple_flags *)(&(f)))->mfc_fn) #define SPACE(f) (((tuple_flags *)(&(f)))->space) -int pccard_get_first_tuple(struct pcmcia_socket *s, unsigned int function, tuple_t *tuple) +int pccard_get_first_tuple(struct pcmcia_socket *s, unsigned int function, + tuple_t *tuple) { - if (!s) - return -EINVAL; - if (!(s->state & SOCKET_PRESENT)) - return -ENODEV; - tuple->TupleLink = tuple->Flags = 0; -#ifdef CONFIG_CARDBUS - if (s->state & SOCKET_CARDBUS) { - struct pci_dev *dev = s->cb_dev; - u_int ptr; - pci_bus_read_config_dword(dev->subordinate, 0, PCI_CARDBUS_CIS, &ptr); - tuple->CISOffset = ptr & ~7; - SPACE(tuple->Flags) = (ptr & 7); - } else -#endif - { + if (!s) + return -EINVAL; + + if (!(s->state & SOCKET_PRESENT) || (s->state & SOCKET_CARDBUS)) + return -ENODEV; + tuple->TupleLink = tuple->Flags = 0; + /* Assume presence of a LONGLINK_C to address 0 */ tuple->CISOffset = tuple->LinkOffset = 0; SPACE(tuple->Flags) = HAS_LINK(tuple->Flags) = 1; - } - if (!(s->state & SOCKET_CARDBUS) && (s->functions > 1) && - !(tuple->Attributes & TUPLE_RETURN_COMMON)) { - cisdata_t req = tuple->DesiredTuple; - tuple->DesiredTuple = CISTPL_LONGLINK_MFC; - if (pccard_get_next_tuple(s, function, tuple) == 0) { - tuple->DesiredTuple = CISTPL_LINKTARGET; - if (pccard_get_next_tuple(s, function, tuple) != 0) - return -ENOSPC; - } else - tuple->CISOffset = tuple->TupleLink = 0; - tuple->DesiredTuple = req; - } - return pccard_get_next_tuple(s, function, tuple); + + if ((s->functions > 1) && !(tuple->Attributes & TUPLE_RETURN_COMMON)) { + cisdata_t req = tuple->DesiredTuple; + tuple->DesiredTuple = CISTPL_LONGLINK_MFC; + if (pccard_get_next_tuple(s, function, tuple) == 0) { + tuple->DesiredTuple = CISTPL_LINKTARGET; + if (pccard_get_next_tuple(s, function, tuple) != 0) + return -ENOSPC; + } else + tuple->CISOffset = tuple->TupleLink = 0; + tuple->DesiredTuple = req; + } + return pccard_get_next_tuple(s, function, tuple); } -EXPORT_SYMBOL(pccard_get_first_tuple); static int follow_link(struct pcmcia_socket *s, tuple_t *tuple) { - u_char link[5]; - u_int ofs; - - if (MFC_FN(tuple->Flags)) { - /* Get indirect link from the MFC tuple */ - read_cis_cache(s, LINK_SPACE(tuple->Flags), - tuple->LinkOffset, 5, link); - ofs = get_unaligned_le32(link + 1); - SPACE(tuple->Flags) = (link[0] == CISTPL_MFC_ATTR); - /* Move to the next indirect link */ - tuple->LinkOffset += 5; - MFC_FN(tuple->Flags)--; - } else if (HAS_LINK(tuple->Flags)) { - ofs = tuple->LinkOffset; - SPACE(tuple->Flags) = LINK_SPACE(tuple->Flags); - HAS_LINK(tuple->Flags) = 0; - } else { - return -1; - } - if (!(s->state & SOCKET_CARDBUS) && SPACE(tuple->Flags)) { - /* This is ugly, but a common CIS error is to code the long - link offset incorrectly, so we check the right spot... */ - read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link); + u_char link[5]; + u_int ofs; + int ret; + + if (MFC_FN(tuple->Flags)) { + /* Get indirect link from the MFC tuple */ + ret = read_cis_cache(s, LINK_SPACE(tuple->Flags), + tuple->LinkOffset, 5, link); + if (ret) + return -1; + ofs = get_unaligned_le32(link + 1); + SPACE(tuple->Flags) = (link[0] == CISTPL_MFC_ATTR); + /* Move to the next indirect link */ + tuple->LinkOffset += 5; + MFC_FN(tuple->Flags)--; + } else if (HAS_LINK(tuple->Flags)) { + ofs = tuple->LinkOffset; + SPACE(tuple->Flags) = LINK_SPACE(tuple->Flags); + HAS_LINK(tuple->Flags) = 0; + } else + return -1; + + if (SPACE(tuple->Flags)) { + /* This is ugly, but a common CIS error is to code the long + link offset incorrectly, so we check the right spot... */ + ret = read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link); + if (ret) + return -1; + if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) && + (strncmp(link+2, "CIS", 3) == 0)) + return ofs; + remove_cis_cache(s, SPACE(tuple->Flags), ofs, 5); + /* Then, we try the wrong spot... */ + ofs = ofs >> 1; + } + ret = read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link); + if (ret) + return -1; if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) && - (strncmp(link+2, "CIS", 3) == 0)) - return ofs; + (strncmp(link+2, "CIS", 3) == 0)) + return ofs; remove_cis_cache(s, SPACE(tuple->Flags), ofs, 5); - /* Then, we try the wrong spot... */ - ofs = ofs >> 1; - } - read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link); - if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) && - (strncmp(link+2, "CIS", 3) == 0)) - return ofs; - remove_cis_cache(s, SPACE(tuple->Flags), ofs, 5); - return -1; + return -1; } -int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function, tuple_t *tuple) +int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function, + tuple_t *tuple) { - u_char link[2], tmp; - int ofs, i, attr; + u_char link[2], tmp; + int ofs, i, attr; + int ret; - if (!s) - return -EINVAL; - if (!(s->state & SOCKET_PRESENT)) - return -ENODEV; + if (!s) + return -EINVAL; + if (!(s->state & SOCKET_PRESENT) || (s->state & SOCKET_CARDBUS)) + return -ENODEV; + + link[1] = tuple->TupleLink; + ofs = tuple->CISOffset + tuple->TupleLink; + attr = SPACE(tuple->Flags); + + for (i = 0; i < MAX_TUPLES; i++) { + if (link[1] == 0xff) + link[0] = CISTPL_END; + else { + ret = read_cis_cache(s, attr, ofs, 2, link); + if (ret) + return -1; + if (link[0] == CISTPL_NULL) { + ofs++; + continue; + } + } - link[1] = tuple->TupleLink; - ofs = tuple->CISOffset + tuple->TupleLink; - attr = SPACE(tuple->Flags); + /* End of chain? Follow long link if possible */ + if (link[0] == CISTPL_END) { + ofs = follow_link(s, tuple); + if (ofs < 0) + return -ENOSPC; + attr = SPACE(tuple->Flags); + ret = read_cis_cache(s, attr, ofs, 2, link); + if (ret) + return -1; + } - for (i = 0; i < MAX_TUPLES; i++) { - if (link[1] == 0xff) { - link[0] = CISTPL_END; - } else { - read_cis_cache(s, attr, ofs, 2, link); - if (link[0] == CISTPL_NULL) { - ofs++; continue; - } + /* Is this a link tuple? Make a note of it */ + if ((link[0] == CISTPL_LONGLINK_A) || + (link[0] == CISTPL_LONGLINK_C) || + (link[0] == CISTPL_LONGLINK_MFC) || + (link[0] == CISTPL_LINKTARGET) || + (link[0] == CISTPL_INDIRECT) || + (link[0] == CISTPL_NO_LINK)) { + switch (link[0]) { + case CISTPL_LONGLINK_A: + HAS_LINK(tuple->Flags) = 1; + LINK_SPACE(tuple->Flags) = attr | IS_ATTR; + ret = read_cis_cache(s, attr, ofs+2, 4, + &tuple->LinkOffset); + if (ret) + return -1; + break; + case CISTPL_LONGLINK_C: + HAS_LINK(tuple->Flags) = 1; + LINK_SPACE(tuple->Flags) = attr & ~IS_ATTR; + ret = read_cis_cache(s, attr, ofs+2, 4, + &tuple->LinkOffset); + if (ret) + return -1; + break; + case CISTPL_INDIRECT: + HAS_LINK(tuple->Flags) = 1; + LINK_SPACE(tuple->Flags) = IS_ATTR | + IS_INDIRECT; + tuple->LinkOffset = 0; + break; + case CISTPL_LONGLINK_MFC: + tuple->LinkOffset = ofs + 3; + LINK_SPACE(tuple->Flags) = attr; + if (function == BIND_FN_ALL) { + /* Follow all the MFC links */ + ret = read_cis_cache(s, attr, ofs+2, + 1, &tmp); + if (ret) + return -1; + MFC_FN(tuple->Flags) = tmp; + } else { + /* Follow exactly one of the links */ + MFC_FN(tuple->Flags) = 1; + tuple->LinkOffset += function * 5; + } + break; + case CISTPL_NO_LINK: + HAS_LINK(tuple->Flags) = 0; + break; + } + if ((tuple->Attributes & TUPLE_RETURN_LINK) && + (tuple->DesiredTuple == RETURN_FIRST_TUPLE)) + break; + } else + if (tuple->DesiredTuple == RETURN_FIRST_TUPLE) + break; + + if (link[0] == tuple->DesiredTuple) + break; + ofs += link[1] + 2; } - - /* End of chain? Follow long link if possible */ - if (link[0] == CISTPL_END) { - ofs = follow_link(s, tuple); - if (ofs < 0) + if (i == MAX_TUPLES) { + dev_dbg(&s->dev, "cs: overrun in pcmcia_get_next_tuple\n"); return -ENOSPC; - attr = SPACE(tuple->Flags); - read_cis_cache(s, attr, ofs, 2, link); } - /* Is this a link tuple? Make a note of it */ - if ((link[0] == CISTPL_LONGLINK_A) || - (link[0] == CISTPL_LONGLINK_C) || - (link[0] == CISTPL_LONGLINK_MFC) || - (link[0] == CISTPL_LINKTARGET) || - (link[0] == CISTPL_INDIRECT) || - (link[0] == CISTPL_NO_LINK)) { - switch (link[0]) { - case CISTPL_LONGLINK_A: - HAS_LINK(tuple->Flags) = 1; - LINK_SPACE(tuple->Flags) = attr | IS_ATTR; - read_cis_cache(s, attr, ofs+2, 4, &tuple->LinkOffset); - break; - case CISTPL_LONGLINK_C: - HAS_LINK(tuple->Flags) = 1; - LINK_SPACE(tuple->Flags) = attr & ~IS_ATTR; - read_cis_cache(s, attr, ofs+2, 4, &tuple->LinkOffset); - break; - case CISTPL_INDIRECT: - HAS_LINK(tuple->Flags) = 1; - LINK_SPACE(tuple->Flags) = IS_ATTR | IS_INDIRECT; - tuple->LinkOffset = 0; - break; - case CISTPL_LONGLINK_MFC: - tuple->LinkOffset = ofs + 3; - LINK_SPACE(tuple->Flags) = attr; - if (function == BIND_FN_ALL) { - /* Follow all the MFC links */ - read_cis_cache(s, attr, ofs+2, 1, &tmp); - MFC_FN(tuple->Flags) = tmp; - } else { - /* Follow exactly one of the links */ - MFC_FN(tuple->Flags) = 1; - tuple->LinkOffset += function * 5; - } - break; - case CISTPL_NO_LINK: - HAS_LINK(tuple->Flags) = 0; - break; - } - if ((tuple->Attributes & TUPLE_RETURN_LINK) && - (tuple->DesiredTuple == RETURN_FIRST_TUPLE)) - break; - } else - if (tuple->DesiredTuple == RETURN_FIRST_TUPLE) - break; - - if (link[0] == tuple->DesiredTuple) - break; - ofs += link[1] + 2; - } - if (i == MAX_TUPLES) { - dev_dbg(&s->dev, "cs: overrun in pcmcia_get_next_tuple\n"); - return -ENOSPC; - } - - tuple->TupleCode = link[0]; - tuple->TupleLink = link[1]; - tuple->CISOffset = ofs + 2; - return 0; + tuple->TupleCode = link[0]; + tuple->TupleLink = link[1]; + tuple->CISOffset = ofs + 2; + return 0; } -EXPORT_SYMBOL(pccard_get_next_tuple); - -/*====================================================================*/ - -#define _MIN(a, b) (((a) < (b)) ? (a) : (b)) int pccard_get_tuple_data(struct pcmcia_socket *s, tuple_t *tuple) { - u_int len; + u_int len; + int ret; - if (!s) - return -EINVAL; + if (!s) + return -EINVAL; - if (tuple->TupleLink < tuple->TupleOffset) - return -ENOSPC; - len = tuple->TupleLink - tuple->TupleOffset; - tuple->TupleDataLen = tuple->TupleLink; - if (len == 0) + if (tuple->TupleLink < tuple->TupleOffset) + return -ENOSPC; + len = tuple->TupleLink - tuple->TupleOffset; + tuple->TupleDataLen = tuple->TupleLink; + if (len == 0) + return 0; + ret = read_cis_cache(s, SPACE(tuple->Flags), + tuple->CISOffset + tuple->TupleOffset, + min(len, (u_int) tuple->TupleDataMax), + tuple->TupleData); + if (ret) + return -1; return 0; - read_cis_cache(s, SPACE(tuple->Flags), - tuple->CISOffset + tuple->TupleOffset, - _MIN(len, tuple->TupleDataMax), tuple->TupleData); - return 0; } -EXPORT_SYMBOL(pccard_get_tuple_data); - -/*====================================================================== - Parsing routines for individual tuples - -======================================================================*/ +/* Parsing routines for individual tuples */ static int parse_device(tuple_t *tuple, cistpl_device_t *device) { - int i; - u_char scale; - u_char *p, *q; + int i; + u_char scale; + u_char *p, *q; - p = (u_char *)tuple->TupleData; - q = p + tuple->TupleDataLen; + p = (u_char *)tuple->TupleData; + q = p + tuple->TupleDataLen; - device->ndev = 0; - for (i = 0; i < CISTPL_MAX_DEVICES; i++) { + device->ndev = 0; + for (i = 0; i < CISTPL_MAX_DEVICES; i++) { - if (*p == 0xff) - break; - device->dev[i].type = (*p >> 4); - device->dev[i].wp = (*p & 0x08) ? 1 : 0; - switch (*p & 0x07) { - case 0: - device->dev[i].speed = 0; - break; - case 1: - device->dev[i].speed = 250; - break; - case 2: - device->dev[i].speed = 200; - break; - case 3: - device->dev[i].speed = 150; - break; - case 4: - device->dev[i].speed = 100; - break; - case 7: - if (++p == q) - return -EINVAL; - device->dev[i].speed = SPEED_CVT(*p); - while (*p & 0x80) + if (*p == 0xff) + break; + device->dev[i].type = (*p >> 4); + device->dev[i].wp = (*p & 0x08) ? 1 : 0; + switch (*p & 0x07) { + case 0: + device->dev[i].speed = 0; + break; + case 1: + device->dev[i].speed = 250; + break; + case 2: + device->dev[i].speed = 200; + break; + case 3: + device->dev[i].speed = 150; + break; + case 4: + device->dev[i].speed = 100; + break; + case 7: if (++p == q) return -EINVAL; - break; - default: - return -EINVAL; - } + device->dev[i].speed = SPEED_CVT(*p); + while (*p & 0x80) + if (++p == q) + return -EINVAL; + break; + default: + return -EINVAL; + } - if (++p == q) - return -EINVAL; - if (*p == 0xff) - break; - scale = *p & 7; - if (scale == 7) - return -EINVAL; - device->dev[i].size = ((*p >> 3) + 1) * (512 << (scale*2)); - device->ndev++; - if (++p == q) - break; - } + if (++p == q) + return -EINVAL; + if (*p == 0xff) + break; + scale = *p & 7; + if (scale == 7) + return -EINVAL; + device->dev[i].size = ((*p >> 3) + 1) * (512 << (scale*2)); + device->ndev++; + if (++p == q) + break; + } - return 0; + return 0; } -/*====================================================================*/ static int parse_checksum(tuple_t *tuple, cistpl_checksum_t *csum) { - u_char *p; - if (tuple->TupleDataLen < 5) - return -EINVAL; - p = (u_char *) tuple->TupleData; - csum->addr = tuple->CISOffset + get_unaligned_le16(p) - 2; - csum->len = get_unaligned_le16(p + 2); - csum->sum = *(p + 4); - return 0; + u_char *p; + if (tuple->TupleDataLen < 5) + return -EINVAL; + p = (u_char *) tuple->TupleData; + csum->addr = tuple->CISOffset + get_unaligned_le16(p) - 2; + csum->len = get_unaligned_le16(p + 2); + csum->sum = *(p + 4); + return 0; } -/*====================================================================*/ static int parse_longlink(tuple_t *tuple, cistpl_longlink_t *link) { - if (tuple->TupleDataLen < 4) - return -EINVAL; - link->addr = get_unaligned_le32(tuple->TupleData); - return 0; + if (tuple->TupleDataLen < 4) + return -EINVAL; + link->addr = get_unaligned_le32(tuple->TupleData); + return 0; } -/*====================================================================*/ -static int parse_longlink_mfc(tuple_t *tuple, - cistpl_longlink_mfc_t *link) +static int parse_longlink_mfc(tuple_t *tuple, cistpl_longlink_mfc_t *link) { - u_char *p; - int i; - - p = (u_char *)tuple->TupleData; - - link->nfn = *p; p++; - if (tuple->TupleDataLen <= link->nfn*5) - return -EINVAL; - for (i = 0; i < link->nfn; i++) { - link->fn[i].space = *p; p++; - link->fn[i].addr = get_unaligned_le32(p); - p += 4; - } - return 0; + u_char *p; + int i; + + p = (u_char *)tuple->TupleData; + + link->nfn = *p; p++; + if (tuple->TupleDataLen <= link->nfn*5) + return -EINVAL; + for (i = 0; i < link->nfn; i++) { + link->fn[i].space = *p; p++; + link->fn[i].addr = get_unaligned_le32(p); + p += 4; + } + return 0; } -/*====================================================================*/ static int parse_strings(u_char *p, u_char *q, int max, char *s, u_char *ofs, u_char *found) { - int i, j, ns; + int i, j, ns; - if (p == q) - return -EINVAL; - ns = 0; j = 0; - for (i = 0; i < max; i++) { - if (*p == 0xff) - break; - ofs[i] = j; - ns++; - for (;;) { - s[j++] = (*p == 0xff) ? '\0' : *p; - if ((*p == '\0') || (*p == 0xff)) - break; - if (++p == q) - return -EINVAL; + if (p == q) + return -EINVAL; + ns = 0; j = 0; + for (i = 0; i < max; i++) { + if (*p == 0xff) + break; + ofs[i] = j; + ns++; + for (;;) { + s[j++] = (*p == 0xff) ? '\0' : *p; + if ((*p == '\0') || (*p == 0xff)) + break; + if (++p == q) + return -EINVAL; + } + if ((*p == 0xff) || (++p == q)) + break; } - if ((*p == 0xff) || (++p == q)) - break; - } - if (found) { - *found = ns; - return 0; - } else { + if (found) { + *found = ns; + return 0; + } + return (ns == max) ? 0 : -EINVAL; - } } -/*====================================================================*/ static int parse_vers_1(tuple_t *tuple, cistpl_vers_1_t *vers_1) { - u_char *p, *q; + u_char *p, *q; - p = (u_char *)tuple->TupleData; - q = p + tuple->TupleDataLen; + p = (u_char *)tuple->TupleData; + q = p + tuple->TupleDataLen; - vers_1->major = *p; p++; - vers_1->minor = *p; p++; - if (p >= q) - return -EINVAL; + vers_1->major = *p; p++; + vers_1->minor = *p; p++; + if (p >= q) + return -EINVAL; - return parse_strings(p, q, CISTPL_VERS_1_MAX_PROD_STRINGS, - vers_1->str, vers_1->ofs, &vers_1->ns); + return parse_strings(p, q, CISTPL_VERS_1_MAX_PROD_STRINGS, + vers_1->str, vers_1->ofs, &vers_1->ns); } -/*====================================================================*/ static int parse_altstr(tuple_t *tuple, cistpl_altstr_t *altstr) { - u_char *p, *q; + u_char *p, *q; - p = (u_char *)tuple->TupleData; - q = p + tuple->TupleDataLen; + p = (u_char *)tuple->TupleData; + q = p + tuple->TupleDataLen; - return parse_strings(p, q, CISTPL_MAX_ALTSTR_STRINGS, - altstr->str, altstr->ofs, &altstr->ns); + return parse_strings(p, q, CISTPL_MAX_ALTSTR_STRINGS, + altstr->str, altstr->ofs, &altstr->ns); } -/*====================================================================*/ static int parse_jedec(tuple_t *tuple, cistpl_jedec_t *jedec) { - u_char *p, *q; - int nid; + u_char *p, *q; + int nid; - p = (u_char *)tuple->TupleData; - q = p + tuple->TupleDataLen; + p = (u_char *)tuple->TupleData; + q = p + tuple->TupleDataLen; - for (nid = 0; nid < CISTPL_MAX_DEVICES; nid++) { - if (p > q-2) - break; - jedec->id[nid].mfr = p[0]; - jedec->id[nid].info = p[1]; - p += 2; - } - jedec->nid = nid; - return 0; + for (nid = 0; nid < CISTPL_MAX_DEVICES; nid++) { + if (p > q-2) + break; + jedec->id[nid].mfr = p[0]; + jedec->id[nid].info = p[1]; + p += 2; + } + jedec->nid = nid; + return 0; } -/*====================================================================*/ static int parse_manfid(tuple_t *tuple, cistpl_manfid_t *m) { - if (tuple->TupleDataLen < 4) - return -EINVAL; - m->manf = get_unaligned_le16(tuple->TupleData); - m->card = get_unaligned_le16(tuple->TupleData + 2); - return 0; + if (tuple->TupleDataLen < 4) + return -EINVAL; + m->manf = get_unaligned_le16(tuple->TupleData); + m->card = get_unaligned_le16(tuple->TupleData + 2); + return 0; } -/*====================================================================*/ static int parse_funcid(tuple_t *tuple, cistpl_funcid_t *f) { - u_char *p; - if (tuple->TupleDataLen < 2) - return -EINVAL; - p = (u_char *)tuple->TupleData; - f->func = p[0]; - f->sysinit = p[1]; - return 0; + u_char *p; + if (tuple->TupleDataLen < 2) + return -EINVAL; + p = (u_char *)tuple->TupleData; + f->func = p[0]; + f->sysinit = p[1]; + return 0; } -/*====================================================================*/ static int parse_funce(tuple_t *tuple, cistpl_funce_t *f) { - u_char *p; - int i; - if (tuple->TupleDataLen < 1) - return -EINVAL; - p = (u_char *)tuple->TupleData; - f->type = p[0]; - for (i = 1; i < tuple->TupleDataLen; i++) - f->data[i-1] = p[i]; - return 0; + u_char *p; + int i; + if (tuple->TupleDataLen < 1) + return -EINVAL; + p = (u_char *)tuple->TupleData; + f->type = p[0]; + for (i = 1; i < tuple->TupleDataLen; i++) + f->data[i-1] = p[i]; + return 0; } -/*====================================================================*/ static int parse_config(tuple_t *tuple, cistpl_config_t *config) { - int rasz, rmsz, i; - u_char *p; - - p = (u_char *)tuple->TupleData; - rasz = *p & 0x03; - rmsz = (*p & 0x3c) >> 2; - if (tuple->TupleDataLen < rasz+rmsz+4) - return -EINVAL; - config->last_idx = *(++p); - p++; - config->base = 0; - for (i = 0; i <= rasz; i++) - config->base += p[i] << (8*i); - p += rasz+1; - for (i = 0; i < 4; i++) - config->rmask[i] = 0; - for (i = 0; i <= rmsz; i++) - config->rmask[i>>2] += p[i] << (8*(i%4)); - config->subtuples = tuple->TupleDataLen - (rasz+rmsz+4); - return 0; + int rasz, rmsz, i; + u_char *p; + + p = (u_char *)tuple->TupleData; + rasz = *p & 0x03; + rmsz = (*p & 0x3c) >> 2; + if (tuple->TupleDataLen < rasz+rmsz+4) + return -EINVAL; + config->last_idx = *(++p); + p++; + config->base = 0; + for (i = 0; i <= rasz; i++) + config->base += p[i] << (8*i); + p += rasz+1; + for (i = 0; i < 4; i++) + config->rmask[i] = 0; + for (i = 0; i <= rmsz; i++) + config->rmask[i>>2] += p[i] << (8*(i%4)); + config->subtuples = tuple->TupleDataLen - (rasz+rmsz+4); + return 0; } -/*====================================================================== +/* The following routines are all used to parse the nightmarish + * config table entries. + */ + +static u_char *parse_power(u_char *p, u_char *q, cistpl_power_t *pwr) +{ + int i; + u_int scale; - The following routines are all used to parse the nightmarish - config table entries. + if (p == q) + return NULL; + pwr->present = *p; + pwr->flags = 0; + p++; + for (i = 0; i < 7; i++) + if (pwr->present & (1<<i)) { + if (p == q) + return NULL; + pwr->param[i] = POWER_CVT(*p); + scale = POWER_SCALE(*p); + while (*p & 0x80) { + if (++p == q) + return NULL; + if ((*p & 0x7f) < 100) + pwr->param[i] += + (*p & 0x7f) * scale / 100; + else if (*p == 0x7d) + pwr->flags |= CISTPL_POWER_HIGHZ_OK; + else if (*p == 0x7e) + pwr->param[i] = 0; + else if (*p == 0x7f) + pwr->flags |= CISTPL_POWER_HIGHZ_REQ; + else + return NULL; + } + p++; + } + return p; +} -======================================================================*/ -static u_char *parse_power(u_char *p, u_char *q, - cistpl_power_t *pwr) +static u_char *parse_timing(u_char *p, u_char *q, cistpl_timing_t *timing) { - int i; - u_int scale; - - if (p == q) - return NULL; - pwr->present = *p; - pwr->flags = 0; - p++; - for (i = 0; i < 7; i++) - if (pwr->present & (1<<i)) { - if (p == q) - return NULL; - pwr->param[i] = POWER_CVT(*p); - scale = POWER_SCALE(*p); - while (*p & 0x80) { + u_char scale; + + if (p == q) + return NULL; + scale = *p; + if ((scale & 3) != 3) { if (++p == q) return NULL; - if ((*p & 0x7f) < 100) - pwr->param[i] += (*p & 0x7f) * scale / 100; - else if (*p == 0x7d) - pwr->flags |= CISTPL_POWER_HIGHZ_OK; - else if (*p == 0x7e) - pwr->param[i] = 0; - else if (*p == 0x7f) - pwr->flags |= CISTPL_POWER_HIGHZ_REQ; - else - return NULL; - } - p++; - } - return p; + timing->wait = SPEED_CVT(*p); + timing->waitscale = exponent[scale & 3]; + } else + timing->wait = 0; + scale >>= 2; + if ((scale & 7) != 7) { + if (++p == q) + return NULL; + timing->ready = SPEED_CVT(*p); + timing->rdyscale = exponent[scale & 7]; + } else + timing->ready = 0; + scale >>= 3; + if (scale != 7) { + if (++p == q) + return NULL; + timing->reserved = SPEED_CVT(*p); + timing->rsvscale = exponent[scale]; + } else + timing->reserved = 0; + p++; + return p; } -/*====================================================================*/ -static u_char *parse_timing(u_char *p, u_char *q, - cistpl_timing_t *timing) +static u_char *parse_io(u_char *p, u_char *q, cistpl_io_t *io) { - u_char scale; + int i, j, bsz, lsz; - if (p == q) - return NULL; - scale = *p; - if ((scale & 3) != 3) { - if (++p == q) - return NULL; - timing->wait = SPEED_CVT(*p); - timing->waitscale = exponent[scale & 3]; - } else - timing->wait = 0; - scale >>= 2; - if ((scale & 7) != 7) { - if (++p == q) + if (p == q) return NULL; - timing->ready = SPEED_CVT(*p); - timing->rdyscale = exponent[scale & 7]; - } else - timing->ready = 0; - scale >>= 3; - if (scale != 7) { + io->flags = *p; + + if (!(*p & 0x80)) { + io->nwin = 1; + io->win[0].base = 0; + io->win[0].len = (1 << (io->flags & CISTPL_IO_LINES_MASK)); + return p+1; + } + if (++p == q) return NULL; - timing->reserved = SPEED_CVT(*p); - timing->rsvscale = exponent[scale]; - } else - timing->reserved = 0; - p++; - return p; -} - -/*====================================================================*/ + io->nwin = (*p & 0x0f) + 1; + bsz = (*p & 0x30) >> 4; + if (bsz == 3) + bsz++; + lsz = (*p & 0xc0) >> 6; + if (lsz == 3) + lsz++; + p++; -static u_char *parse_io(u_char *p, u_char *q, cistpl_io_t *io) -{ - int i, j, bsz, lsz; - - if (p == q) - return NULL; - io->flags = *p; - - if (!(*p & 0x80)) { - io->nwin = 1; - io->win[0].base = 0; - io->win[0].len = (1 << (io->flags & CISTPL_IO_LINES_MASK)); - return p+1; - } - - if (++p == q) - return NULL; - io->nwin = (*p & 0x0f) + 1; - bsz = (*p & 0x30) >> 4; - if (bsz == 3) - bsz++; - lsz = (*p & 0xc0) >> 6; - if (lsz == 3) - lsz++; - p++; - - for (i = 0; i < io->nwin; i++) { - io->win[i].base = 0; - io->win[i].len = 1; - for (j = 0; j < bsz; j++, p++) { - if (p == q) - return NULL; - io->win[i].base += *p << (j*8); - } - for (j = 0; j < lsz; j++, p++) { - if (p == q) - return NULL; - io->win[i].len += *p << (j*8); + for (i = 0; i < io->nwin; i++) { + io->win[i].base = 0; + io->win[i].len = 1; + for (j = 0; j < bsz; j++, p++) { + if (p == q) + return NULL; + io->win[i].base += *p << (j*8); + } + for (j = 0; j < lsz; j++, p++) { + if (p == q) + return NULL; + io->win[i].len += *p << (j*8); + } } - } - return p; + return p; } -/*====================================================================*/ static u_char *parse_mem(u_char *p, u_char *q, cistpl_mem_t *mem) { - int i, j, asz, lsz, has_ha; - u_int len, ca, ha; - - if (p == q) - return NULL; - - mem->nwin = (*p & 0x07) + 1; - lsz = (*p & 0x18) >> 3; - asz = (*p & 0x60) >> 5; - has_ha = (*p & 0x80); - if (++p == q) - return NULL; - - for (i = 0; i < mem->nwin; i++) { - len = ca = ha = 0; - for (j = 0; j < lsz; j++, p++) { - if (p == q) - return NULL; - len += *p << (j*8); - } - for (j = 0; j < asz; j++, p++) { - if (p == q) - return NULL; - ca += *p << (j*8); + int i, j, asz, lsz, has_ha; + u_int len, ca, ha; + + if (p == q) + return NULL; + + mem->nwin = (*p & 0x07) + 1; + lsz = (*p & 0x18) >> 3; + asz = (*p & 0x60) >> 5; + has_ha = (*p & 0x80); + if (++p == q) + return NULL; + + for (i = 0; i < mem->nwin; i++) { + len = ca = ha = 0; + for (j = 0; j < lsz; j++, p++) { + if (p == q) + return NULL; + len += *p << (j*8); + } + for (j = 0; j < asz; j++, p++) { + if (p == q) + return NULL; + ca += *p << (j*8); + } + if (has_ha) + for (j = 0; j < asz; j++, p++) { + if (p == q) + return NULL; + ha += *p << (j*8); + } + mem->win[i].len = len << 8; + mem->win[i].card_addr = ca << 8; + mem->win[i].host_addr = ha << 8; } - if (has_ha) - for (j = 0; j < asz; j++, p++) { - if (p == q) - return NULL; - ha += *p << (j*8); - } - mem->win[i].len = len << 8; - mem->win[i].card_addr = ca << 8; - mem->win[i].host_addr = ha << 8; - } - return p; + return p; } -/*====================================================================*/ static u_char *parse_irq(u_char *p, u_char *q, cistpl_irq_t *irq) { - if (p == q) - return NULL; - irq->IRQInfo1 = *p; p++; - if (irq->IRQInfo1 & IRQ_INFO2_VALID) { - if (p+2 > q) + if (p == q) return NULL; - irq->IRQInfo2 = (p[1]<<8) + p[0]; - p += 2; - } - return p; + irq->IRQInfo1 = *p; p++; + if (irq->IRQInfo1 & IRQ_INFO2_VALID) { + if (p+2 > q) + return NULL; + irq->IRQInfo2 = (p[1]<<8) + p[0]; + p += 2; + } + return p; } -/*====================================================================*/ static int parse_cftable_entry(tuple_t *tuple, cistpl_cftable_entry_t *entry) { - u_char *p, *q, features; - - p = tuple->TupleData; - q = p + tuple->TupleDataLen; - entry->index = *p & 0x3f; - entry->flags = 0; - if (*p & 0x40) - entry->flags |= CISTPL_CFTABLE_DEFAULT; - if (*p & 0x80) { - if (++p == q) - return -EINVAL; - if (*p & 0x10) - entry->flags |= CISTPL_CFTABLE_BVDS; - if (*p & 0x20) - entry->flags |= CISTPL_CFTABLE_WP; - if (*p & 0x40) - entry->flags |= CISTPL_CFTABLE_RDYBSY; - if (*p & 0x80) - entry->flags |= CISTPL_CFTABLE_MWAIT; - entry->interface = *p & 0x0f; - } else - entry->interface = 0; - - /* Process optional features */ - if (++p == q) - return -EINVAL; - features = *p; p++; - - /* Power options */ - if ((features & 3) > 0) { - p = parse_power(p, q, &entry->vcc); - if (p == NULL) - return -EINVAL; - } else - entry->vcc.present = 0; - if ((features & 3) > 1) { - p = parse_power(p, q, &entry->vpp1); - if (p == NULL) - return -EINVAL; - } else - entry->vpp1.present = 0; - if ((features & 3) > 2) { - p = parse_power(p, q, &entry->vpp2); - if (p == NULL) - return -EINVAL; - } else - entry->vpp2.present = 0; + u_char *p, *q, features; - /* Timing options */ - if (features & 0x04) { - p = parse_timing(p, q, &entry->timing); - if (p == NULL) - return -EINVAL; - } else { - entry->timing.wait = 0; - entry->timing.ready = 0; - entry->timing.reserved = 0; - } - - /* I/O window options */ - if (features & 0x08) { - p = parse_io(p, q, &entry->io); - if (p == NULL) - return -EINVAL; - } else - entry->io.nwin = 0; - - /* Interrupt options */ - if (features & 0x10) { - p = parse_irq(p, q, &entry->irq); - if (p == NULL) - return -EINVAL; - } else - entry->irq.IRQInfo1 = 0; - - switch (features & 0x60) { - case 0x00: - entry->mem.nwin = 0; - break; - case 0x20: - entry->mem.nwin = 1; - entry->mem.win[0].len = get_unaligned_le16(p) << 8; - entry->mem.win[0].card_addr = 0; - entry->mem.win[0].host_addr = 0; - p += 2; - if (p > q) - return -EINVAL; - break; - case 0x40: - entry->mem.nwin = 1; - entry->mem.win[0].len = get_unaligned_le16(p) << 8; - entry->mem.win[0].card_addr = get_unaligned_le16(p + 2) << 8; - entry->mem.win[0].host_addr = 0; - p += 4; - if (p > q) - return -EINVAL; - break; - case 0x60: - p = parse_mem(p, q, &entry->mem); - if (p == NULL) - return -EINVAL; - break; - } + p = tuple->TupleData; + q = p + tuple->TupleDataLen; + entry->index = *p & 0x3f; + entry->flags = 0; + if (*p & 0x40) + entry->flags |= CISTPL_CFTABLE_DEFAULT; + if (*p & 0x80) { + if (++p == q) + return -EINVAL; + if (*p & 0x10) + entry->flags |= CISTPL_CFTABLE_BVDS; + if (*p & 0x20) + entry->flags |= CISTPL_CFTABLE_WP; + if (*p & 0x40) + entry->flags |= CISTPL_CFTABLE_RDYBSY; + if (*p & 0x80) + entry->flags |= CISTPL_CFTABLE_MWAIT; + entry->interface = *p & 0x0f; + } else + entry->interface = 0; - /* Misc features */ - if (features & 0x80) { - if (p == q) + /* Process optional features */ + if (++p == q) return -EINVAL; - entry->flags |= (*p << 8); - while (*p & 0x80) - if (++p == q) - return -EINVAL; - p++; - } - - entry->subtuples = q-p; - - return 0; -} - -/*====================================================================*/ - -#ifdef CONFIG_CARDBUS + features = *p; p++; -static int parse_bar(tuple_t *tuple, cistpl_bar_t *bar) -{ - u_char *p; - if (tuple->TupleDataLen < 6) - return -EINVAL; - p = (u_char *)tuple->TupleData; - bar->attr = *p; - p += 2; - bar->size = get_unaligned_le32(p); - return 0; -} + /* Power options */ + if ((features & 3) > 0) { + p = parse_power(p, q, &entry->vcc); + if (p == NULL) + return -EINVAL; + } else + entry->vcc.present = 0; + if ((features & 3) > 1) { + p = parse_power(p, q, &entry->vpp1); + if (p == NULL) + return -EINVAL; + } else + entry->vpp1.present = 0; + if ((features & 3) > 2) { + p = parse_power(p, q, &entry->vpp2); + if (p == NULL) + return -EINVAL; + } else + entry->vpp2.present = 0; -static int parse_config_cb(tuple_t *tuple, cistpl_config_t *config) -{ - u_char *p; - - p = (u_char *)tuple->TupleData; - if ((*p != 3) || (tuple->TupleDataLen < 6)) - return -EINVAL; - config->last_idx = *(++p); - p++; - config->base = get_unaligned_le32(p); - config->subtuples = tuple->TupleDataLen - 6; - return 0; -} + /* Timing options */ + if (features & 0x04) { + p = parse_timing(p, q, &entry->timing); + if (p == NULL) + return -EINVAL; + } else { + entry->timing.wait = 0; + entry->timing.ready = 0; + entry->timing.reserved = 0; + } -static int parse_cftable_entry_cb(tuple_t *tuple, - cistpl_cftable_entry_cb_t *entry) -{ - u_char *p, *q, features; - - p = tuple->TupleData; - q = p + tuple->TupleDataLen; - entry->index = *p & 0x3f; - entry->flags = 0; - if (*p & 0x40) - entry->flags |= CISTPL_CFTABLE_DEFAULT; - - /* Process optional features */ - if (++p == q) - return -EINVAL; - features = *p; p++; - - /* Power options */ - if ((features & 3) > 0) { - p = parse_power(p, q, &entry->vcc); - if (p == NULL) - return -EINVAL; - } else - entry->vcc.present = 0; - if ((features & 3) > 1) { - p = parse_power(p, q, &entry->vpp1); - if (p == NULL) - return -EINVAL; - } else - entry->vpp1.present = 0; - if ((features & 3) > 2) { - p = parse_power(p, q, &entry->vpp2); - if (p == NULL) - return -EINVAL; - } else - entry->vpp2.present = 0; + /* I/O window options */ + if (features & 0x08) { + p = parse_io(p, q, &entry->io); + if (p == NULL) + return -EINVAL; + } else + entry->io.nwin = 0; - /* I/O window options */ - if (features & 0x08) { - if (p == q) - return -EINVAL; - entry->io = *p; p++; - } else - entry->io = 0; - - /* Interrupt options */ - if (features & 0x10) { - p = parse_irq(p, q, &entry->irq); - if (p == NULL) - return -EINVAL; - } else - entry->irq.IRQInfo1 = 0; + /* Interrupt options */ + if (features & 0x10) { + p = parse_irq(p, q, &entry->irq); + if (p == NULL) + return -EINVAL; + } else + entry->irq.IRQInfo1 = 0; - if (features & 0x20) { - if (p == q) - return -EINVAL; - entry->mem = *p; p++; - } else - entry->mem = 0; + switch (features & 0x60) { + case 0x00: + entry->mem.nwin = 0; + break; + case 0x20: + entry->mem.nwin = 1; + entry->mem.win[0].len = get_unaligned_le16(p) << 8; + entry->mem.win[0].card_addr = 0; + entry->mem.win[0].host_addr = 0; + p += 2; + if (p > q) + return -EINVAL; + break; + case 0x40: + entry->mem.nwin = 1; + entry->mem.win[0].len = get_unaligned_le16(p) << 8; + entry->mem.win[0].card_addr = get_unaligned_le16(p + 2) << 8; + entry->mem.win[0].host_addr = 0; + p += 4; + if (p > q) + return -EINVAL; + break; + case 0x60: + p = parse_mem(p, q, &entry->mem); + if (p == NULL) + return -EINVAL; + break; + } - /* Misc features */ - if (features & 0x80) { - if (p == q) - return -EINVAL; - entry->flags |= (*p << 8); - if (*p & 0x80) { - if (++p == q) - return -EINVAL; - entry->flags |= (*p << 16); + /* Misc features */ + if (features & 0x80) { + if (p == q) + return -EINVAL; + entry->flags |= (*p << 8); + while (*p & 0x80) + if (++p == q) + return -EINVAL; + p++; } - while (*p & 0x80) - if (++p == q) - return -EINVAL; - p++; - } - entry->subtuples = q-p; + entry->subtuples = q-p; - return 0; + return 0; } -#endif - -/*====================================================================*/ static int parse_device_geo(tuple_t *tuple, cistpl_device_geo_t *geo) { - u_char *p, *q; - int n; + u_char *p, *q; + int n; - p = (u_char *)tuple->TupleData; - q = p + tuple->TupleDataLen; + p = (u_char *)tuple->TupleData; + q = p + tuple->TupleDataLen; - for (n = 0; n < CISTPL_MAX_DEVICES; n++) { - if (p > q-6) - break; - geo->geo[n].buswidth = p[0]; - geo->geo[n].erase_block = 1 << (p[1]-1); - geo->geo[n].read_block = 1 << (p[2]-1); - geo->geo[n].write_block = 1 << (p[3]-1); - geo->geo[n].partition = 1 << (p[4]-1); - geo->geo[n].interleave = 1 << (p[5]-1); - p += 6; - } - geo->ngeo = n; - return 0; + for (n = 0; n < CISTPL_MAX_DEVICES; n++) { + if (p > q-6) + break; + geo->geo[n].buswidth = p[0]; + geo->geo[n].erase_block = 1 << (p[1]-1); + geo->geo[n].read_block = 1 << (p[2]-1); + geo->geo[n].write_block = 1 << (p[3]-1); + geo->geo[n].partition = 1 << (p[4]-1); + geo->geo[n].interleave = 1 << (p[5]-1); + p += 6; + } + geo->ngeo = n; + return 0; } -/*====================================================================*/ static int parse_vers_2(tuple_t *tuple, cistpl_vers_2_t *v2) { - u_char *p, *q; - - if (tuple->TupleDataLen < 10) - return -EINVAL; - - p = tuple->TupleData; - q = p + tuple->TupleDataLen; - - v2->vers = p[0]; - v2->comply = p[1]; - v2->dindex = get_unaligned_le16(p + 2); - v2->vspec8 = p[6]; - v2->vspec9 = p[7]; - v2->nhdr = p[8]; - p += 9; - return parse_strings(p, q, 2, v2->str, &v2->vendor, NULL); + u_char *p, *q; + + if (tuple->TupleDataLen < 10) + return -EINVAL; + + p = tuple->TupleData; + q = p + tuple->TupleDataLen; + + v2->vers = p[0]; + v2->comply = p[1]; + v2->dindex = get_unaligned_le16(p + 2); + v2->vspec8 = p[6]; + v2->vspec9 = p[7]; + v2->nhdr = p[8]; + p += 9; + return parse_strings(p, q, 2, v2->str, &v2->vendor, NULL); } -/*====================================================================*/ static int parse_org(tuple_t *tuple, cistpl_org_t *org) { - u_char *p, *q; - int i; - - p = tuple->TupleData; - q = p + tuple->TupleDataLen; - if (p == q) - return -EINVAL; - org->data_org = *p; - if (++p == q) - return -EINVAL; - for (i = 0; i < 30; i++) { - org->desc[i] = *p; - if (*p == '\0') - break; + u_char *p, *q; + int i; + + p = tuple->TupleData; + q = p + tuple->TupleDataLen; + if (p == q) + return -EINVAL; + org->data_org = *p; if (++p == q) return -EINVAL; - } - return 0; + for (i = 0; i < 30; i++) { + org->desc[i] = *p; + if (*p == '\0') + break; + if (++p == q) + return -EINVAL; + } + return 0; } -/*====================================================================*/ static int parse_format(tuple_t *tuple, cistpl_format_t *fmt) { - u_char *p; + u_char *p; - if (tuple->TupleDataLen < 10) - return -EINVAL; + if (tuple->TupleDataLen < 10) + return -EINVAL; - p = tuple->TupleData; + p = tuple->TupleData; - fmt->type = p[0]; - fmt->edc = p[1]; - fmt->offset = get_unaligned_le32(p + 2); - fmt->length = get_unaligned_le32(p + 6); + fmt->type = p[0]; + fmt->edc = p[1]; + fmt->offset = get_unaligned_le32(p + 2); + fmt->length = get_unaligned_le32(p + 6); - return 0; + return 0; } -/*====================================================================*/ int pcmcia_parse_tuple(tuple_t *tuple, cisparse_t *parse) { - int ret = 0; - - if (tuple->TupleDataLen > tuple->TupleDataMax) - return -EINVAL; - switch (tuple->TupleCode) { - case CISTPL_DEVICE: - case CISTPL_DEVICE_A: - ret = parse_device(tuple, &parse->device); - break; -#ifdef CONFIG_CARDBUS - case CISTPL_BAR: - ret = parse_bar(tuple, &parse->bar); - break; - case CISTPL_CONFIG_CB: - ret = parse_config_cb(tuple, &parse->config); - break; - case CISTPL_CFTABLE_ENTRY_CB: - ret = parse_cftable_entry_cb(tuple, &parse->cftable_entry_cb); - break; -#endif - case CISTPL_CHECKSUM: - ret = parse_checksum(tuple, &parse->checksum); - break; - case CISTPL_LONGLINK_A: - case CISTPL_LONGLINK_C: - ret = parse_longlink(tuple, &parse->longlink); - break; - case CISTPL_LONGLINK_MFC: - ret = parse_longlink_mfc(tuple, &parse->longlink_mfc); - break; - case CISTPL_VERS_1: - ret = parse_vers_1(tuple, &parse->version_1); - break; - case CISTPL_ALTSTR: - ret = parse_altstr(tuple, &parse->altstr); - break; - case CISTPL_JEDEC_A: - case CISTPL_JEDEC_C: - ret = parse_jedec(tuple, &parse->jedec); - break; - case CISTPL_MANFID: - ret = parse_manfid(tuple, &parse->manfid); - break; - case CISTPL_FUNCID: - ret = parse_funcid(tuple, &parse->funcid); - break; - case CISTPL_FUNCE: - ret = parse_funce(tuple, &parse->funce); - break; - case CISTPL_CONFIG: - ret = parse_config(tuple, &parse->config); - break; - case CISTPL_CFTABLE_ENTRY: - ret = parse_cftable_entry(tuple, &parse->cftable_entry); - break; - case CISTPL_DEVICE_GEO: - case CISTPL_DEVICE_GEO_A: - ret = parse_device_geo(tuple, &parse->device_geo); - break; - case CISTPL_VERS_2: - ret = parse_vers_2(tuple, &parse->vers_2); - break; - case CISTPL_ORG: - ret = parse_org(tuple, &parse->org); - break; - case CISTPL_FORMAT: - case CISTPL_FORMAT_A: - ret = parse_format(tuple, &parse->format); - break; - case CISTPL_NO_LINK: - case CISTPL_LINKTARGET: - ret = 0; - break; - default: - ret = -EINVAL; - break; - } - if (ret) - pr_debug("parse_tuple failed %d\n", ret); - return ret; + int ret = 0; + + if (tuple->TupleDataLen > tuple->TupleDataMax) + return -EINVAL; + switch (tuple->TupleCode) { + case CISTPL_DEVICE: + case CISTPL_DEVICE_A: + ret = parse_device(tuple, &parse->device); + break; + case CISTPL_CHECKSUM: + ret = parse_checksum(tuple, &parse->checksum); + break; + case CISTPL_LONGLINK_A: + case CISTPL_LONGLINK_C: + ret = parse_longlink(tuple, &parse->longlink); + break; + case CISTPL_LONGLINK_MFC: + ret = parse_longlink_mfc(tuple, &parse->longlink_mfc); + break; + case CISTPL_VERS_1: + ret = parse_vers_1(tuple, &parse->version_1); + break; + case CISTPL_ALTSTR: + ret = parse_altstr(tuple, &parse->altstr); + break; + case CISTPL_JEDEC_A: + case CISTPL_JEDEC_C: + ret = parse_jedec(tuple, &parse->jedec); + break; + case CISTPL_MANFID: + ret = parse_manfid(tuple, &parse->manfid); + break; + case CISTPL_FUNCID: + ret = parse_funcid(tuple, &parse->funcid); + break; + case CISTPL_FUNCE: + ret = parse_funce(tuple, &parse->funce); + break; + case CISTPL_CONFIG: + ret = parse_config(tuple, &parse->config); + break; + case CISTPL_CFTABLE_ENTRY: + ret = parse_cftable_entry(tuple, &parse->cftable_entry); + break; + case CISTPL_DEVICE_GEO: + case CISTPL_DEVICE_GEO_A: + ret = parse_device_geo(tuple, &parse->device_geo); + break; + case CISTPL_VERS_2: + ret = parse_vers_2(tuple, &parse->vers_2); + break; + case CISTPL_ORG: + ret = parse_org(tuple, &parse->org); + break; + case CISTPL_FORMAT: + case CISTPL_FORMAT_A: + ret = parse_format(tuple, &parse->format); + break; + case CISTPL_NO_LINK: + case CISTPL_LINKTARGET: + ret = 0; + break; + default: + ret = -EINVAL; + break; + } + if (ret) + pr_debug("parse_tuple failed %d\n", ret); + return ret; } EXPORT_SYMBOL(pcmcia_parse_tuple); -/*====================================================================== - This is used internally by Card Services to look up CIS stuff. +/** + * pccard_validate_cis() - check whether card has a sensible CIS + * @s: the struct pcmcia_socket we are to check + * @info: returns the number of tuples in the (valid) CIS, or 0 + * + * This tries to determine if a card has a sensible CIS. In @info, it + * returns the number of tuples in the CIS, or 0 if the CIS looks bad. The + * checks include making sure several critical tuples are present and + * valid; seeing if the total number of tuples is reasonable; and + * looking for tuples that use reserved codes. + * + * The function returns 0 on success. + */ +int pccard_validate_cis(struct pcmcia_socket *s, unsigned int *info) +{ + tuple_t *tuple; + cisparse_t *p; + unsigned int count = 0; + int ret, reserved, dev_ok = 0, ident_ok = 0; -======================================================================*/ + if (!s) + return -EINVAL; + + if (s->functions) { + WARN_ON(1); + return -EINVAL; + } + + /* We do not want to validate the CIS cache... */ + mutex_lock(&s->ops_mutex); + destroy_cis_cache(s); + mutex_unlock(&s->ops_mutex); + + tuple = kmalloc(sizeof(*tuple), GFP_KERNEL); + if (tuple == NULL) { + dev_warn(&s->dev, "no memory to validate CIS\n"); + return -ENOMEM; + } + p = kmalloc(sizeof(*p), GFP_KERNEL); + if (p == NULL) { + kfree(tuple); + dev_warn(&s->dev, "no memory to validate CIS\n"); + return -ENOMEM; + } + + count = reserved = 0; + tuple->DesiredTuple = RETURN_FIRST_TUPLE; + tuple->Attributes = TUPLE_RETURN_COMMON; + ret = pccard_get_first_tuple(s, BIND_FN_ALL, tuple); + if (ret != 0) + goto done; + + /* First tuple should be DEVICE; we should really have either that + or a CFTABLE_ENTRY of some sort */ + if ((tuple->TupleCode == CISTPL_DEVICE) || + (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_CFTABLE_ENTRY, p)) || + (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_CFTABLE_ENTRY_CB, p))) + dev_ok++; + + /* All cards should have a MANFID tuple, and/or a VERS_1 or VERS_2 + tuple, for card identification. Certain old D-Link and Linksys + cards have only a broken VERS_2 tuple; hence the bogus test. */ + if ((pccard_read_tuple(s, BIND_FN_ALL, CISTPL_MANFID, p) == 0) || + (pccard_read_tuple(s, BIND_FN_ALL, CISTPL_VERS_1, p) == 0) || + (pccard_read_tuple(s, BIND_FN_ALL, CISTPL_VERS_2, p) != -ENOSPC)) + ident_ok++; + + if (!dev_ok && !ident_ok) + goto done; + + for (count = 1; count < MAX_TUPLES; count++) { + ret = pccard_get_next_tuple(s, BIND_FN_ALL, tuple); + if (ret != 0) + break; + if (((tuple->TupleCode > 0x23) && (tuple->TupleCode < 0x40)) || + ((tuple->TupleCode > 0x47) && (tuple->TupleCode < 0x80)) || + ((tuple->TupleCode > 0x90) && (tuple->TupleCode < 0xff))) + reserved++; + } + if ((count == MAX_TUPLES) || (reserved > 5) || + ((!dev_ok || !ident_ok) && (count > 10))) + count = 0; + + ret = 0; -int pccard_read_tuple(struct pcmcia_socket *s, unsigned int function, cisdata_t code, void *parse) -{ - tuple_t tuple; - cisdata_t *buf; - int ret; - - buf = kmalloc(256, GFP_KERNEL); - if (buf == NULL) { - dev_printk(KERN_WARNING, &s->dev, "no memory to read tuple\n"); - return -ENOMEM; - } - tuple.DesiredTuple = code; - tuple.Attributes = 0; - if (function == BIND_FN_ALL) - tuple.Attributes = TUPLE_RETURN_COMMON; - ret = pccard_get_first_tuple(s, function, &tuple); - if (ret != 0) - goto done; - tuple.TupleData = buf; - tuple.TupleOffset = 0; - tuple.TupleDataMax = 255; - ret = pccard_get_tuple_data(s, &tuple); - if (ret != 0) - goto done; - ret = pcmcia_parse_tuple(&tuple, parse); done: - kfree(buf); - return ret; + /* invalidate CIS cache on failure */ + if (!dev_ok || !ident_ok || !count) { + mutex_lock(&s->ops_mutex); + destroy_cis_cache(s); + mutex_unlock(&s->ops_mutex); + ret = -EIO; + } + + if (info) + *info = count; + kfree(tuple); + kfree(p); + return ret; } -EXPORT_SYMBOL(pccard_read_tuple); -/** - * pccard_loop_tuple() - loop over tuples in the CIS - * @s: the struct pcmcia_socket where the card is inserted - * @function: the device function we loop for - * @code: which CIS code shall we look for? - * @parse: buffer where the tuple shall be parsed (or NULL, if no parse) - * @priv_data: private data to be passed to the loop_tuple function. - * @loop_tuple: function to call for each CIS entry of type @function. IT - * gets passed the raw tuple, the paresed tuple (if @parse is - * set) and @priv_data. - * - * pccard_loop_tuple() loops over all CIS entries of type @function, and - * calls the @loop_tuple function for each entry. If the call to @loop_tuple - * returns 0, the loop exits. Returns 0 on success or errorcode otherwise. - */ -int pccard_loop_tuple(struct pcmcia_socket *s, unsigned int function, - cisdata_t code, cisparse_t *parse, void *priv_data, - int (*loop_tuple) (tuple_t *tuple, - cisparse_t *parse, - void *priv_data)) +#define to_socket(_dev) container_of(_dev, struct pcmcia_socket, dev) + +static ssize_t pccard_extract_cis(struct pcmcia_socket *s, char *buf, + loff_t off, size_t count) { tuple_t tuple; - cisdata_t *buf; - int ret; - - buf = kzalloc(256, GFP_KERNEL); - if (buf == NULL) { - dev_printk(KERN_WARNING, &s->dev, "no memory to read tuple\n"); + int status, i; + loff_t pointer = 0; + ssize_t ret = 0; + u_char *tuplebuffer; + u_char *tempbuffer; + + tuplebuffer = kmalloc(sizeof(u_char) * 256, GFP_KERNEL); + if (!tuplebuffer) return -ENOMEM; + + tempbuffer = kmalloc(sizeof(u_char) * 258, GFP_KERNEL); + if (!tempbuffer) { + ret = -ENOMEM; + goto free_tuple; } - tuple.TupleData = buf; - tuple.TupleDataMax = 255; + memset(&tuple, 0, sizeof(tuple_t)); + + tuple.Attributes = TUPLE_RETURN_LINK | TUPLE_RETURN_COMMON; + tuple.DesiredTuple = RETURN_FIRST_TUPLE; tuple.TupleOffset = 0; - tuple.DesiredTuple = code; - tuple.Attributes = 0; - ret = pccard_get_first_tuple(s, function, &tuple); - while (!ret) { - if (pccard_get_tuple_data(s, &tuple)) - goto next_entry; + status = pccard_get_first_tuple(s, BIND_FN_ALL, &tuple); + while (!status) { + tuple.TupleData = tuplebuffer; + tuple.TupleDataMax = 255; + memset(tuplebuffer, 0, sizeof(u_char) * 255); + + status = pccard_get_tuple_data(s, &tuple); + if (status) + break; + + if (off < (pointer + 2 + tuple.TupleDataLen)) { + tempbuffer[0] = tuple.TupleCode & 0xff; + tempbuffer[1] = tuple.TupleLink & 0xff; + for (i = 0; i < tuple.TupleDataLen; i++) + tempbuffer[i + 2] = tuplebuffer[i] & 0xff; + + for (i = 0; i < (2 + tuple.TupleDataLen); i++) { + if (((i + pointer) >= off) && + (i + pointer) < (off + count)) { + buf[ret] = tempbuffer[i]; + ret++; + } + } + } - if (parse) - if (pcmcia_parse_tuple(&tuple, parse)) - goto next_entry; + pointer += 2 + tuple.TupleDataLen; - ret = loop_tuple(&tuple, parse, priv_data); - if (!ret) + if (pointer >= (off + count)) break; -next_entry: - ret = pccard_get_next_tuple(s, function, &tuple); + if (tuple.TupleCode == CISTPL_END) + break; + status = pccard_get_next_tuple(s, BIND_FN_ALL, &tuple); } - kfree(buf); + kfree(tempbuffer); + free_tuple: + kfree(tuplebuffer); + return ret; } -EXPORT_SYMBOL(pccard_loop_tuple); -/*====================================================================== +static ssize_t pccard_show_cis(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + unsigned int size = 0x200; - This tries to determine if a card has a sensible CIS. It returns - the number of tuples in the CIS, or 0 if the CIS looks bad. The - checks include making sure several critical tuples are present and - valid; seeing if the total number of tuples is reasonable; and - looking for tuples that use reserved codes. + if (off >= size) + count = 0; + else { + struct pcmcia_socket *s; + unsigned int chains = 1; -======================================================================*/ + if (off + count > size) + count = size - off; -int pccard_validate_cis(struct pcmcia_socket *s, unsigned int *info) + s = to_socket(container_of(kobj, struct device, kobj)); + + if (!(s->state & SOCKET_PRESENT)) + return -ENODEV; + if (!s->functions && pccard_validate_cis(s, &chains)) + return -EIO; + if (!chains) + return -ENODATA; + + count = pccard_extract_cis(s, buf, off, count); + } + + return count; +} + + +static ssize_t pccard_store_cis(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { - tuple_t *tuple; - cisparse_t *p; - unsigned int count = 0; - int ret, reserved, dev_ok = 0, ident_ok = 0; - - if (!s) - return -EINVAL; - - tuple = kmalloc(sizeof(*tuple), GFP_KERNEL); - if (tuple == NULL) { - dev_printk(KERN_WARNING, &s->dev, "no memory to validate CIS\n"); - return -ENOMEM; - } - p = kmalloc(sizeof(*p), GFP_KERNEL); - if (p == NULL) { - kfree(tuple); - dev_printk(KERN_WARNING, &s->dev, "no memory to validate CIS\n"); - return -ENOMEM; - } - - count = reserved = 0; - tuple->DesiredTuple = RETURN_FIRST_TUPLE; - tuple->Attributes = TUPLE_RETURN_COMMON; - ret = pccard_get_first_tuple(s, BIND_FN_ALL, tuple); - if (ret != 0) - goto done; - - /* First tuple should be DEVICE; we should really have either that - or a CFTABLE_ENTRY of some sort */ - if ((tuple->TupleCode == CISTPL_DEVICE) || - (pccard_read_tuple(s, BIND_FN_ALL, CISTPL_CFTABLE_ENTRY, p) == 0) || - (pccard_read_tuple(s, BIND_FN_ALL, CISTPL_CFTABLE_ENTRY_CB, p) == 0)) - dev_ok++; - - /* All cards should have a MANFID tuple, and/or a VERS_1 or VERS_2 - tuple, for card identification. Certain old D-Link and Linksys - cards have only a broken VERS_2 tuple; hence the bogus test. */ - if ((pccard_read_tuple(s, BIND_FN_ALL, CISTPL_MANFID, p) == 0) || - (pccard_read_tuple(s, BIND_FN_ALL, CISTPL_VERS_1, p) == 0) || - (pccard_read_tuple(s, BIND_FN_ALL, CISTPL_VERS_2, p) != -ENOSPC)) - ident_ok++; - - if (!dev_ok && !ident_ok) - goto done; - - for (count = 1; count < MAX_TUPLES; count++) { - ret = pccard_get_next_tuple(s, BIND_FN_ALL, tuple); - if (ret != 0) - break; - if (((tuple->TupleCode > 0x23) && (tuple->TupleCode < 0x40)) || - ((tuple->TupleCode > 0x47) && (tuple->TupleCode < 0x80)) || - ((tuple->TupleCode > 0x90) && (tuple->TupleCode < 0xff))) - reserved++; - } - if ((count == MAX_TUPLES) || (reserved > 5) || - ((!dev_ok || !ident_ok) && (count > 10))) - count = 0; + struct pcmcia_socket *s; + int error; -done: - if (info) - *info = count; - kfree(tuple); - kfree(p); - return 0; + s = to_socket(container_of(kobj, struct device, kobj)); + + if (off) + return -EINVAL; + + if (count >= CISTPL_MAX_CIS_SIZE) + return -EINVAL; + + if (!(s->state & SOCKET_PRESENT)) + return -ENODEV; + + error = pcmcia_replace_cis(s, buf, count); + if (error) + return -EIO; + + pcmcia_parse_uevents(s, PCMCIA_UEVENT_REQUERY); + + return count; } -EXPORT_SYMBOL(pccard_validate_cis); + + +struct bin_attribute pccard_cis_attr = { + .attr = { .name = "cis", .mode = S_IRUGO | S_IWUSR }, + .size = 0x200, + .read = pccard_show_cis, + .write = pccard_store_cis, +}; diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 6d6f82b38a68..976d80706eae 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -76,83 +76,18 @@ DECLARE_RWSEM(pcmcia_socket_list_rwsem); EXPORT_SYMBOL(pcmcia_socket_list_rwsem); -/* - * Low-level PCMCIA socket drivers need to register with the PCCard - * core using pcmcia_register_socket. - * - * socket drivers are expected to use the following callbacks in their - * .drv struct: - * - pcmcia_socket_dev_suspend - * - pcmcia_socket_dev_resume - * These functions check for the appropriate struct pcmcia_soket arrays, - * and pass them to the low-level functions pcmcia_{suspend,resume}_socket - */ -static int socket_early_resume(struct pcmcia_socket *skt); -static int socket_late_resume(struct pcmcia_socket *skt); -static int socket_resume(struct pcmcia_socket *skt); -static int socket_suspend(struct pcmcia_socket *skt); - -static void pcmcia_socket_dev_run(struct device *dev, - int (*cb)(struct pcmcia_socket *)) -{ - struct pcmcia_socket *socket; - - down_read(&pcmcia_socket_list_rwsem); - list_for_each_entry(socket, &pcmcia_socket_list, socket_list) { - if (socket->dev.parent != dev) - continue; - mutex_lock(&socket->skt_mutex); - cb(socket); - mutex_unlock(&socket->skt_mutex); - } - up_read(&pcmcia_socket_list_rwsem); -} - -int pcmcia_socket_dev_suspend(struct device *dev) -{ - pcmcia_socket_dev_run(dev, socket_suspend); - return 0; -} -EXPORT_SYMBOL(pcmcia_socket_dev_suspend); - -void pcmcia_socket_dev_early_resume(struct device *dev) -{ - pcmcia_socket_dev_run(dev, socket_early_resume); -} -EXPORT_SYMBOL(pcmcia_socket_dev_early_resume); - -void pcmcia_socket_dev_late_resume(struct device *dev) -{ - pcmcia_socket_dev_run(dev, socket_late_resume); -} -EXPORT_SYMBOL(pcmcia_socket_dev_late_resume); - -int pcmcia_socket_dev_resume(struct device *dev) -{ - pcmcia_socket_dev_run(dev, socket_resume); - return 0; -} -EXPORT_SYMBOL(pcmcia_socket_dev_resume); - - struct pcmcia_socket *pcmcia_get_socket(struct pcmcia_socket *skt) { struct device *dev = get_device(&skt->dev); if (!dev) return NULL; - skt = dev_get_drvdata(dev); - if (!try_module_get(skt->owner)) { - put_device(&skt->dev); - return NULL; - } - return skt; + return dev_get_drvdata(dev); } EXPORT_SYMBOL(pcmcia_get_socket); void pcmcia_put_socket(struct pcmcia_socket *skt) { - module_put(skt->owner); put_device(&skt->dev); } EXPORT_SYMBOL(pcmcia_put_socket); @@ -181,8 +116,6 @@ int pcmcia_register_socket(struct pcmcia_socket *socket) dev_dbg(&socket->dev, "pcmcia_register_socket(0x%p)\n", socket->ops); - spin_lock_init(&socket->lock); - /* try to obtain a socket number [yes, it gets ugly if we * register more than 2^sizeof(unsigned int) pcmcia * sockets... but the socket number is deprecated @@ -228,10 +161,13 @@ int pcmcia_register_socket(struct pcmcia_socket *socket) init_completion(&socket->socket_released); init_completion(&socket->thread_done); mutex_init(&socket->skt_mutex); + mutex_init(&socket->ops_mutex); spin_lock_init(&socket->thread_lock); if (socket->resource_ops->init) { + mutex_lock(&socket->ops_mutex); ret = socket->resource_ops->init(socket); + mutex_unlock(&socket->ops_mutex); if (ret) goto err; } @@ -283,15 +219,17 @@ void pcmcia_unregister_socket(struct pcmcia_socket *socket) if (socket->thread) kthread_stop(socket->thread); - release_cis_mem(socket); - /* remove from our own list */ down_write(&pcmcia_socket_list_rwsem); list_del(&socket->socket_list); up_write(&pcmcia_socket_list_rwsem); /* wait for sysfs to drop all references */ - release_resource_db(socket); + if (socket->resource_ops->exit) { + mutex_lock(&socket->ops_mutex); + socket->resource_ops->exit(socket); + mutex_unlock(&socket->ops_mutex); + } wait_for_completion(&socket->socket_released); } /* pcmcia_unregister_socket */ EXPORT_SYMBOL(pcmcia_unregister_socket); @@ -328,7 +266,7 @@ static int send_event(struct pcmcia_socket *s, event_t event, int priority) { int ret; - if (s->state & SOCKET_CARDBUS) + if ((s->state & SOCKET_CARDBUS) && (event != CS_EVENT_CARD_REMOVAL)) return 0; dev_dbg(&s->dev, "send_event(event %d, pri %d, callback 0x%p)\n", @@ -346,13 +284,6 @@ static int send_event(struct pcmcia_socket *s, event_t event, int priority) return ret; } -static void socket_remove_drivers(struct pcmcia_socket *skt) -{ - dev_dbg(&skt->dev, "remove_drivers\n"); - - send_event(skt, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH); -} - static int socket_reset(struct pcmcia_socket *skt) { int status, i; @@ -395,7 +326,9 @@ static void socket_shutdown(struct pcmcia_socket *s) dev_dbg(&s->dev, "shutdown\n"); - socket_remove_drivers(s); + send_event(s, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH); + + mutex_lock(&s->ops_mutex); s->state &= SOCKET_INUSE | SOCKET_PRESENT; msleep(shutdown_delay * 10); s->state &= SOCKET_INUSE; @@ -404,13 +337,22 @@ static void socket_shutdown(struct pcmcia_socket *s) s->socket = dead_socket; s->ops->init(s); s->ops->set_socket(s, &s->socket); - s->irq.AssignedIRQ = s->irq.Config = 0; s->lock_count = 0; - destroy_cis_cache(s); + kfree(s->fake_cis); + s->fake_cis = NULL; + s->functions = 0; + + /* From here on we can be sure that only we (that is, the + * pccardd thread) accesses this socket, and all (16-bit) + * PCMCIA interactions are gone. Therefore, release + * ops_mutex so that we don't get a sysfs-related lockdep + * warning. + */ + mutex_unlock(&s->ops_mutex); + #ifdef CONFIG_CARDBUS cb_free(s); #endif - s->functions = 0; /* give socket some time to power down */ msleep(100); @@ -421,7 +363,7 @@ static void socket_shutdown(struct pcmcia_socket *s) "*** DANGER *** unable to remove socket power\n"); } - cs_socket_put(s); + s->state &= ~SOCKET_INUSE; } static int socket_setup(struct pcmcia_socket *skt, int initial_delay) @@ -460,7 +402,8 @@ static int socket_setup(struct pcmcia_socket *skt, int initial_delay) return -EINVAL; } skt->state |= SOCKET_CARDBUS; - } + } else + skt->state &= ~SOCKET_CARDBUS; /* * Decode the card voltage requirements, and apply power to the card. @@ -509,8 +452,12 @@ static int socket_insert(struct pcmcia_socket *skt) dev_dbg(&skt->dev, "insert\n"); - if (!cs_socket_get(skt)) - return -ENODEV; + mutex_lock(&skt->ops_mutex); + if (skt->state & SOCKET_INUSE) { + mutex_unlock(&skt->ops_mutex); + return -EINVAL; + } + skt->state |= SOCKET_INUSE; ret = socket_setup(skt, setup_delay); if (ret == 0) { @@ -528,9 +475,11 @@ static int socket_insert(struct pcmcia_socket *skt) } #endif dev_dbg(&skt->dev, "insert done\n"); + mutex_unlock(&skt->ops_mutex); send_event(skt, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW); } else { + mutex_unlock(&skt->ops_mutex); socket_shutdown(skt); } @@ -542,58 +491,72 @@ static int socket_suspend(struct pcmcia_socket *skt) if (skt->state & SOCKET_SUSPEND) return -EBUSY; + mutex_lock(&skt->ops_mutex); + skt->suspended_state = skt->state; + send_event(skt, CS_EVENT_PM_SUSPEND, CS_EVENT_PRI_LOW); skt->socket = dead_socket; skt->ops->set_socket(skt, &skt->socket); if (skt->ops->suspend) skt->ops->suspend(skt); skt->state |= SOCKET_SUSPEND; - + mutex_unlock(&skt->ops_mutex); return 0; } static int socket_early_resume(struct pcmcia_socket *skt) { + mutex_lock(&skt->ops_mutex); skt->socket = dead_socket; skt->ops->init(skt); skt->ops->set_socket(skt, &skt->socket); if (skt->state & SOCKET_PRESENT) skt->resume_status = socket_setup(skt, resume_delay); + mutex_unlock(&skt->ops_mutex); return 0; } static int socket_late_resume(struct pcmcia_socket *skt) { + int ret; + + mutex_lock(&skt->ops_mutex); + skt->state &= ~SOCKET_SUSPEND; + mutex_unlock(&skt->ops_mutex); + if (!(skt->state & SOCKET_PRESENT)) { - skt->state &= ~SOCKET_SUSPEND; - return socket_insert(skt); + ret = socket_insert(skt); + if (ret == -ENODEV) + ret = 0; + return ret; } - if (skt->resume_status == 0) { - /* - * FIXME: need a better check here for cardbus cards. - */ - if (verify_cis_cache(skt) != 0) { - dev_dbg(&skt->dev, "cis mismatch - different card\n"); - socket_remove_drivers(skt); - destroy_cis_cache(skt); - /* - * Workaround: give DS time to schedule removal. - * Remove me once the 100ms delay is eliminated - * in ds.c - */ - msleep(200); - send_event(skt, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW); - } else { - dev_dbg(&skt->dev, "cis matches cache\n"); - send_event(skt, CS_EVENT_PM_RESUME, CS_EVENT_PRI_LOW); - } - } else { + if (skt->resume_status) { socket_shutdown(skt); + return 0; } - skt->state &= ~SOCKET_SUSPEND; + if (skt->suspended_state != skt->state) { + dev_dbg(&skt->dev, + "suspend state 0x%x != resume state 0x%x\n", + skt->suspended_state, skt->state); + + socket_shutdown(skt); + return socket_insert(skt); + } + +#ifdef CONFIG_CARDBUS + if (skt->state & SOCKET_CARDBUS) { + /* We can't be sure the CardBus card is the same + * as the one previously inserted. Therefore, remove + * and re-add... */ + cb_free(skt); + cb_alloc(skt); + return 0; + } +#endif + send_event(skt, CS_EVENT_PM_RESUME, CS_EVENT_PRI_LOW); return 0; } @@ -672,20 +635,26 @@ static int pccardd(void *__skt) complete(&skt->thread_done); + /* wait for userspace to catch up */ + msleep(250); + set_freezable(); for (;;) { unsigned long flags; unsigned int events; + unsigned int sysfs_events; set_current_state(TASK_INTERRUPTIBLE); spin_lock_irqsave(&skt->thread_lock, flags); events = skt->thread_events; skt->thread_events = 0; + sysfs_events = skt->sysfs_events; + skt->sysfs_events = 0; spin_unlock_irqrestore(&skt->thread_lock, flags); + mutex_lock(&skt->skt_mutex); if (events) { - mutex_lock(&skt->skt_mutex); if (events & SS_DETECT) socket_detect_change(skt); if (events & SS_BATDEAD) @@ -694,10 +663,41 @@ 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); - mutex_unlock(&skt->skt_mutex); - continue; } + if (sysfs_events) { + if (sysfs_events & PCMCIA_UEVENT_EJECT) + socket_remove(skt); + if (sysfs_events & PCMCIA_UEVENT_INSERT) + socket_insert(skt); + if ((sysfs_events & PCMCIA_UEVENT_SUSPEND) && + !(skt->state & SOCKET_CARDBUS)) { + if (skt->callback) + ret = skt->callback->suspend(skt); + else + ret = 0; + if (!ret) { + socket_suspend(skt); + msleep(100); + } + } + if ((sysfs_events & PCMCIA_UEVENT_RESUME) && + !(skt->state & SOCKET_CARDBUS)) { + ret = socket_resume(skt); + if (!ret && skt->callback) + skt->callback->resume(skt); + } + if ((sysfs_events & PCMCIA_UEVENT_REQUERY) && + !(skt->state & SOCKET_CARDBUS)) { + if (!ret && skt->callback) + skt->callback->requery(skt); + } + } + mutex_unlock(&skt->skt_mutex); + + if (events || sysfs_events) + continue; + if (kthread_should_stop()) break; @@ -707,6 +707,13 @@ static int pccardd(void *__skt) /* make sure we are running before we exit */ set_current_state(TASK_RUNNING); + /* shut down socket, if a device is still present */ + if (skt->state & SOCKET_PRESENT) { + mutex_lock(&skt->skt_mutex); + socket_remove(skt); + mutex_unlock(&skt->skt_mutex); + } + /* remove from the device core */ pccard_sysfs_remove_socket(&skt->dev); device_unregister(&skt->dev); @@ -732,6 +739,31 @@ void pcmcia_parse_events(struct pcmcia_socket *s, u_int events) } /* pcmcia_parse_events */ EXPORT_SYMBOL(pcmcia_parse_events); +/** + * pcmcia_parse_uevents() - tell pccardd to issue manual commands + * @s: the PCMCIA socket we wan't to command + * @events: events to pass to pccardd + * + * userspace-issued insert, eject, suspend and resume commands must be + * handled by pccardd to avoid any sysfs-related deadlocks. Valid events + * are PCMCIA_UEVENT_EJECT (for eject), PCMCIA_UEVENT__INSERT (for insert), + * PCMCIA_UEVENT_RESUME (for resume), PCMCIA_UEVENT_SUSPEND (for suspend) + * and PCMCIA_UEVENT_REQUERY (for re-querying the PCMCIA card). + */ +void pcmcia_parse_uevents(struct pcmcia_socket *s, u_int events) +{ + unsigned long flags; + dev_dbg(&s->dev, "parse_uevents: events %08x\n", events); + if (s->thread) { + spin_lock_irqsave(&s->thread_lock, flags); + s->sysfs_events |= events; + spin_unlock_irqrestore(&s->thread_lock, flags); + + wake_up_process(s->thread); + } +} +EXPORT_SYMBOL(pcmcia_parse_uevents); + /* register pcmcia_callback */ int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c) @@ -796,7 +828,10 @@ int pcmcia_reset_card(struct pcmcia_socket *skt) send_event(skt, CS_EVENT_RESET_PHYSICAL, CS_EVENT_PRI_LOW); if (skt->callback) skt->callback->suspend(skt); - if (socket_reset(skt) == 0) { + mutex_lock(&skt->ops_mutex); + ret = socket_reset(skt); + mutex_unlock(&skt->ops_mutex); + if (ret == 0) { send_event(skt, CS_EVENT_CARD_RESET, CS_EVENT_PRI_LOW); if (skt->callback) skt->callback->resume(skt); @@ -812,146 +847,86 @@ int pcmcia_reset_card(struct pcmcia_socket *skt) EXPORT_SYMBOL(pcmcia_reset_card); -/* These shut down or wake up a socket. They are sort of user - * initiated versions of the APM suspend and resume actions. - */ -int pcmcia_suspend_card(struct pcmcia_socket *skt) +static int pcmcia_socket_uevent(struct device *dev, + struct kobj_uevent_env *env) { - int ret; + struct pcmcia_socket *s = container_of(dev, struct pcmcia_socket, dev); - dev_dbg(&skt->dev, "suspending socket\n"); + if (add_uevent_var(env, "SOCKET_NO=%u", s->sock)) + return -ENOMEM; - mutex_lock(&skt->skt_mutex); - do { - if (!(skt->state & SOCKET_PRESENT)) { - ret = -ENODEV; - break; - } - if (skt->state & SOCKET_CARDBUS) { - ret = -EPERM; - break; - } - if (skt->callback) { - ret = skt->callback->suspend(skt); - if (ret) - break; - } - ret = socket_suspend(skt); - } while (0); - mutex_unlock(&skt->skt_mutex); + return 0; +} - return ret; -} /* suspend_card */ -EXPORT_SYMBOL(pcmcia_suspend_card); +static struct completion pcmcia_unload; -int pcmcia_resume_card(struct pcmcia_socket *skt) +static void pcmcia_release_socket_class(struct class *data) { - int ret; - - dev_dbg(&skt->dev, "waking up socket\n"); - - mutex_lock(&skt->skt_mutex); - do { - if (!(skt->state & SOCKET_PRESENT)) { - ret = -ENODEV; - break; - } - if (skt->state & SOCKET_CARDBUS) { - ret = -EPERM; - break; - } - ret = socket_resume(skt); - if (!ret && skt->callback) - skt->callback->resume(skt); - } while (0); - mutex_unlock(&skt->skt_mutex); + complete(&pcmcia_unload); +} - return ret; -} /* resume_card */ -EXPORT_SYMBOL(pcmcia_resume_card); +#ifdef CONFIG_PM -/* These handle user requests to eject or insert a card. */ -int pcmcia_eject_card(struct pcmcia_socket *skt) +static int __pcmcia_pm_op(struct device *dev, + int (*callback) (struct pcmcia_socket *skt)) { + struct pcmcia_socket *s = container_of(dev, struct pcmcia_socket, dev); int ret; - dev_dbg(&skt->dev, "user eject request\n"); - - mutex_lock(&skt->skt_mutex); - do { - if (!(skt->state & SOCKET_PRESENT)) { - ret = -ENODEV; - break; - } - - ret = send_event(skt, CS_EVENT_EJECTION_REQUEST, CS_EVENT_PRI_LOW); - if (ret != 0) { - ret = -EINVAL; - break; - } - - socket_remove(skt); - ret = 0; - } while (0); - mutex_unlock(&skt->skt_mutex); + mutex_lock(&s->skt_mutex); + ret = callback(s); + mutex_unlock(&s->skt_mutex); return ret; -} /* eject_card */ -EXPORT_SYMBOL(pcmcia_eject_card); - +} -int pcmcia_insert_card(struct pcmcia_socket *skt) +static int pcmcia_socket_dev_suspend_noirq(struct device *dev) { - int ret; - - dev_dbg(&skt->dev, "user insert request\n"); - - mutex_lock(&skt->skt_mutex); - do { - if (skt->state & SOCKET_PRESENT) { - ret = -EBUSY; - break; - } - if (socket_insert(skt) == -ENODEV) { - ret = -ENODEV; - break; - } - ret = 0; - } while (0); - mutex_unlock(&skt->skt_mutex); - - return ret; -} /* insert_card */ -EXPORT_SYMBOL(pcmcia_insert_card); + return __pcmcia_pm_op(dev, socket_suspend); +} +static int pcmcia_socket_dev_resume_noirq(struct device *dev) +{ + return __pcmcia_pm_op(dev, socket_early_resume); +} -static int pcmcia_socket_uevent(struct device *dev, - struct kobj_uevent_env *env) +static int pcmcia_socket_dev_resume(struct device *dev) { - struct pcmcia_socket *s = container_of(dev, struct pcmcia_socket, dev); + return __pcmcia_pm_op(dev, socket_late_resume); +} - if (add_uevent_var(env, "SOCKET_NO=%u", s->sock)) - return -ENOMEM; +static const struct dev_pm_ops pcmcia_socket_pm_ops = { + /* dev_resume may be called with IRQs enabled */ + SET_SYSTEM_SLEEP_PM_OPS(NULL, + pcmcia_socket_dev_resume) - return 0; -} + /* late suspend must be called with IRQs disabled */ + .suspend_noirq = pcmcia_socket_dev_suspend_noirq, + .freeze_noirq = pcmcia_socket_dev_suspend_noirq, + .poweroff_noirq = pcmcia_socket_dev_suspend_noirq, + /* early resume must be called with IRQs disabled */ + .resume_noirq = pcmcia_socket_dev_resume_noirq, + .thaw_noirq = pcmcia_socket_dev_resume_noirq, + .restore_noirq = pcmcia_socket_dev_resume_noirq, +}; -static struct completion pcmcia_unload; +#define PCMCIA_SOCKET_CLASS_PM_OPS (&pcmcia_socket_pm_ops) -static void pcmcia_release_socket_class(struct class *data) -{ - complete(&pcmcia_unload); -} +#else /* CONFIG_PM */ + +#define PCMCIA_SOCKET_CLASS_PM_OPS NULL +#endif /* CONFIG_PM */ struct class pcmcia_socket_class = { .name = "pcmcia_socket", .dev_uevent = pcmcia_socket_uevent, .dev_release = pcmcia_release_socket, .class_release = pcmcia_release_socket_class, + .pm = PCMCIA_SOCKET_CLASS_PM_OPS, }; EXPORT_SYMBOL(pcmcia_socket_class); diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h index 3bc02d53a3a3..4126a75445ea 100644 --- a/drivers/pcmcia/cs_internal.h +++ b/drivers/pcmcia/cs_internal.h @@ -52,13 +52,11 @@ struct cis_cache_entry { struct pccard_resource_ops { int (*validate_mem) (struct pcmcia_socket *s); - int (*adjust_io_region) (struct resource *res, - unsigned long r_start, - unsigned long r_end, - struct pcmcia_socket *s); - struct resource* (*find_io) (unsigned long base, int num, - unsigned long align, - struct pcmcia_socket *s); + int (*find_io) (struct pcmcia_socket *s, + unsigned int attr, + unsigned int *base, + unsigned int num, + unsigned int align); struct resource* (*find_mem) (unsigned long base, unsigned long num, unsigned long align, int low, struct pcmcia_socket *s); @@ -87,37 +85,19 @@ struct pccard_resource_ops { #define SOCKET_CARDBUS 0x8000 #define SOCKET_CARDBUS_CONFIG 0x10000 -static inline int cs_socket_get(struct pcmcia_socket *skt) -{ - int ret; - - WARN_ON(skt->state & SOCKET_INUSE); - - ret = try_module_get(skt->owner); - if (ret) - skt->state |= SOCKET_INUSE; - return ret; -} - -static inline void cs_socket_put(struct pcmcia_socket *skt) -{ - if (skt->state & SOCKET_INUSE) { - skt->state &= ~SOCKET_INUSE; - module_put(skt->owner); - } -} +/* + * Stuff internal to module "pcmcia_rsrc": + */ +extern int static_init(struct pcmcia_socket *s); +extern struct resource *pcmcia_make_resource(unsigned long start, + unsigned long end, + int flags, const char *name); /* * Stuff internal to module "pcmcia_core": */ -/* cistpl.c */ -int verify_cis_cache(struct pcmcia_socket *s); - -/* rsrc_mgr.c */ -void release_resource_db(struct pcmcia_socket *s); - /* socket_sysfs.c */ extern int pccard_sysfs_add_socket(struct device *dev); extern void pccard_sysfs_remove_socket(struct device *dev); @@ -125,8 +105,6 @@ extern void pccard_sysfs_remove_socket(struct device *dev); /* cardbus.c */ int cb_alloc(struct pcmcia_socket *s); void cb_free(struct pcmcia_socket *s); -int read_cb_mem(struct pcmcia_socket *s, int space, u_int addr, u_int len, - void *ptr); @@ -138,7 +116,8 @@ struct pcmcia_callback{ struct module *owner; int (*event) (struct pcmcia_socket *s, event_t event, int priority); - void (*requery) (struct pcmcia_socket *s, int new_cis); + void (*requery) (struct pcmcia_socket *s); + int (*validate) (struct pcmcia_socket *s, unsigned int *i); int (*suspend) (struct pcmcia_socket *s); int (*resume) (struct pcmcia_socket *s); }; @@ -151,16 +130,37 @@ extern struct class pcmcia_socket_class; int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c); struct pcmcia_socket *pcmcia_get_socket_by_nr(unsigned int nr); -int pcmcia_suspend_card(struct pcmcia_socket *skt); -int pcmcia_resume_card(struct pcmcia_socket *skt); - -int pcmcia_eject_card(struct pcmcia_socket *skt); -int pcmcia_insert_card(struct pcmcia_socket *skt); +void pcmcia_parse_uevents(struct pcmcia_socket *socket, unsigned int events); +#define PCMCIA_UEVENT_EJECT 0x0001 +#define PCMCIA_UEVENT_INSERT 0x0002 +#define PCMCIA_UEVENT_SUSPEND 0x0004 +#define PCMCIA_UEVENT_RESUME 0x0008 +#define PCMCIA_UEVENT_REQUERY 0x0010 struct pcmcia_socket *pcmcia_get_socket(struct pcmcia_socket *skt); void pcmcia_put_socket(struct pcmcia_socket *skt); +/* + * Stuff internal to module "pcmcia". + */ +/* ds.c */ +extern struct bus_type pcmcia_bus_type; + +/* pcmcia_resource.c */ +extern int pcmcia_release_configuration(struct pcmcia_device *p_dev); +extern int pcmcia_validate_mem(struct pcmcia_socket *s); +extern struct resource *pcmcia_find_mem_region(u_long base, + u_long num, + u_long align, + int low, + struct pcmcia_socket *s); + +void pcmcia_cleanup_irq(struct pcmcia_socket *s); +int pcmcia_setup_irq(struct pcmcia_device *p_dev); + /* cistpl.c */ +extern struct bin_attribute pccard_cis_attr; + int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, u_int len, void *ptr); void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, @@ -172,8 +172,8 @@ int pccard_read_tuple(struct pcmcia_socket *s, unsigned int function, int pcmcia_replace_cis(struct pcmcia_socket *s, const u8 *data, const size_t len); int pccard_validate_cis(struct pcmcia_socket *s, unsigned int *count); +int verify_cis_cache(struct pcmcia_socket *s); -/* loop over CIS entries */ int pccard_loop_tuple(struct pcmcia_socket *s, unsigned int function, cisdata_t code, cisparse_t *parse, void *priv_data, int (*loop_tuple) (tuple_t *tuple, @@ -189,35 +189,8 @@ int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function, int pccard_get_tuple_data(struct pcmcia_socket *s, tuple_t *tuple); -/* rsrc_mgr.c */ -int pcmcia_validate_mem(struct pcmcia_socket *s); -struct resource *pcmcia_find_io_region(unsigned long base, - int num, - unsigned long align, - struct pcmcia_socket *s); -int pcmcia_adjust_io_region(struct resource *res, - unsigned long r_start, - unsigned long r_end, - struct pcmcia_socket *s); -struct resource *pcmcia_find_mem_region(u_long base, - u_long num, - u_long align, - int low, - struct pcmcia_socket *s); - -/* - * Stuff internal to module "pcmcia". - */ -/* ds.c */ -extern struct bus_type pcmcia_bus_type; - -/* pcmcia_resource.c */ -extern int pcmcia_release_configuration(struct pcmcia_device *p_dev); - #ifdef CONFIG_PCMCIA_IOCTL /* ds.c */ -extern spinlock_t pcmcia_dev_list_lock; - extern struct pcmcia_device *pcmcia_get_dev(struct pcmcia_device *p_dev); extern void pcmcia_put_dev(struct pcmcia_device *p_dev); diff --git a/drivers/pcmcia/db1xxx_ss.c b/drivers/pcmcia/db1xxx_ss.c new file mode 100644 index 000000000000..0f4cc3f00028 --- /dev/null +++ b/drivers/pcmcia/db1xxx_ss.c @@ -0,0 +1,580 @@ +/* + * PCMCIA socket code for the Alchemy Db1xxx/Pb1xxx boards. + * + * Copyright (c) 2009 Manuel Lauss <manuel.lauss@gmail.com> + * + */ + +/* This is a fairly generic PCMCIA socket driver suitable for the + * following Alchemy Development boards: + * Db1000, Db/Pb1500, Db/Pb1100, Db/Pb1550, Db/Pb1200. + * + * The Db1000 is used as a reference: Per-socket card-, carddetect- and + * statuschange IRQs connected to SoC GPIOs, control and status register + * bits arranged in per-socket groups in an external PLD. All boards + * listed here use this layout, including bit positions and meanings. + * Of course there are exceptions in later boards: + * + * - Pb1100/Pb1500: single socket only; voltage key bits VS are + * at STATUS[5:4] (instead of STATUS[1:0]). + * - Au1200-based: additional card-eject irqs, irqs not gpios! + */ + +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/pm.h> +#include <linux/platform_device.h> +#include <linux/resource.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +#include <pcmcia/cs_types.h> +#include <pcmcia/ss.h> + +#include <asm/mach-au1x00/au1000.h> +#include <asm/mach-db1x00/bcsr.h> + +#define MEM_MAP_SIZE 0x400000 +#define IO_MAP_SIZE 0x1000 + +struct db1x_pcmcia_sock { + struct pcmcia_socket socket; + int nr; /* socket number */ + void *virt_io; + + phys_addr_t phys_io; + phys_addr_t phys_attr; + phys_addr_t phys_mem; + + /* previous flags for set_socket() */ + unsigned int old_flags; + + /* interrupt sources: linux irq numbers! */ + int insert_irq; /* default carddetect irq */ + int stschg_irq; /* card-status-change irq */ + int card_irq; /* card irq */ + int eject_irq; /* db1200/pb1200 have these */ + +#define BOARD_TYPE_DEFAULT 0 /* most boards */ +#define BOARD_TYPE_DB1200 1 /* IRQs aren't gpios */ +#define BOARD_TYPE_PB1100 2 /* VS bits slightly different */ + int board_type; +}; + +#define to_db1x_socket(x) container_of(x, struct db1x_pcmcia_sock, socket) + +/* DB/PB1200: check CPLD SIGSTATUS register bit 10/12 */ +static int db1200_card_inserted(struct db1x_pcmcia_sock *sock) +{ + unsigned short sigstat; + + sigstat = bcsr_read(BCSR_SIGSTAT); + return sigstat & 1 << (8 + 2 * sock->nr); +} + +/* carddetect gpio: low-active */ +static int db1000_card_inserted(struct db1x_pcmcia_sock *sock) +{ + return !gpio_get_value(irq_to_gpio(sock->insert_irq)); +} + +static int db1x_card_inserted(struct db1x_pcmcia_sock *sock) +{ + switch (sock->board_type) { + case BOARD_TYPE_DB1200: + return db1200_card_inserted(sock); + default: + return db1000_card_inserted(sock); + } +} + +/* STSCHG tends to bounce heavily when cards are inserted/ejected. + * To avoid this, the interrupt is normally disabled and only enabled + * after reset to a card has been de-asserted. + */ +static inline void set_stschg(struct db1x_pcmcia_sock *sock, int en) +{ + if (sock->stschg_irq != -1) { + if (en) + enable_irq(sock->stschg_irq); + else + disable_irq(sock->stschg_irq); + } +} + +static irqreturn_t db1000_pcmcia_cdirq(int irq, void *data) +{ + struct db1x_pcmcia_sock *sock = data; + + pcmcia_parse_events(&sock->socket, SS_DETECT); + + return IRQ_HANDLED; +} + +static irqreturn_t db1000_pcmcia_stschgirq(int irq, void *data) +{ + struct db1x_pcmcia_sock *sock = data; + + pcmcia_parse_events(&sock->socket, SS_STSCHG); + + return IRQ_HANDLED; +} + +static irqreturn_t db1200_pcmcia_cdirq(int irq, void *data) +{ + struct db1x_pcmcia_sock *sock = data; + + /* Db/Pb1200 have separate per-socket insertion and ejection + * interrupts which stay asserted as long as the card is + * inserted/missing. The one which caused us to be called + * needs to be disabled and the other one enabled. + */ + if (irq == sock->insert_irq) { + disable_irq_nosync(sock->insert_irq); + enable_irq(sock->eject_irq); + } else { + disable_irq_nosync(sock->eject_irq); + enable_irq(sock->insert_irq); + } + + pcmcia_parse_events(&sock->socket, SS_DETECT); + + return IRQ_HANDLED; +} + +static int db1x_pcmcia_setup_irqs(struct db1x_pcmcia_sock *sock) +{ + int ret; + + if (sock->stschg_irq != -1) { + ret = request_irq(sock->stschg_irq, db1000_pcmcia_stschgirq, + 0, "pcmcia_stschg", sock); + if (ret) + return ret; + } + + /* Db/Pb1200 have separate per-socket insertion and ejection + * interrupts, which should show edge behaviour but don't. + * So interrupts are disabled until both insertion and + * ejection handler have been registered and the currently + * active one disabled. + */ + if (sock->board_type == BOARD_TYPE_DB1200) { + ret = request_irq(sock->insert_irq, db1200_pcmcia_cdirq, + IRQF_DISABLED, "pcmcia_insert", sock); + if (ret) + goto out1; + + ret = request_irq(sock->eject_irq, db1200_pcmcia_cdirq, + IRQF_DISABLED, "pcmcia_eject", sock); + if (ret) { + free_irq(sock->insert_irq, sock); + goto out1; + } + + /* enable the currently silent one */ + if (db1200_card_inserted(sock)) + enable_irq(sock->eject_irq); + else + enable_irq(sock->insert_irq); + } else { + /* all other (older) Db1x00 boards use a GPIO to show + * card detection status: use both-edge triggers. + */ + set_irq_type(sock->insert_irq, IRQ_TYPE_EDGE_BOTH); + ret = request_irq(sock->insert_irq, db1000_pcmcia_cdirq, + 0, "pcmcia_carddetect", sock); + + if (ret) + goto out1; + } + + return 0; /* all done */ + +out1: + if (sock->stschg_irq != -1) + free_irq(sock->stschg_irq, sock); + + return ret; +} + +static void db1x_pcmcia_free_irqs(struct db1x_pcmcia_sock *sock) +{ + if (sock->stschg_irq != -1) + free_irq(sock->stschg_irq, sock); + + free_irq(sock->insert_irq, sock); + if (sock->eject_irq != -1) + free_irq(sock->eject_irq, sock); +} + +/* + * configure a PCMCIA socket on the Db1x00 series of boards (and + * compatibles). + * + * 2 external registers are involved: + * pcmcia_status (offset 0x04): bits [0:1/2:3]: read card voltage id + * pcmcia_control(offset 0x10): + * bits[0:1] set vcc for card + * bits[2:3] set vpp for card + * bit 4: enable data buffers + * bit 7: reset# for card + * add 8 for second socket. + */ +static int db1x_pcmcia_configure(struct pcmcia_socket *skt, + struct socket_state_t *state) +{ + struct db1x_pcmcia_sock *sock = to_db1x_socket(skt); + unsigned short cr_clr, cr_set; + unsigned int changed; + int v, p, ret; + + /* card voltage setup */ + cr_clr = (0xf << (sock->nr * 8)); /* clear voltage settings */ + cr_set = 0; + v = p = ret = 0; + + switch (state->Vcc) { + case 50: + ++v; + case 33: + ++v; + case 0: + break; + default: + printk(KERN_INFO "pcmcia%d unsupported Vcc %d\n", + sock->nr, state->Vcc); + } + + switch (state->Vpp) { + case 12: + ++p; + case 33: + case 50: + ++p; + case 0: + break; + default: + printk(KERN_INFO "pcmcia%d unsupported Vpp %d\n", + sock->nr, state->Vpp); + } + + /* sanity check: Vpp must be 0, 12, or Vcc */ + if (((state->Vcc == 33) && (state->Vpp == 50)) || + ((state->Vcc == 50) && (state->Vpp == 33))) { + printk(KERN_INFO "pcmcia%d bad Vcc/Vpp combo (%d %d)\n", + sock->nr, state->Vcc, state->Vpp); + v = p = 0; + ret = -EINVAL; + } + + /* create new voltage code */ + cr_set |= ((v << 2) | p) << (sock->nr * 8); + + changed = state->flags ^ sock->old_flags; + + if (changed & SS_RESET) { + if (state->flags & SS_RESET) { + set_stschg(sock, 0); + /* assert reset, disable io buffers */ + cr_clr |= (1 << (7 + (sock->nr * 8))); + cr_clr |= (1 << (4 + (sock->nr * 8))); + } else { + /* de-assert reset, enable io buffers */ + cr_set |= 1 << (7 + (sock->nr * 8)); + cr_set |= 1 << (4 + (sock->nr * 8)); + } + } + + /* update PCMCIA configuration */ + bcsr_mod(BCSR_PCMCIA, cr_clr, cr_set); + + sock->old_flags = state->flags; + + /* reset was taken away: give card time to initialize properly */ + if ((changed & SS_RESET) && !(state->flags & SS_RESET)) { + msleep(500); + set_stschg(sock, 1); + } + + return ret; +} + +/* VCC bits at [3:2]/[11:10] */ +#define GET_VCC(cr, socknr) \ + ((((cr) >> 2) >> ((socknr) * 8)) & 3) + +/* VS bits at [0:1]/[3:2] */ +#define GET_VS(sr, socknr) \ + (((sr) >> (2 * (socknr))) & 3) + +/* reset bits at [7]/[15] */ +#define GET_RESET(cr, socknr) \ + ((cr) & (1 << (7 + (8 * (socknr))))) + +static int db1x_pcmcia_get_status(struct pcmcia_socket *skt, + unsigned int *value) +{ + struct db1x_pcmcia_sock *sock = to_db1x_socket(skt); + unsigned short cr, sr; + unsigned int status; + + status = db1x_card_inserted(sock) ? SS_DETECT : 0; + + cr = bcsr_read(BCSR_PCMCIA); + sr = bcsr_read(BCSR_STATUS); + + /* PB1100/PB1500: voltage key bits are at [5:4] */ + if (sock->board_type == BOARD_TYPE_PB1100) + sr >>= 4; + + /* determine card type */ + switch (GET_VS(sr, sock->nr)) { + case 0: + case 2: + status |= SS_3VCARD; /* 3V card */ + case 3: + break; /* 5V card: set nothing */ + default: + status |= SS_XVCARD; /* treated as unsupported in core */ + } + + /* if Vcc is not zero, we have applied power to a card */ + status |= GET_VCC(cr, sock->nr) ? SS_POWERON : 0; + + /* reset de-asserted? then we're ready */ + status |= (GET_RESET(cr, sock->nr)) ? SS_READY : SS_RESET; + + *value = status; + + return 0; +} + +static int db1x_pcmcia_sock_init(struct pcmcia_socket *skt) +{ + return 0; +} + +static int db1x_pcmcia_sock_suspend(struct pcmcia_socket *skt) +{ + return 0; +} + +static int au1x00_pcmcia_set_io_map(struct pcmcia_socket *skt, + struct pccard_io_map *map) +{ + struct db1x_pcmcia_sock *sock = to_db1x_socket(skt); + + map->start = (u32)sock->virt_io; + map->stop = map->start + IO_MAP_SIZE; + + return 0; +} + +static int au1x00_pcmcia_set_mem_map(struct pcmcia_socket *skt, + struct pccard_mem_map *map) +{ + struct db1x_pcmcia_sock *sock = to_db1x_socket(skt); + + if (map->flags & MAP_ATTRIB) + map->static_start = sock->phys_attr + map->card_start; + else + map->static_start = sock->phys_mem + map->card_start; + + return 0; +} + +static struct pccard_operations db1x_pcmcia_operations = { + .init = db1x_pcmcia_sock_init, + .suspend = db1x_pcmcia_sock_suspend, + .get_status = db1x_pcmcia_get_status, + .set_socket = db1x_pcmcia_configure, + .set_io_map = au1x00_pcmcia_set_io_map, + .set_mem_map = au1x00_pcmcia_set_mem_map, +}; + +static int __devinit db1x_pcmcia_socket_probe(struct platform_device *pdev) +{ + struct db1x_pcmcia_sock *sock; + struct resource *r; + int ret, bid; + + sock = kzalloc(sizeof(struct db1x_pcmcia_sock), GFP_KERNEL); + if (!sock) + return -ENOMEM; + + sock->nr = pdev->id; + + bid = BCSR_WHOAMI_BOARD(bcsr_read(BCSR_WHOAMI)); + switch (bid) { + case BCSR_WHOAMI_PB1500: + case BCSR_WHOAMI_PB1500R2: + case BCSR_WHOAMI_PB1100: + sock->board_type = BOARD_TYPE_PB1100; + break; + case BCSR_WHOAMI_DB1000 ... BCSR_WHOAMI_PB1550_SDR: + sock->board_type = BOARD_TYPE_DEFAULT; + break; + case BCSR_WHOAMI_PB1200 ... BCSR_WHOAMI_DB1200: + sock->board_type = BOARD_TYPE_DB1200; + break; + default: + printk(KERN_INFO "db1xxx-ss: unknown board %d!\n", bid); + ret = -ENODEV; + goto out0; + }; + + /* + * gather resources necessary and optional nice-to-haves to + * operate a socket: + * This includes IRQs for Carddetection/ejection, the card + * itself and optional status change detection. + * Also, the memory areas covered by a socket. For these + * we require the real 36bit addresses (see the au1000.h + * header for more information). + */ + + /* card: irq assigned to the card itself. */ + r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "card"); + sock->card_irq = r ? r->start : 0; + + /* insert: irq which triggers on card insertion/ejection */ + r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "insert"); + sock->insert_irq = r ? r->start : -1; + + /* stschg: irq which trigger on card status change (optional) */ + r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "stschg"); + sock->stschg_irq = r ? r->start : -1; + + /* eject: irq which triggers on ejection (DB1200/PB1200 only) */ + r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "eject"); + sock->eject_irq = r ? r->start : -1; + + ret = -ENODEV; + + /* 36bit PCMCIA Attribute area address */ + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-attr"); + if (!r) { + printk(KERN_ERR "pcmcia%d has no 'pseudo-attr' resource!\n", + sock->nr); + goto out0; + } + sock->phys_attr = r->start; + + /* 36bit PCMCIA Memory area address */ + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-mem"); + if (!r) { + printk(KERN_ERR "pcmcia%d has no 'pseudo-mem' resource!\n", + sock->nr); + goto out0; + } + sock->phys_mem = r->start; + + /* 36bit PCMCIA IO area address */ + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-io"); + if (!r) { + printk(KERN_ERR "pcmcia%d has no 'pseudo-io' resource!\n", + sock->nr); + goto out0; + } + sock->phys_io = r->start; + + /* + * PCMCIA client drivers use the inb/outb macros to access + * the IO registers. Since mips_io_port_base is added + * to the access address of the mips implementation of + * inb/outb, we need to subtract it here because we want + * to access the I/O or MEM address directly, without + * going through this "mips_io_port_base" mechanism. + */ + sock->virt_io = (void *)(ioremap(sock->phys_io, IO_MAP_SIZE) - + mips_io_port_base); + + if (!sock->virt_io) { + printk(KERN_ERR "pcmcia%d: cannot remap IO area\n", + sock->nr); + ret = -ENOMEM; + goto out0; + } + + sock->socket.ops = &db1x_pcmcia_operations; + sock->socket.owner = THIS_MODULE; + sock->socket.pci_irq = sock->card_irq; + sock->socket.features = SS_CAP_STATIC_MAP | SS_CAP_PCCARD; + sock->socket.map_size = MEM_MAP_SIZE; + sock->socket.io_offset = (unsigned long)sock->virt_io; + sock->socket.dev.parent = &pdev->dev; + sock->socket.resource_ops = &pccard_static_ops; + + platform_set_drvdata(pdev, sock); + + ret = db1x_pcmcia_setup_irqs(sock); + if (ret) { + printk(KERN_ERR "pcmcia%d cannot setup interrupts\n", + sock->nr); + goto out1; + } + + set_stschg(sock, 0); + + ret = pcmcia_register_socket(&sock->socket); + if (ret) { + printk(KERN_ERR "pcmcia%d failed to register\n", sock->nr); + goto out2; + } + + printk(KERN_INFO "Alchemy Db/Pb1xxx pcmcia%d @ io/attr/mem %09llx" + "(%p) %09llx %09llx card/insert/stschg/eject irqs @ %d " + "%d %d %d\n", sock->nr, sock->phys_io, sock->virt_io, + sock->phys_attr, sock->phys_mem, sock->card_irq, + sock->insert_irq, sock->stschg_irq, sock->eject_irq); + + return 0; + +out2: + db1x_pcmcia_free_irqs(sock); +out1: + iounmap((void *)(sock->virt_io + (u32)mips_io_port_base)); +out0: + kfree(sock); + return ret; +} + +static int __devexit db1x_pcmcia_socket_remove(struct platform_device *pdev) +{ + struct db1x_pcmcia_sock *sock = platform_get_drvdata(pdev); + + db1x_pcmcia_free_irqs(sock); + pcmcia_unregister_socket(&sock->socket); + iounmap((void *)(sock->virt_io + (u32)mips_io_port_base)); + kfree(sock); + + return 0; +} + +static struct platform_driver db1x_pcmcia_socket_driver = { + .driver = { + .name = "db1xxx_pcmcia", + .owner = THIS_MODULE, + }, + .probe = db1x_pcmcia_socket_probe, + .remove = __devexit_p(db1x_pcmcia_socket_remove), +}; + +int __init db1x_pcmcia_socket_load(void) +{ + return platform_driver_register(&db1x_pcmcia_socket_driver); +} + +void __exit db1x_pcmcia_socket_unload(void) +{ + platform_driver_unregister(&db1x_pcmcia_socket_driver); +} + +module_init(db1x_pcmcia_socket_load); +module_exit(db1x_pcmcia_socket_unload); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("PCMCIA Socket Services for Alchemy Db/Pb1x00 boards"); +MODULE_AUTHOR("Manuel Lauss"); diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 1a4a3c49cc15..7ef7adee5e4f 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -24,6 +24,7 @@ #include <linux/firmware.h> #include <linux/kref.h> #include <linux/dma-mapping.h> +#include <linux/slab.h> #include <pcmcia/cs_types.h> #include <pcmcia/cs.h> @@ -42,8 +43,6 @@ MODULE_DESCRIPTION("PCMCIA Driver Services"); MODULE_LICENSE("GPL"); -spinlock_t pcmcia_dev_list_lock; - /*====================================================================*/ static void pcmcia_check_driver(struct pcmcia_driver *p_drv) @@ -126,9 +125,9 @@ pcmcia_store_new_id(struct device_driver *driver, const char *buf, size_t count) dynid->id.device_no = device_no; memcpy(dynid->id.prod_id_hash, prod_id_hash, sizeof(__u32) * 4); - spin_lock(&pdrv->dynids.lock); + mutex_lock(&pdrv->dynids.lock); list_add_tail(&dynid->node, &pdrv->dynids.list); - spin_unlock(&pdrv->dynids.lock); + mutex_unlock(&pdrv->dynids.lock); if (get_driver(&pdrv->drv)) { retval = driver_attach(&pdrv->drv); @@ -146,12 +145,12 @@ pcmcia_free_dynids(struct pcmcia_driver *drv) { struct pcmcia_dynid *dynid, *n; - spin_lock(&drv->dynids.lock); + mutex_lock(&drv->dynids.lock); list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) { list_del(&dynid->node); kfree(dynid); } - spin_unlock(&drv->dynids.lock); + mutex_unlock(&drv->dynids.lock); } static int @@ -182,7 +181,7 @@ int pcmcia_register_driver(struct pcmcia_driver *driver) /* initialize common fields */ driver->drv.bus = &pcmcia_bus_type; driver->drv.owner = driver->owner; - spin_lock_init(&driver->dynids.lock); + mutex_init(&driver->dynids.lock); INIT_LIST_HEAD(&driver->dynids.list); pr_debug("registering driver %s\n", driver->drv.name); @@ -239,30 +238,21 @@ static void pcmcia_release_function(struct kref *ref) static void pcmcia_release_dev(struct device *dev) { struct pcmcia_device *p_dev = to_pcmcia_dev(dev); + int i; dev_dbg(dev, "releasing device\n"); pcmcia_put_socket(p_dev->socket); + for (i = 0; i < 4; i++) + kfree(p_dev->prod_id[i]); kfree(p_dev->devname); kref_put(&p_dev->function_config->ref, pcmcia_release_function); kfree(p_dev); } -static void pcmcia_add_device_later(struct pcmcia_socket *s, int mfc) -{ - if (!s->pcmcia_state.device_add_pending) { - dev_dbg(&s->dev, "scheduling to add %s secondary" - " device to %d\n", mfc ? "mfc" : "pfc", s->sock); - s->pcmcia_state.device_add_pending = 1; - s->pcmcia_state.mfc_pfc = mfc; - schedule_work(&s->device_add); - } - return; -} static int pcmcia_device_probe(struct device *dev) { struct pcmcia_device *p_dev; struct pcmcia_driver *p_drv; - struct pcmcia_device_id *did; struct pcmcia_socket *s; cistpl_config_t cis_config; int ret = 0; @@ -275,18 +265,6 @@ 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_set_drvdata(dev) - * 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 = dev_get_drvdata(&p_dev->dev); - dev_dbg(dev, "trying to bind to %s\n", p_drv->drv.name); if ((!p_drv->probe) || (!p_dev->function_config) || @@ -315,9 +293,11 @@ static int pcmcia_device_probe(struct device *dev) goto put_module; } - if (did && (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) && + mutex_lock(&s->ops_mutex); + if ((s->pcmcia_state.has_pfc) && (p_dev->socket->device_count == 1) && (p_dev->device_no == 0)) - pcmcia_add_device_later(p_dev->socket, 0); + pcmcia_parse_uevents(s, PCMCIA_UEVENT_REQUERY); + mutex_unlock(&s->ops_mutex); put_module: if (ret) @@ -336,26 +316,26 @@ static void pcmcia_card_remove(struct pcmcia_socket *s, struct pcmcia_device *le { struct pcmcia_device *p_dev; struct pcmcia_device *tmp; - unsigned long flags; dev_dbg(leftover ? &leftover->dev : &s->dev, "pcmcia_card_remove(%d) %s\n", s->sock, leftover ? leftover->devname : ""); + mutex_lock(&s->ops_mutex); if (!leftover) s->device_count = 0; else s->device_count = 1; + mutex_unlock(&s->ops_mutex); /* 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); + mutex_lock(&s->ops_mutex); list_del(&p_dev->socket_device_list); - p_dev->_removed = 1; - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + mutex_unlock(&s->ops_mutex); dev_dbg(&p_dev->dev, "unregistering device\n"); device_unregister(&p_dev->dev); @@ -368,7 +348,6 @@ 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; p_dev = to_pcmcia_dev(dev); @@ -380,9 +359,8 @@ static int pcmcia_device_remove(struct device *dev) * pseudo multi-function card, we need to unbind * all devices */ - did = dev_get_drvdata(&p_dev->dev); - if (did && (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) && - (p_dev->socket->device_count != 0) && + if ((p_dev->socket->pcmcia_state.has_pfc) && + (p_dev->socket->device_count > 0) && (p_dev->device_no == 0)) pcmcia_card_remove(p_dev->socket, p_dev); @@ -393,8 +371,6 @@ static int pcmcia_device_remove(struct device *dev) if (p_drv->remove) p_drv->remove(p_dev); - p_dev->dev_node = NULL; - /* check for proper unloading */ if (p_dev->_irq || p_dev->_io || p_dev->_locked) dev_printk(KERN_INFO, dev, @@ -431,16 +407,20 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev) if (!pccard_read_tuple(p_dev->socket, BIND_FN_ALL, CISTPL_MANFID, &manf_id)) { + mutex_lock(&p_dev->socket->ops_mutex); p_dev->manf_id = manf_id.manf; p_dev->card_id = manf_id.card; p_dev->has_manf_id = 1; p_dev->has_card_id = 1; + mutex_unlock(&p_dev->socket->ops_mutex); } if (!pccard_read_tuple(p_dev->socket, p_dev->func, CISTPL_FUNCID, &func_id)) { + mutex_lock(&p_dev->socket->ops_mutex); p_dev->func_id = func_id.func; p_dev->has_func_id = 1; + mutex_unlock(&p_dev->socket->ops_mutex); } else { /* rule of thumb: cards with no FUNCID, but with * common memory device geometry information, are @@ -457,17 +437,21 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev) dev_dbg(&p_dev->dev, "mem device geometry probably means " "FUNCID_MEMORY\n"); + mutex_lock(&p_dev->socket->ops_mutex); p_dev->func_id = CISTPL_FUNCID_MEMORY; p_dev->has_func_id = 1; + mutex_unlock(&p_dev->socket->ops_mutex); } kfree(devgeo); } if (!pccard_read_tuple(p_dev->socket, BIND_FN_ALL, CISTPL_VERS_1, vers1)) { + mutex_lock(&p_dev->socket->ops_mutex); for (i = 0; i < min_t(unsigned int, 4, vers1->ns); i++) { char *tmp; unsigned int length; + char *new; tmp = vers1->str + vers1->ofs[i]; @@ -475,14 +459,17 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev) if ((length < 2) || (length > 255)) continue; - p_dev->prod_id[i] = kmalloc(sizeof(char) * length, - GFP_KERNEL); - if (!p_dev->prod_id[i]) + new = kmalloc(sizeof(char) * length, GFP_KERNEL); + if (!new) continue; - p_dev->prod_id[i] = strncpy(p_dev->prod_id[i], - tmp, length); + new = strncpy(new, tmp, length); + + tmp = p_dev->prod_id[i]; + p_dev->prod_id[i] = new; + kfree(tmp); } + mutex_unlock(&p_dev->socket->ops_mutex); } kfree(vers1); @@ -490,38 +477,34 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev) } -/* device_add_lock is needed to avoid double registration by cardmgr and kernel. - * Serializes pcmcia_device_add; will most likely be removed in future. - * - * While it has the caveat that adding new PCMCIA devices inside(!) device_register() - * won't work, this doesn't matter much at the moment: the driver core doesn't - * support it either. - */ -static DEFINE_MUTEX(device_add_lock); - struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int function) { struct pcmcia_device *p_dev, *tmp_dev; - unsigned long flags; + int i; s = pcmcia_get_socket(s); if (!s) return NULL; - mutex_lock(&device_add_lock); - pr_debug("adding device to %d, function %d\n", s->sock, function); - /* max of 4 devices per card */ - if (s->device_count == 4) - goto err_put; - p_dev = kzalloc(sizeof(struct pcmcia_device), GFP_KERNEL); if (!p_dev) goto err_put; - p_dev->socket = s; + mutex_lock(&s->ops_mutex); p_dev->device_no = (s->device_count++); + mutex_unlock(&s->ops_mutex); + + /* max of 2 PFC devices */ + if ((p_dev->device_no >= 2) && (function == 0)) + goto err_free; + + /* max of 4 devices overall */ + if (p_dev->device_no >= 4) + goto err_free; + + p_dev->socket = s; p_dev->func = function; p_dev->dev.bus = &pcmcia_bus_type; @@ -538,12 +521,12 @@ struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int fu goto err_free; dev_dbg(&p_dev->dev, "devname is %s\n", p_dev->devname); - spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + mutex_lock(&s->ops_mutex); /* * 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. + * Note that this is serialized by ops_mutex, 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) { @@ -556,41 +539,48 @@ struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int fu /* Add to the list in pcmcia_bus_socket */ list_add(&p_dev->socket_device_list, &s->devices_list); - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + if (pcmcia_setup_irq(p_dev)) + dev_warn(&p_dev->dev, + "IRQ setup failed -- device might not work\n"); if (!p_dev->function_config) { dev_dbg(&p_dev->dev, "creating config_t\n"); p_dev->function_config = kzalloc(sizeof(struct config_t), GFP_KERNEL); - if (!p_dev->function_config) + if (!p_dev->function_config) { + mutex_unlock(&s->ops_mutex); goto err_unreg; + } kref_init(&p_dev->function_config->ref); } + mutex_unlock(&s->ops_mutex); dev_printk(KERN_NOTICE, &p_dev->dev, - "pcmcia: registering new device %s\n", - p_dev->devname); + "pcmcia: registering new device %s (IRQ: %d)\n", + p_dev->devname, p_dev->irq); pcmcia_device_query(p_dev); if (device_register(&p_dev->dev)) goto err_unreg; - mutex_unlock(&device_add_lock); - return p_dev; err_unreg: - spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + mutex_lock(&s->ops_mutex); list_del(&p_dev->socket_device_list); - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + mutex_unlock(&s->ops_mutex); err_free: + mutex_lock(&s->ops_mutex); + s->device_count--; + mutex_unlock(&s->ops_mutex); + + for (i = 0; i < 4; i++) + kfree(p_dev->prod_id[i]); kfree(p_dev->devname); kfree(p_dev); - s->device_count--; err_put: - mutex_unlock(&device_add_lock); pcmcia_put_socket(s); return NULL; @@ -601,19 +591,23 @@ static int pcmcia_card_add(struct pcmcia_socket *s) { cistpl_longlink_mfc_t mfc; unsigned int no_funcs, i, no_chains; - int ret = 0; + int ret = -EAGAIN; + mutex_lock(&s->ops_mutex); if (!(s->resource_setup_done)) { dev_dbg(&s->dev, "no resources available, delaying card_add\n"); + mutex_unlock(&s->ops_mutex); return -EAGAIN; /* try again, but later... */ } if (pcmcia_validate_mem(s)) { dev_dbg(&s->dev, "validating mem resources failed, " "delaying card_add\n"); + mutex_unlock(&s->ops_mutex); return -EAGAIN; /* try again, but later... */ } + mutex_unlock(&s->ops_mutex); ret = pccard_validate_cis(s, &no_chains); if (ret || !no_chains) { @@ -634,17 +628,7 @@ static int pcmcia_card_add(struct pcmcia_socket *s) } -static void pcmcia_delayed_add_device(struct work_struct *work) -{ - struct pcmcia_socket *s = - container_of(work, struct pcmcia_socket, device_add); - dev_dbg(&s->dev, "adding additional device to %d\n", s->sock); - pcmcia_device_add(s, s->pcmcia_state.mfc_pfc); - s->pcmcia_state.device_add_pending = 0; - s->pcmcia_state.mfc_pfc = 0; -} - -static int pcmcia_requery(struct device *dev, void * _data) +static int pcmcia_requery_callback(struct device *dev, void * _data) { struct pcmcia_device *p_dev = to_pcmcia_dev(dev); if (!p_dev->dev.driver) { @@ -655,45 +639,58 @@ static int pcmcia_requery(struct device *dev, void * _data) return 0; } -static void pcmcia_bus_rescan(struct pcmcia_socket *skt, int new_cis) -{ - int no_devices = 0; - int ret = 0; - unsigned long flags; - - /* must be called with skt_mutex held */ - dev_dbg(&skt->dev, "re-scanning socket %d\n", skt->sock); - - spin_lock_irqsave(&pcmcia_dev_list_lock, flags); - if (list_empty(&skt->devices_list)) - no_devices = 1; - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); - /* If this is because of a CIS override, start over */ - if (new_cis && !no_devices) - pcmcia_card_remove(skt, NULL); +static void pcmcia_requery(struct pcmcia_socket *s) +{ + int has_pfc; - /* if no devices were added for this socket yet because of - * missing resource information or other trouble, we need to - * do this now. */ - if (no_devices || new_cis) { - ret = pcmcia_card_add(skt); - if (ret) - return; + if (s->functions == 0) { + pcmcia_card_add(s); + return; } /* some device information might have changed because of a CIS * update or because we can finally read it correctly... so * determine it again, overwriting old values if necessary. */ - bus_for_each_dev(&pcmcia_bus_type, NULL, NULL, pcmcia_requery); + bus_for_each_dev(&pcmcia_bus_type, NULL, NULL, pcmcia_requery_callback); + + /* if the CIS changed, we need to check whether the number of + * functions changed. */ + if (s->fake_cis) { + int old_funcs, new_funcs; + cistpl_longlink_mfc_t mfc; + + /* does this cis override add or remove functions? */ + old_funcs = s->functions; + + if (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_LONGLINK_MFC, + &mfc)) + new_funcs = mfc.nfn; + else + new_funcs = 1; + if (old_funcs != new_funcs) { + /* we need to re-start */ + pcmcia_card_remove(s, NULL); + pcmcia_card_add(s); + } + } + + /* If the PCMCIA device consists of two pseudo devices, + * call pcmcia_device_add() -- which will fail if both + * devices are already registered. */ + mutex_lock(&s->ops_mutex); + has_pfc = s->pcmcia_state.has_pfc; + mutex_unlock(&s->ops_mutex); + if (has_pfc) + pcmcia_device_add(s, 0); /* we re-scan all devices, not just the ones connected to this * socket. This does not matter, though. */ - ret = bus_rescan_devices(&pcmcia_bus_type); - if (ret) - printk(KERN_INFO "pcmcia: bus_rescan_devices failed\n"); + if (bus_rescan_devices(&pcmcia_bus_type)) + dev_warn(&s->dev, "rescanning the bus failed\n"); } + #ifdef CONFIG_PCMCIA_LOAD_CIS /** @@ -710,9 +707,8 @@ static int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename) struct pcmcia_socket *s = dev->socket; const struct firmware *fw; int ret = -ENOMEM; - int no_funcs; - int old_funcs; cistpl_longlink_mfc_t mfc; + int old_funcs, new_funcs = 1; if (!filename) return -EINVAL; @@ -735,23 +731,20 @@ static int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename) goto release; } + /* we need to re-start if the number of functions changed */ + old_funcs = s->functions; + if (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_LONGLINK_MFC, + &mfc)) + new_funcs = mfc.nfn; + + if (old_funcs != new_funcs) + ret = -EBUSY; /* update information */ pcmcia_device_query(dev); - /* does this cis override add or remove functions? */ - old_funcs = s->functions; - - if (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_LONGLINK_MFC, &mfc)) - no_funcs = mfc.nfn; - else - no_funcs = 1; - s->functions = no_funcs; - - if (old_funcs > no_funcs) - pcmcia_card_remove(s, dev); - else if (no_funcs > old_funcs) - pcmcia_add_device_later(s, 1); + /* requery (as number of functions might have changed) */ + pcmcia_parse_uevents(s, PCMCIA_UEVENT_REQUERY); } release: release_firmware(fw); @@ -816,17 +809,23 @@ static inline int pcmcia_devmatch(struct pcmcia_device *dev, } if (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) { + dev_dbg(&dev->dev, "this is a pseudo-multi-function device\n"); + mutex_lock(&dev->socket->ops_mutex); + dev->socket->pcmcia_state.has_pfc = 1; + mutex_unlock(&dev->socket->ops_mutex); if (dev->device_no != did->device_no) return 0; } if (did->match_flags & PCMCIA_DEV_ID_MATCH_FUNC_ID) { + int ret; + if ((!dev->has_func_id) || (dev->func_id != did->func_id)) return 0; /* if this is a pseudo-multi-function device, * we need explicit matches */ - if (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) + if (dev->socket->pcmcia_state.has_pfc) return 0; if (dev->device_no) return 0; @@ -835,19 +834,22 @@ static inline int pcmcia_devmatch(struct pcmcia_device *dev, * after it has re-checked that there is no possible module * with a prod_id/manf_id/card_id match. */ - dev_dbg(&dev->dev, - "skipping FUNC_ID match until userspace interaction\n"); - if (!dev->allow_func_id_match) + mutex_lock(&dev->socket->ops_mutex); + ret = dev->allow_func_id_match; + mutex_unlock(&dev->socket->ops_mutex); + + if (!ret) { + dev_dbg(&dev->dev, + "skipping FUNC_ID match until userspace ACK\n"); return 0; + } } if (did->match_flags & PCMCIA_DEV_ID_MATCH_FAKE_CIS) { dev_dbg(&dev->dev, "device needs a fake CIS\n"); if (!dev->socket->fake_cis) - pcmcia_load_firmware(dev, did->cisfile); - - if (!dev->socket->fake_cis) - return 0; + if (pcmcia_load_firmware(dev, did->cisfile)) + return 0; } if (did->match_flags & PCMCIA_DEV_ID_MATCH_ANONYMOUS) { @@ -859,8 +861,6 @@ static inline int pcmcia_devmatch(struct pcmcia_device *dev, return 0; } - dev_set_drvdata(&dev->dev, did); - return 1; } @@ -873,16 +873,16 @@ static int pcmcia_bus_match(struct device *dev, struct device_driver *drv) struct pcmcia_dynid *dynid; /* match dynamic devices first */ - spin_lock(&p_drv->dynids.lock); + mutex_lock(&p_drv->dynids.lock); list_for_each_entry(dynid, &p_drv->dynids.list, node) { dev_dbg(dev, "trying to match to %s\n", drv->name); if (pcmcia_devmatch(p_dev, &dynid->id)) { dev_dbg(dev, "matched to %s\n", drv->name); - spin_unlock(&p_drv->dynids.lock); + mutex_unlock(&p_drv->dynids.lock); return 1; } } - spin_unlock(&p_drv->dynids.lock); + mutex_unlock(&p_drv->dynids.lock); #ifdef CONFIG_PCMCIA_IOCTL /* matching by cardmgr */ @@ -964,19 +964,20 @@ static int runtime_suspend(struct device *dev) { int rc; - down(&dev->sem); + device_lock(dev); rc = pcmcia_dev_suspend(dev, PMSG_SUSPEND); - up(&dev->sem); + device_unlock(dev); return rc; } -static void runtime_resume(struct device *dev) +static int runtime_resume(struct device *dev) { int rc; - down(&dev->sem); + device_lock(dev); rc = pcmcia_dev_resume(dev); - up(&dev->sem); + device_unlock(dev); + return rc; } /************************ per-device sysfs output ***************************/ @@ -1027,7 +1028,7 @@ static ssize_t pcmcia_store_pm_state(struct device *dev, struct device_attribute if ((!p_dev->suspended) && !strncmp(buf, "off", 3)) ret = runtime_suspend(dev); else if (p_dev->suspended && !strncmp(buf, "on", 2)) - runtime_resume(dev); + ret = runtime_resume(dev); return ret ? ret : count; } @@ -1059,19 +1060,14 @@ static ssize_t pcmcia_store_allow_func_id_match(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct pcmcia_device *p_dev = to_pcmcia_dev(dev); - int ret; if (!count) return -EINVAL; - mutex_lock(&p_dev->socket->skt_mutex); + mutex_lock(&p_dev->socket->ops_mutex); p_dev->allow_func_id_match = 1; - mutex_unlock(&p_dev->socket->skt_mutex); - - ret = bus_rescan_devices(&pcmcia_bus_type); - if (ret) - printk(KERN_INFO "pcmcia: bus_rescan_devices failed after " - "allowing func_id matches\n"); + mutex_unlock(&p_dev->socket->ops_mutex); + pcmcia_parse_uevents(p_dev->socket, PCMCIA_UEVENT_REQUERY); return count; } @@ -1099,8 +1095,13 @@ static int pcmcia_dev_suspend(struct device *dev, pm_message_t state) struct pcmcia_driver *p_drv = NULL; int ret = 0; - if (p_dev->suspended) + mutex_lock(&p_dev->socket->ops_mutex); + if (p_dev->suspended) { + mutex_unlock(&p_dev->socket->ops_mutex); return 0; + } + p_dev->suspended = 1; + mutex_unlock(&p_dev->socket->ops_mutex); dev_dbg(dev, "suspending\n"); @@ -1117,6 +1118,9 @@ static int pcmcia_dev_suspend(struct device *dev, pm_message_t state) "pcmcia: device %s (driver %s) did " "not want to go to sleep (%d)\n", p_dev->devname, p_drv->drv.name, ret); + mutex_lock(&p_dev->socket->ops_mutex); + p_dev->suspended = 0; + mutex_unlock(&p_dev->socket->ops_mutex); goto out; } } @@ -1127,8 +1131,6 @@ static int pcmcia_dev_suspend(struct device *dev, pm_message_t state) } out: - if (!ret) - p_dev->suspended = 1; return ret; } @@ -1139,8 +1141,13 @@ static int pcmcia_dev_resume(struct device *dev) struct pcmcia_driver *p_drv = NULL; int ret = 0; - if (!p_dev->suspended) + mutex_lock(&p_dev->socket->ops_mutex); + if (!p_dev->suspended) { + mutex_unlock(&p_dev->socket->ops_mutex); return 0; + } + p_dev->suspended = 0; + mutex_unlock(&p_dev->socket->ops_mutex); dev_dbg(dev, "resuming\n"); @@ -1161,8 +1168,6 @@ static int pcmcia_dev_resume(struct device *dev) ret = p_drv->resume(p_dev); out: - if (!ret) - p_dev->suspended = 0; return ret; } @@ -1237,13 +1242,21 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) switch (event) { case CS_EVENT_CARD_REMOVAL: - s->pcmcia_state.present = 0; + atomic_set(&skt->present, 0); pcmcia_card_remove(skt, NULL); handle_event(skt, event); + mutex_lock(&s->ops_mutex); + destroy_cis_cache(s); + pcmcia_cleanup_irq(s); + mutex_unlock(&s->ops_mutex); break; case CS_EVENT_CARD_INSERTION: - s->pcmcia_state.present = 1; + atomic_set(&skt->present, 1); + mutex_lock(&s->ops_mutex); + s->pcmcia_state.has_pfc = 0; + destroy_cis_cache(s); /* to be on the safe side... */ + mutex_unlock(&s->ops_mutex); pcmcia_card_add(skt); handle_event(skt, event); break; @@ -1251,8 +1264,25 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) case CS_EVENT_EJECTION_REQUEST: break; - case CS_EVENT_PM_SUSPEND: case CS_EVENT_PM_RESUME: + if (verify_cis_cache(skt) != 0) { + dev_dbg(&skt->dev, "cis mismatch - different card\n"); + /* first, remove the card */ + ds_event(skt, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH); + mutex_lock(&s->ops_mutex); + destroy_cis_cache(skt); + kfree(skt->fake_cis); + skt->fake_cis = NULL; + s->functions = 0; + mutex_unlock(&s->ops_mutex); + /* now, add the new card */ + ds_event(skt, CS_EVENT_CARD_INSERTION, + CS_EVENT_PRI_LOW); + } + handle_event(skt, event); + break; + + case CS_EVENT_PM_SUSPEND: case CS_EVENT_RESET_PHYSICAL: case CS_EVENT_CARD_RESET: default: @@ -1265,7 +1295,13 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) return 0; } /* ds_event */ - +/* + * NOTE: This is racy. There's no guarantee the card will still be + * physically present, even if the call to this function returns + * non-NULL. Furthermore, the device driver most likely is unbound + * almost immediately, so the timeframe where pcmcia_dev_present + * returns NULL is probably really really small. + */ struct pcmcia_device *pcmcia_dev_present(struct pcmcia_device *_p_dev) { struct pcmcia_device *p_dev; @@ -1275,17 +1311,9 @@ struct pcmcia_device *pcmcia_dev_present(struct pcmcia_device *_p_dev) if (!p_dev) return NULL; - if (!p_dev->socket->pcmcia_state.present) - goto out; - - if (p_dev->_removed) - goto out; + if (atomic_read(&p_dev->socket->present) != 0) + ret = p_dev; - if (p_dev->suspended) - goto out; - - ret = p_dev; - out: pcmcia_put_dev(p_dev); return ret; } @@ -1295,7 +1323,8 @@ EXPORT_SYMBOL(pcmcia_dev_present); static struct pcmcia_callback pcmcia_bus_callback = { .owner = THIS_MODULE, .event = ds_event, - .requery = pcmcia_bus_rescan, + .requery = pcmcia_requery, + .validate = pccard_validate_cis, .suspend = pcmcia_bus_suspend, .resume = pcmcia_bus_resume, }; @@ -1313,17 +1342,17 @@ static int __devinit pcmcia_bus_add_socket(struct device *dev, return -ENODEV; } - /* - * Ugly. But we want to wait for the socket threads to have started up. - * We really should let the drivers themselves drive some of this.. - */ - msleep(250); + ret = sysfs_create_bin_file(&dev->kobj, &pccard_cis_attr); + if (ret) { + dev_printk(KERN_ERR, dev, "PCMCIA registration failed\n"); + pcmcia_put_socket(socket); + return ret; + } #ifdef CONFIG_PCMCIA_IOCTL init_waitqueue_head(&socket->queue); #endif INIT_LIST_HEAD(&socket->devices_list); - INIT_WORK(&socket->device_add, pcmcia_delayed_add_device); memset(&socket->pcmcia_state, 0, sizeof(u8)); socket->device_count = 0; @@ -1334,6 +1363,8 @@ static int __devinit pcmcia_bus_add_socket(struct device *dev, return ret; } + atomic_set(&socket->present, 0); + return 0; } @@ -1345,14 +1376,16 @@ static void pcmcia_bus_remove_socket(struct device *dev, if (!socket) return; - socket->pcmcia_state.dead = 1; pccard_register_pcmcia(socket, NULL); /* unregister any unbound devices */ mutex_lock(&socket->skt_mutex); pcmcia_card_remove(socket, NULL); + release_cis_mem(socket); mutex_unlock(&socket->skt_mutex); + sysfs_remove_bin_file(&dev->kobj, &pccard_cis_attr); + pcmcia_put_socket(socket); return; @@ -1383,8 +1416,6 @@ static int __init init_pcmcia_bus(void) { int ret; - spin_lock_init(&pcmcia_dev_list_lock); - ret = bus_register(&pcmcia_bus_type); if (ret < 0) { printk(KERN_WARNING "pcmcia: bus_register error: %d\n", ret); diff --git a/drivers/pcmcia/electra_cf.c b/drivers/pcmcia/electra_cf.c index d187ba4c5e0e..f94d8281cfb0 100644 --- a/drivers/pcmcia/electra_cf.c +++ b/drivers/pcmcia/electra_cf.c @@ -31,6 +31,7 @@ #include <linux/mm.h> #include <linux/vmalloc.h> #include <linux/of_platform.h> +#include <linux/slab.h> #include <pcmcia/ss.h> @@ -184,7 +185,7 @@ static int __devinit electra_cf_probe(struct of_device *ofdev, const struct of_device_id *match) { struct device *device = &ofdev->dev; - struct device_node *np = ofdev->node; + struct device_node *np = ofdev->dev.of_node; struct electra_cf_socket *cf; struct resource mem, io; int status; @@ -347,7 +348,7 @@ static int __devexit electra_cf_remove(struct of_device *ofdev) return 0; } -static struct of_device_id electra_cf_match[] = { +static const struct of_device_id electra_cf_match[] = { { .compatible = "electra-cf", }, @@ -356,8 +357,11 @@ static struct of_device_id electra_cf_match[] = { MODULE_DEVICE_TABLE(of, electra_cf_match); static struct of_platform_driver electra_cf_driver = { - .name = (char *)driver_name, - .match_table = electra_cf_match, + .driver = { + .name = (char *)driver_name, + .owner = THIS_MODULE, + .of_match_table = electra_cf_match, + }, .probe = electra_cf_probe, .remove = electra_cf_remove, }; diff --git a/drivers/pcmcia/i82092.c b/drivers/pcmcia/i82092.c index a04f21c8170f..3003bb3dfcc0 100644 --- a/drivers/pcmcia/i82092.c +++ b/drivers/pcmcia/i82092.c @@ -39,27 +39,11 @@ static struct pci_device_id i82092aa_pci_ids[] = { }; MODULE_DEVICE_TABLE(pci, i82092aa_pci_ids); -#ifdef CONFIG_PM -static int i82092aa_socket_suspend (struct pci_dev *dev, pm_message_t state) -{ - return pcmcia_socket_dev_suspend(&dev->dev); -} - -static int i82092aa_socket_resume (struct pci_dev *dev) -{ - return pcmcia_socket_dev_resume(&dev->dev); -} -#endif - static struct pci_driver i82092aa_pci_driver = { .name = "i82092aa", .id_table = i82092aa_pci_ids, .probe = i82092aa_pci_probe, .remove = __devexit_p(i82092aa_pci_remove), -#ifdef CONFIG_PM - .suspend = i82092aa_socket_suspend, - .resume = i82092aa_socket_resume, -#endif }; @@ -133,6 +117,7 @@ static int __devinit i82092aa_pci_probe(struct pci_dev *dev, const struct pci_de sockets[i].socket.map_size = 0x1000; sockets[i].socket.irq_mask = 0; sockets[i].socket.pci_irq = dev->irq; + sockets[i].socket.cb_dev = dev; sockets[i].socket.owner = THIS_MODULE; sockets[i].number = i; diff --git a/drivers/pcmcia/i82365.c b/drivers/pcmcia/i82365.c index c13fd9360511..9e2a15628de5 100644 --- a/drivers/pcmcia/i82365.c +++ b/drivers/pcmcia/i82365.c @@ -40,7 +40,6 @@ #include <linux/kernel.h> #include <linux/errno.h> #include <linux/timer.h> -#include <linux/slab.h> #include <linux/ioport.h> #include <linux/delay.h> #include <linux/workqueue.h> @@ -1223,16 +1222,7 @@ static int pcic_init(struct pcmcia_socket *s) return 0; } -static int i82365_drv_pcmcia_suspend(struct platform_device *dev, - pm_message_t state) -{ - return pcmcia_socket_dev_suspend(&dev->dev); -} -static int i82365_drv_pcmcia_resume(struct platform_device *dev) -{ - return pcmcia_socket_dev_resume(&dev->dev); -} static struct pccard_operations pcic_operations = { .init = pcic_init, .get_status = pcic_get_status, @@ -1248,8 +1238,6 @@ static struct platform_driver i82365_driver = { .name = "i82365", .owner = THIS_MODULE, }, - .suspend = i82365_drv_pcmcia_suspend, - .resume = i82365_drv_pcmcia_resume, }; static struct platform_device *i82365_device; diff --git a/drivers/pcmcia/i82365.h b/drivers/pcmcia/i82365.h index 622860c689d9..3f84d7a2dc84 100644 --- a/drivers/pcmcia/i82365.h +++ b/drivers/pcmcia/i82365.h @@ -77,8 +77,8 @@ #define I365_VPP2_5V 0x04 /* Vpp2 = 5.0v */ #define I365_VPP2_12V 0x08 /* Vpp2 = 12.0v */ #define I365_VPP1_MASK 0x03 /* Mask for turning off Vpp1 */ -#define I365_VPP1_5V 0x01 /* Vpp2 = 5.0v */ -#define I365_VPP1_12V 0x02 /* Vpp2 = 12.0v */ +#define I365_VPP1_5V 0x01 /* Vpp1 = 5.0v */ +#define I365_VPP1_12V 0x02 /* Vpp1 = 12.0v */ /* Flags for I365_INTCTL */ #define I365_RING_ENA 0x80 @@ -95,6 +95,7 @@ #define I365_CSC_DETECT 0x08 #define I365_CSC_ANY 0x0F #define I365_CSC_GPI 0x10 +#define I365_CSC_IRQ_MASK 0xF0 /* Flags for I365_ADDRWIN */ #define I365_ENA_IO(map) (0x40 << (map)) diff --git a/drivers/pcmcia/m32r_cfc.c b/drivers/pcmcia/m32r_cfc.c index 26a621c9e2fc..7e16ed8eb0a4 100644 --- a/drivers/pcmcia/m32r_cfc.c +++ b/drivers/pcmcia/m32r_cfc.c @@ -16,7 +16,6 @@ #include <linux/kernel.h> #include <linux/errno.h> #include <linux/timer.h> -#include <linux/slab.h> #include <linux/ioport.h> #include <linux/delay.h> #include <linux/workqueue.h> @@ -685,16 +684,7 @@ static struct pccard_operations pcc_operations = { .set_mem_map = pcc_set_mem_map, }; -static int cfc_drv_pcmcia_suspend(struct platform_device *dev, - pm_message_t state) -{ - return pcmcia_socket_dev_suspend(&dev->dev); -} -static int cfc_drv_pcmcia_resume(struct platform_device *dev) -{ - return pcmcia_socket_dev_resume(&dev->dev); -} /*====================================================================*/ static struct platform_driver pcc_driver = { @@ -702,8 +692,6 @@ static struct platform_driver pcc_driver = { .name = "cfc", .owner = THIS_MODULE, }, - .suspend = cfc_drv_pcmcia_suspend, - .resume = cfc_drv_pcmcia_resume, }; static struct platform_device pcc_device = { @@ -764,7 +752,7 @@ static int __init init_m32r_pcc(void) for (i = 0 ; i < pcc_sockets ; i++) { socket[i].socket.dev.parent = &pcc_device.dev; socket[i].socket.ops = &pcc_operations; - socket[i].socket.resource_ops = &pccard_nonstatic_ops; + socket[i].socket.resource_ops = &pccard_static_ops; socket[i].socket.owner = THIS_MODULE; socket[i].number = i; ret = pcmcia_register_socket(&socket[i].socket); diff --git a/drivers/pcmcia/m32r_pcc.c b/drivers/pcmcia/m32r_pcc.c index 72844c5a6d05..6c5c3f910d71 100644 --- a/drivers/pcmcia/m32r_pcc.c +++ b/drivers/pcmcia/m32r_pcc.c @@ -16,7 +16,6 @@ #include <linux/kernel.h> #include <linux/errno.h> #include <linux/timer.h> -#include <linux/slab.h> #include <linux/ioport.h> #include <linux/delay.h> #include <linux/workqueue.h> @@ -663,16 +662,6 @@ static struct pccard_operations pcc_operations = { .set_mem_map = pcc_set_mem_map, }; -static int pcc_drv_pcmcia_suspend(struct platform_device *dev, - pm_message_t state) -{ - return pcmcia_socket_dev_suspend(&dev->dev); -} - -static int pcc_drv_pcmcia_resume(struct platform_device *dev) -{ - return pcmcia_socket_dev_resume(&dev->dev); -} /*====================================================================*/ static struct platform_driver pcc_driver = { @@ -680,8 +669,6 @@ static struct platform_driver pcc_driver = { .name = "pcc", .owner = THIS_MODULE, }, - .suspend = pcc_drv_pcmcia_suspend, - .resume = pcc_drv_pcmcia_resume, }; static struct platform_device pcc_device = { diff --git a/drivers/pcmcia/m8xx_pcmcia.c b/drivers/pcmcia/m8xx_pcmcia.c index 7f79c4e169ae..25e5e30a18af 100644 --- a/drivers/pcmcia/m8xx_pcmcia.c +++ b/drivers/pcmcia/m8xx_pcmcia.c @@ -42,7 +42,6 @@ #include <linux/kernel.h> #include <linux/errno.h> -#include <linux/slab.h> #include <linux/timer.h> #include <linux/ioport.h> #include <linux/delay.h> @@ -1158,7 +1157,7 @@ static int __init m8xx_probe(struct of_device *ofdev, unsigned int i, m, hwirq; pcmconf8xx_t *pcmcia; int status; - struct device_node *np = ofdev->node; + struct device_node *np = ofdev->dev.of_node; pcmcia_info("%s\n", version); @@ -1233,7 +1232,7 @@ static int __init m8xx_probe(struct of_device *ofdev, socket[i].socket.io_offset = 0; socket[i].socket.pci_irq = pcmcia_schlvl; socket[i].socket.ops = &m8xx_services; - socket[i].socket.resource_ops = &pccard_nonstatic_ops; + socket[i].socket.resource_ops = &pccard_iodyn_ops; socket[i].socket.cb_dev = NULL; socket[i].socket.dev.parent = &ofdev->dev; socket[i].pcmcia = pcmcia; @@ -1288,22 +1287,7 @@ static int m8xx_remove(struct of_device *ofdev) return 0; } -#ifdef CONFIG_PM -static int m8xx_suspend(struct platform_device *pdev, pm_message_t state) -{ - return pcmcia_socket_dev_suspend(&pdev->dev); -} - -static int m8xx_resume(struct platform_device *pdev) -{ - return pcmcia_socket_dev_resume(&pdev->dev); -} -#else -#define m8xx_suspend NULL -#define m8xx_resume NULL -#endif - -static struct of_device_id m8xx_pcmcia_match[] = { +static const struct of_device_id m8xx_pcmcia_match[] = { { .type = "pcmcia", .compatible = "fsl,pq-pcmcia", @@ -1314,12 +1298,13 @@ static struct of_device_id m8xx_pcmcia_match[] = { MODULE_DEVICE_TABLE(of, m8xx_pcmcia_match); static struct of_platform_driver m8xx_pcmcia_driver = { - .name = driver_name, - .match_table = m8xx_pcmcia_match, + .driver = { + .name = driver_name, + .owner = THIS_MODULE, + .of_match_table = m8xx_pcmcia_match, + }, .probe = m8xx_probe, .remove = m8xx_remove, - .suspend = m8xx_suspend, - .resume = m8xx_resume, }; static int __init m8xx_init(void) diff --git a/drivers/pcmcia/o2micro.h b/drivers/pcmcia/o2micro.h index 624442fc0d35..e74bebac2695 100644 --- a/drivers/pcmcia/o2micro.h +++ b/drivers/pcmcia/o2micro.h @@ -116,13 +116,12 @@ static int o2micro_override(struct yenta_socket *socket) * from Eric Still, 02Micro. */ u8 a, b; + bool use_speedup; if (PCI_FUNC(socket->dev->devfn) == 0) { a = config_readb(socket, O2_RESERVED1); b = config_readb(socket, O2_RESERVED2); - - dev_printk(KERN_INFO, &socket->dev->dev, - "O2: res at 0x94/0xD4: %02x/%02x\n", a, b); + dev_dbg(&socket->dev->dev, "O2: 0x94/0xD4: %02x/%02x\n", a, b); switch (socket->dev->device) { /* @@ -135,23 +134,37 @@ static int o2micro_override(struct yenta_socket *socket) case PCI_DEVICE_ID_O2_6812: case PCI_DEVICE_ID_O2_6832: case PCI_DEVICE_ID_O2_6836: - case PCI_DEVICE_ID_O2_6933: - dev_printk(KERN_INFO, &socket->dev->dev, - "Yenta O2: old bridge, disabling read " - "prefetch/write burst\n"); - config_writeb(socket, O2_RESERVED1, - a & ~(O2_RES_READ_PREFETCH | O2_RES_WRITE_BURST)); - config_writeb(socket, O2_RESERVED2, - b & ~(O2_RES_READ_PREFETCH | O2_RES_WRITE_BURST)); + case PCI_DEVICE_ID_O2_6933: + use_speedup = false; break; - default: - dev_printk(KERN_INFO , &socket->dev->dev, - "O2: enabling read prefetch/write burst\n"); + use_speedup = true; + break; + } + + /* the user may override our decision */ + if (strcasecmp(o2_speedup, "on") == 0) + use_speedup = true; + else if (strcasecmp(o2_speedup, "off") == 0) + use_speedup = false; + else if (strcasecmp(o2_speedup, "default") != 0) + dev_warn(&socket->dev->dev, + "O2: Unknown parameter, using 'default'"); + + if (use_speedup) { + dev_info(&socket->dev->dev, + "O2: enabling read prefetch/write burst\n"); + config_writeb(socket, O2_RESERVED1, + a | O2_RES_READ_PREFETCH | O2_RES_WRITE_BURST); + config_writeb(socket, O2_RESERVED2, + b | O2_RES_READ_PREFETCH | O2_RES_WRITE_BURST); + } else { + dev_info(&socket->dev->dev, + "O2: disabling read prefetch/write burst\n"); config_writeb(socket, O2_RESERVED1, - a | O2_RES_READ_PREFETCH | O2_RES_WRITE_BURST); + a & ~(O2_RES_READ_PREFETCH | O2_RES_WRITE_BURST)); config_writeb(socket, O2_RESERVED2, - b | O2_RES_READ_PREFETCH | O2_RES_WRITE_BURST); + b & ~(O2_RES_READ_PREFETCH | O2_RES_WRITE_BURST)); } } diff --git a/drivers/pcmcia/omap_cf.c b/drivers/pcmcia/omap_cf.c index 663781d20129..0ad06a3bd562 100644 --- a/drivers/pcmcia/omap_cf.c +++ b/drivers/pcmcia/omap_cf.c @@ -16,6 +16,7 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/interrupt.h> +#include <linux/slab.h> #include <pcmcia/ss.h> @@ -71,8 +72,6 @@ struct omap_cf_socket { #define POLL_INTERVAL (2 * HZ) -#define SZ_2K (2 * SZ_1K) - /*--------------------------------------------------------------------------*/ static int omap_cf_ss_init(struct pcmcia_socket *s) @@ -118,7 +117,7 @@ static int omap_cf_get_status(struct pcmcia_socket *s, u_int *sp) *sp = SS_READY | SS_DETECT | SS_POWERON | SS_3VCARD; cf = container_of(s, struct omap_cf_socket, socket); - s->irq.AssignedIRQ = 0; + s->pcmcia_irq = 0; s->pci_irq = cf->irq; } else *sp = 0; @@ -332,24 +331,12 @@ static int __exit omap_cf_remove(struct platform_device *pdev) return 0; } -static int omap_cf_suspend(struct platform_device *pdev, pm_message_t mesg) -{ - return pcmcia_socket_dev_suspend(&pdev->dev); -} - -static int omap_cf_resume(struct platform_device *pdev) -{ - return pcmcia_socket_dev_resume(&pdev->dev); -} - static struct platform_driver omap_cf_driver = { .driver = { .name = (char *) driver_name, .owner = THIS_MODULE, }, .remove = __exit_p(omap_cf_remove), - .suspend = omap_cf_suspend, - .resume = omap_cf_resume, }; static int __init omap_cf_init(void) diff --git a/drivers/pcmcia/pcmcia_cis.c b/drivers/pcmcia/pcmcia_cis.c new file mode 100644 index 000000000000..4a65eaf96b0a --- /dev/null +++ b/drivers/pcmcia/pcmcia_cis.c @@ -0,0 +1,356 @@ +/* + * PCMCIA high-level CIS access functions + * + * The initial developer of the original code is David A. Hinds + * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds + * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + * + * Copyright (C) 1999 David A. Hinds + * Copyright (C) 2004-2009 Dominik Brodowski + * + * 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/slab.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/netdevice.h> + +#include <pcmcia/cs_types.h> +#include <pcmcia/cisreg.h> +#include <pcmcia/cistpl.h> +#include <pcmcia/ss.h> +#include <pcmcia/cs.h> +#include <pcmcia/ds.h> +#include "cs_internal.h" + + +/** + * pccard_read_tuple() - internal CIS tuple access + * @s: the struct pcmcia_socket where the card is inserted + * @function: the device function we loop for + * @code: which CIS code shall we look for? + * @parse: buffer where the tuple shall be parsed (or NULL, if no parse) + * + * pccard_read_tuple() reads out one tuple and attempts to parse it + */ +int pccard_read_tuple(struct pcmcia_socket *s, unsigned int function, + cisdata_t code, void *parse) +{ + tuple_t tuple; + cisdata_t *buf; + int ret; + + buf = kmalloc(256, GFP_KERNEL); + if (buf == NULL) { + dev_printk(KERN_WARNING, &s->dev, "no memory to read tuple\n"); + return -ENOMEM; + } + tuple.DesiredTuple = code; + tuple.Attributes = 0; + if (function == BIND_FN_ALL) + tuple.Attributes = TUPLE_RETURN_COMMON; + ret = pccard_get_first_tuple(s, function, &tuple); + if (ret != 0) + goto done; + tuple.TupleData = buf; + tuple.TupleOffset = 0; + tuple.TupleDataMax = 255; + ret = pccard_get_tuple_data(s, &tuple); + if (ret != 0) + goto done; + ret = pcmcia_parse_tuple(&tuple, parse); +done: + kfree(buf); + return ret; +} + + +/** + * pccard_loop_tuple() - loop over tuples in the CIS + * @s: the struct pcmcia_socket where the card is inserted + * @function: the device function we loop for + * @code: which CIS code shall we look for? + * @parse: buffer where the tuple shall be parsed (or NULL, if no parse) + * @priv_data: private data to be passed to the loop_tuple function. + * @loop_tuple: function to call for each CIS entry of type @function. IT + * gets passed the raw tuple, the paresed tuple (if @parse is + * set) and @priv_data. + * + * pccard_loop_tuple() loops over all CIS entries of type @function, and + * calls the @loop_tuple function for each entry. If the call to @loop_tuple + * returns 0, the loop exits. Returns 0 on success or errorcode otherwise. + */ +int pccard_loop_tuple(struct pcmcia_socket *s, unsigned int function, + cisdata_t code, cisparse_t *parse, void *priv_data, + int (*loop_tuple) (tuple_t *tuple, + cisparse_t *parse, + void *priv_data)) +{ + tuple_t tuple; + cisdata_t *buf; + int ret; + + buf = kzalloc(256, GFP_KERNEL); + if (buf == NULL) { + dev_printk(KERN_WARNING, &s->dev, "no memory to read tuple\n"); + return -ENOMEM; + } + + tuple.TupleData = buf; + tuple.TupleDataMax = 255; + tuple.TupleOffset = 0; + tuple.DesiredTuple = code; + tuple.Attributes = 0; + + ret = pccard_get_first_tuple(s, function, &tuple); + while (!ret) { + if (pccard_get_tuple_data(s, &tuple)) + goto next_entry; + + if (parse) + if (pcmcia_parse_tuple(&tuple, parse)) + goto next_entry; + + ret = loop_tuple(&tuple, parse, priv_data); + if (!ret) + break; + +next_entry: + ret = pccard_get_next_tuple(s, function, &tuple); + } + + kfree(buf); + return ret; +} + +struct pcmcia_cfg_mem { + struct pcmcia_device *p_dev; + void *priv_data; + int (*conf_check) (struct pcmcia_device *p_dev, + cistpl_cftable_entry_t *cfg, + cistpl_cftable_entry_t *dflt, + unsigned int vcc, + void *priv_data); + cisparse_t parse; + cistpl_cftable_entry_t dflt; +}; + +/** + * pcmcia_do_loop_config() - internal helper for pcmcia_loop_config() + * + * pcmcia_do_loop_config() is the internal callback for the call from + * pcmcia_loop_config() to pccard_loop_tuple(). Data is transferred + * by a struct pcmcia_cfg_mem. + */ +static int pcmcia_do_loop_config(tuple_t *tuple, cisparse_t *parse, void *priv) +{ + cistpl_cftable_entry_t *cfg = &parse->cftable_entry; + struct pcmcia_cfg_mem *cfg_mem = priv; + + /* default values */ + cfg_mem->p_dev->conf.ConfigIndex = cfg->index; + if (cfg->flags & CISTPL_CFTABLE_DEFAULT) + cfg_mem->dflt = *cfg; + + return cfg_mem->conf_check(cfg_mem->p_dev, cfg, &cfg_mem->dflt, + cfg_mem->p_dev->socket->socket.Vcc, + cfg_mem->priv_data); +} + +/** + * pcmcia_loop_config() - loop over configuration options + * @p_dev: the struct pcmcia_device which we need to loop for. + * @conf_check: function to call for each configuration option. + * It gets passed the struct pcmcia_device, the CIS data + * describing the configuration option, and private data + * being passed to pcmcia_loop_config() + * @priv_data: private data to be passed to the conf_check function. + * + * pcmcia_loop_config() loops over all configuration options, and calls + * the driver-specific conf_check() for each one, checking whether + * it is a valid one. Returns 0 on success or errorcode otherwise. + */ +int pcmcia_loop_config(struct pcmcia_device *p_dev, + int (*conf_check) (struct pcmcia_device *p_dev, + cistpl_cftable_entry_t *cfg, + cistpl_cftable_entry_t *dflt, + unsigned int vcc, + void *priv_data), + void *priv_data) +{ + struct pcmcia_cfg_mem *cfg_mem; + int ret; + + cfg_mem = kzalloc(sizeof(struct pcmcia_cfg_mem), GFP_KERNEL); + if (cfg_mem == NULL) + return -ENOMEM; + + cfg_mem->p_dev = p_dev; + cfg_mem->conf_check = conf_check; + cfg_mem->priv_data = priv_data; + + ret = pccard_loop_tuple(p_dev->socket, p_dev->func, + CISTPL_CFTABLE_ENTRY, &cfg_mem->parse, + cfg_mem, pcmcia_do_loop_config); + + kfree(cfg_mem); + return ret; +} +EXPORT_SYMBOL(pcmcia_loop_config); + + +struct pcmcia_loop_mem { + struct pcmcia_device *p_dev; + void *priv_data; + int (*loop_tuple) (struct pcmcia_device *p_dev, + tuple_t *tuple, + void *priv_data); +}; + +/** + * pcmcia_do_loop_tuple() - internal helper for pcmcia_loop_config() + * + * pcmcia_do_loop_tuple() is the internal callback for the call from + * pcmcia_loop_tuple() to pccard_loop_tuple(). Data is transferred + * by a struct pcmcia_cfg_mem. + */ +static int pcmcia_do_loop_tuple(tuple_t *tuple, cisparse_t *parse, void *priv) +{ + struct pcmcia_loop_mem *loop = priv; + + return loop->loop_tuple(loop->p_dev, tuple, loop->priv_data); +}; + +/** + * pcmcia_loop_tuple() - loop over tuples in the CIS + * @p_dev: the struct pcmcia_device which we need to loop for. + * @code: which CIS code shall we look for? + * @priv_data: private data to be passed to the loop_tuple function. + * @loop_tuple: function to call for each CIS entry of type @function. IT + * gets passed the raw tuple and @priv_data. + * + * pcmcia_loop_tuple() loops over all CIS entries of type @function, and + * calls the @loop_tuple function for each entry. If the call to @loop_tuple + * returns 0, the loop exits. Returns 0 on success or errorcode otherwise. + */ +int pcmcia_loop_tuple(struct pcmcia_device *p_dev, cisdata_t code, + int (*loop_tuple) (struct pcmcia_device *p_dev, + tuple_t *tuple, + void *priv_data), + void *priv_data) +{ + struct pcmcia_loop_mem loop = { + .p_dev = p_dev, + .loop_tuple = loop_tuple, + .priv_data = priv_data}; + + return pccard_loop_tuple(p_dev->socket, p_dev->func, code, NULL, + &loop, pcmcia_do_loop_tuple); +} +EXPORT_SYMBOL(pcmcia_loop_tuple); + + +struct pcmcia_loop_get { + size_t len; + cisdata_t **buf; +}; + +/** + * pcmcia_do_get_tuple() - internal helper for pcmcia_get_tuple() + * + * pcmcia_do_get_tuple() is the internal callback for the call from + * pcmcia_get_tuple() to pcmcia_loop_tuple(). As we're only interested in + * the first tuple, return 0 unconditionally. Create a memory buffer large + * enough to hold the content of the tuple, and fill it with the tuple data. + * The caller is responsible to free the buffer. + */ +static int pcmcia_do_get_tuple(struct pcmcia_device *p_dev, tuple_t *tuple, + void *priv) +{ + struct pcmcia_loop_get *get = priv; + + *get->buf = kzalloc(tuple->TupleDataLen, GFP_KERNEL); + if (*get->buf) { + get->len = tuple->TupleDataLen; + memcpy(*get->buf, tuple->TupleData, tuple->TupleDataLen); + } else + dev_dbg(&p_dev->dev, "do_get_tuple: out of memory\n"); + return 0; +} + +/** + * pcmcia_get_tuple() - get first tuple from CIS + * @p_dev: the struct pcmcia_device which we need to loop for. + * @code: which CIS code shall we look for? + * @buf: pointer to store the buffer to. + * + * pcmcia_get_tuple() gets the content of the first CIS entry of type @code. + * It returns the buffer length (or zero). The caller is responsible to free + * the buffer passed in @buf. + */ +size_t pcmcia_get_tuple(struct pcmcia_device *p_dev, cisdata_t code, + unsigned char **buf) +{ + struct pcmcia_loop_get get = { + .len = 0, + .buf = buf, + }; + + *get.buf = NULL; + pcmcia_loop_tuple(p_dev, code, pcmcia_do_get_tuple, &get); + + return get.len; +} +EXPORT_SYMBOL(pcmcia_get_tuple); + + +/** + * pcmcia_do_get_mac() - internal helper for pcmcia_get_mac_from_cis() + * + * pcmcia_do_get_mac() is the internal callback for the call from + * pcmcia_get_mac_from_cis() to pcmcia_loop_tuple(). We check whether the + * tuple contains a proper LAN_NODE_ID of length 6, and copy the data + * to struct net_device->dev_addr[i]. + */ +static int pcmcia_do_get_mac(struct pcmcia_device *p_dev, tuple_t *tuple, + void *priv) +{ + struct net_device *dev = priv; + int i; + + if (tuple->TupleData[0] != CISTPL_FUNCE_LAN_NODE_ID) + return -EINVAL; + if (tuple->TupleDataLen < ETH_ALEN + 2) { + dev_warn(&p_dev->dev, "Invalid CIS tuple length for " + "LAN_NODE_ID\n"); + return -EINVAL; + } + + if (tuple->TupleData[1] != ETH_ALEN) { + dev_warn(&p_dev->dev, "Invalid header for LAN_NODE_ID\n"); + return -EINVAL; + } + for (i = 0; i < 6; i++) + dev->dev_addr[i] = tuple->TupleData[i+2]; + return 0; +} + +/** + * pcmcia_get_mac_from_cis() - read out MAC address from CISTPL_FUNCE + * @p_dev: the struct pcmcia_device for which we want the address. + * @dev: a properly prepared struct net_device to store the info to. + * + * pcmcia_get_mac_from_cis() reads out the hardware MAC address from + * CISTPL_FUNCE and stores it into struct net_device *dev->dev_addr which + * must be set up properly by the driver (see examples!). + */ +int pcmcia_get_mac_from_cis(struct pcmcia_device *p_dev, struct net_device *dev) +{ + return pcmcia_loop_tuple(p_dev, CISTPL_FUNCE, pcmcia_do_get_mac, dev); +} +EXPORT_SYMBOL(pcmcia_get_mac_from_cis); + diff --git a/drivers/pcmcia/pcmcia_ioctl.c b/drivers/pcmcia/pcmcia_ioctl.c index f73fd5beaa37..d007a2a03830 100644 --- a/drivers/pcmcia/pcmcia_ioctl.c +++ b/drivers/pcmcia/pcmcia_ioctl.c @@ -27,6 +27,7 @@ #include <linux/proc_fs.h> #include <linux/poll.h> #include <linux/pci.h> +#include <linux/slab.h> #include <linux/seq_file.h> #include <linux/smp_lock.h> #include <linux/workqueue.h> @@ -62,16 +63,15 @@ static struct pcmcia_device *get_pcmcia_device(struct pcmcia_socket *s, unsigned int function) { struct pcmcia_device *p_dev = NULL; - unsigned long flags; - spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + mutex_lock(&s->ops_mutex); list_for_each_entry(p_dev, &s->devices_list, socket_device_list) { if (p_dev->func == function) { - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + mutex_unlock(&s->ops_mutex); return pcmcia_get_dev(p_dev); } } - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + mutex_unlock(&s->ops_mutex); return NULL; } @@ -169,7 +169,6 @@ static int pcmcia_adjust_resource_info(adjust_t *adj) { struct pcmcia_socket *s; int ret = -ENOSYS; - unsigned long flags; down_read(&pcmcia_socket_list_rwsem); list_for_each_entry(s, &pcmcia_socket_list, socket_list) { @@ -182,14 +181,13 @@ static int pcmcia_adjust_resource_info(adjust_t *adj) /* you can't use the old interface if the new * one was used before */ - spin_lock_irqsave(&s->lock, flags); + mutex_lock(&s->ops_mutex); if ((s->resource_setup_new) && !(s->resource_setup_old)) { - spin_unlock_irqrestore(&s->lock, flags); + mutex_unlock(&s->ops_mutex); continue; } else if (!(s->resource_setup_old)) s->resource_setup_old = 1; - spin_unlock_irqrestore(&s->lock, flags); switch (adj->Resource) { case RES_MEMORY_RANGE: @@ -208,10 +206,9 @@ static int pcmcia_adjust_resource_info(adjust_t *adj) * last call to adjust_resource_info, we * always need to assume this is the latest * one... */ - spin_lock_irqsave(&s->lock, flags); s->resource_setup_done = 1; - spin_unlock_irqrestore(&s->lock, flags); } + mutex_unlock(&s->ops_mutex); } } up_read(&pcmcia_socket_list_rwsem); @@ -304,7 +301,9 @@ static int pccard_get_status(struct pcmcia_socket *s, (c->IntType & (INT_MEMORY_AND_IO | INT_ZOOMED_VIDEO))) { u_char reg; if (c->CardValues & PRESENT_PIN_REPLACE) { + mutex_lock(&s->ops_mutex); pcmcia_read_cis_mem(s, 1, (c->ConfigBase+CISREG_PRR)>>1, 1, ®); + mutex_unlock(&s->ops_mutex); status->CardState |= (reg & PRR_WP_STATUS) ? CS_EVENT_WRITE_PROTECT : 0; status->CardState |= @@ -318,7 +317,9 @@ static int pccard_get_status(struct pcmcia_socket *s, status->CardState |= CS_EVENT_READY_CHANGE; } if (c->CardValues & PRESENT_EXT_STATUS) { + mutex_lock(&s->ops_mutex); pcmcia_read_cis_mem(s, 1, (c->ConfigBase+CISREG_ESR)>>1, 1, ®); + mutex_unlock(&s->ops_mutex); status->CardState |= (reg & ESR_REQ_ATTN) ? CS_EVENT_REQUEST_ATTENTION : 0; } @@ -354,7 +355,7 @@ static int pccard_get_configuration_info(struct pcmcia_socket *s, if (s->state & SOCKET_CARDBUS_CONFIG) { config->Attributes = CONF_VALID_CLIENT; config->IntType = INT_CARDBUS; - config->AssignedIRQ = s->irq.AssignedIRQ; + config->AssignedIRQ = s->pcmcia_irq; if (config->AssignedIRQ) config->Attributes |= CONF_ENABLE_IRQ; if (s->io[0].res) { @@ -394,7 +395,7 @@ static int pccard_get_configuration_info(struct pcmcia_socket *s, config->ExtStatus = c->ExtStatus; config->Present = config->CardValues = c->CardValues; config->IRQAttributes = c->irq.Attributes; - config->AssignedIRQ = s->irq.AssignedIRQ; + config->AssignedIRQ = s->pcmcia_irq; config->BasePort1 = c->io.BasePort1; config->NumPorts1 = c->io.NumPorts1; config->Attributes1 = c->io.Attributes1; @@ -470,7 +471,6 @@ static int bind_request(struct pcmcia_socket *s, bind_info_t *bind_info) struct pcmcia_driver *p_drv; struct pcmcia_device *p_dev; int ret = 0; - unsigned long flags; s = pcmcia_get_socket(s); if (!s) @@ -490,7 +490,7 @@ static int bind_request(struct pcmcia_socket *s, bind_info_t *bind_info) goto err_put_driver; } - spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + mutex_lock(&s->ops_mutex); list_for_each_entry(p_dev, &s->devices_list, socket_device_list) { if (p_dev->func == bind_info->function) { if ((p_dev->dev.driver == &p_drv->drv)) { @@ -499,7 +499,7 @@ static int bind_request(struct pcmcia_socket *s, bind_info_t *bind_info) * registered, and it was registered * by userspace before, we need to * return the "instance". */ - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + mutex_unlock(&s->ops_mutex); bind_info->instance = p_dev; ret = -EBUSY; goto err_put_module; @@ -507,7 +507,7 @@ static int bind_request(struct pcmcia_socket *s, bind_info_t *bind_info) /* the correct driver managed to bind * itself magically to the correct * device. */ - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + mutex_unlock(&s->ops_mutex); p_dev->cardmgr = p_drv; ret = 0; goto err_put_module; @@ -516,12 +516,12 @@ static int bind_request(struct pcmcia_socket *s, bind_info_t *bind_info) /* there's already a device available where * no device has been bound to yet. So we don't * need to register a device! */ - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + mutex_unlock(&s->ops_mutex); goto rescan; } } } - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + mutex_unlock(&s->ops_mutex); p_dev = pcmcia_device_add(s, bind_info->function); if (!p_dev) { @@ -575,10 +575,8 @@ static struct pci_bus *pcmcia_lookup_bus(struct pcmcia_socket *s) static int get_device_info(struct pcmcia_socket *s, bind_info_t *bind_info, int first) { - dev_node_t *node; struct pcmcia_device *p_dev; struct pcmcia_driver *p_drv; - unsigned long flags; int ret = 0; #ifdef CONFIG_CARDBUS @@ -617,7 +615,7 @@ static int get_device_info(struct pcmcia_socket *s, bind_info_t *bind_info, int } #endif - spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + mutex_lock(&s->ops_mutex); list_for_each_entry(p_dev, &s->devices_list, socket_device_list) { if (p_dev->func == bind_info->function) { p_dev = pcmcia_get_dev(p_dev); @@ -626,11 +624,11 @@ static int get_device_info(struct pcmcia_socket *s, bind_info_t *bind_info, int goto found; } } - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + mutex_unlock(&s->ops_mutex); return -ENODEV; found: - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + mutex_unlock(&s->ops_mutex); p_drv = to_pcmcia_drv(p_dev->dev.driver); if (p_drv && !p_dev->_locked) { @@ -638,21 +636,13 @@ static int get_device_info(struct pcmcia_socket *s, bind_info_t *bind_info, int goto err_put; } - if (first) - node = p_dev->dev_node; - else - for (node = p_dev->dev_node; node; node = node->next) - if (node == bind_info->next) - break; - if (!node) { + if (!first) { ret = -ENODEV; goto err_put; } - strlcpy(bind_info->name, node->dev_name, DEV_NAME_LEN); - bind_info->major = node->major; - bind_info->minor = node->minor; - bind_info->next = node->next; + strlcpy(bind_info->name, dev_name(&p_dev->dev), DEV_NAME_LEN); + bind_info->next = NULL; err_put: pcmcia_put_dev(p_dev); @@ -716,7 +706,7 @@ static int ds_open(struct inode *inode, struct file *file) warning_printed = 1; } - if (s->pcmcia_state.present) + if (atomic_read(&s->present)) queue_event(user, CS_EVENT_CARD_INSERTION); out: unlock_kernel(); @@ -775,9 +765,6 @@ static ssize_t ds_read(struct file *file, char __user *buf, return -EIO; s = user->socket; - if (s->pcmcia_state.dead) - return -EIO; - ret = wait_event_interruptible(s->queue, !queue_empty(user)); if (ret == 0) ret = put_user(get_queued_event(user), (int __user *)buf) ? -EFAULT : 4; @@ -826,8 +813,7 @@ static u_int ds_poll(struct file *file, poll_table *wait) /*====================================================================*/ -static int ds_ioctl(struct inode *inode, struct file *file, - u_int cmd, u_long arg) +static int ds_ioctl(struct file *file, u_int cmd, u_long arg) { struct pcmcia_socket *s; void __user *uarg = (char __user *)arg; @@ -843,8 +829,6 @@ static int ds_ioctl(struct inode *inode, struct file *file, return -EIO; s = user->socket; - if (s->pcmcia_state.dead) - return -EIO; size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT; if (size > sizeof(ds_ioctl_arg_t)) @@ -931,16 +915,16 @@ static int ds_ioctl(struct inode *inode, struct file *file, ret = pccard_validate_cis(s, &buf->cisinfo.Chains); break; case DS_SUSPEND_CARD: - ret = pcmcia_suspend_card(s); + pcmcia_parse_uevents(s, PCMCIA_UEVENT_SUSPEND); break; case DS_RESUME_CARD: - ret = pcmcia_resume_card(s); + pcmcia_parse_uevents(s, PCMCIA_UEVENT_RESUME); break; case DS_EJECT_CARD: - err = pcmcia_eject_card(s); + pcmcia_parse_uevents(s, PCMCIA_UEVENT_EJECT); break; case DS_INSERT_CARD: - err = pcmcia_insert_card(s); + pcmcia_parse_uevents(s, PCMCIA_UEVENT_INSERT); break; case DS_ACCESS_CONFIGURATION_REGISTER: if ((buf->conf_reg.Action == CS_WRITE) && !capable(CAP_SYS_ADMIN)) { @@ -1036,13 +1020,25 @@ free_out: return err; } /* ds_ioctl */ +static long ds_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int ret; + + lock_kernel(); + ret = ds_ioctl(file, cmd, arg); + unlock_kernel(); + + return ret; +} + + /*====================================================================*/ static const struct file_operations ds_fops = { .owner = THIS_MODULE, .open = ds_open, .release = ds_release, - .ioctl = ds_ioctl, + .unlocked_ioctl = ds_unlocked_ioctl, .read = ds_read, .write = ds_write, .poll = ds_poll, diff --git a/drivers/pcmcia/pcmcia_resource.c b/drivers/pcmcia/pcmcia_resource.c index d5db95644b64..29f91fac1dff 100644 --- a/drivers/pcmcia/pcmcia_resource.c +++ b/drivers/pcmcia/pcmcia_resource.c @@ -21,6 +21,9 @@ #include <linux/pci.h> #include <linux/device.h> #include <linux/netdevice.h> +#include <linux/slab.h> + +#include <asm/irq.h> #include <pcmcia/cs_types.h> #include <pcmcia/ss.h> @@ -37,11 +40,21 @@ static int io_speed; module_param(io_speed, int, 0444); -#ifdef CONFIG_PCMCIA_PROBE -#include <asm/irq.h> -/* mask of IRQs already reserved by other cards, we should avoid using them */ -static u8 pcmcia_used_irq[NR_IRQS]; -#endif +int pcmcia_validate_mem(struct pcmcia_socket *s) +{ + if (s->resource_ops->validate_mem) + return s->resource_ops->validate_mem(s); + /* if there is no callback, we can assume that everything is OK */ + return 0; +} + +struct resource *pcmcia_find_mem_region(u_long base, u_long num, u_long align, + int low, struct pcmcia_socket *s) +{ + if (s->resource_ops->find_mem) + return s->resource_ops->find_mem(base, num, align, low, s); + return NULL; +} /** alloc_io_space @@ -52,8 +65,7 @@ static u8 pcmcia_used_irq[NR_IRQS]; static int alloc_io_space(struct pcmcia_socket *s, u_int attr, unsigned int *base, unsigned int num, u_int lines) { - int i; - unsigned int try, align; + unsigned int align; align = (*base) ? (lines ? 1<<lines : 0) : 1; if (align && (align < num)) { @@ -70,50 +82,8 @@ static int alloc_io_space(struct pcmcia_socket *s, u_int attr, *base, align); align = 0; } - if ((s->features & SS_CAP_STATIC_MAP) && s->io_offset) { - *base = s->io_offset | (*base & 0x0fff); - return 0; - } - /* Check for an already-allocated window that must conflict with - * what was asked for. It is a hack because it does not catch all - * potential conflicts, just the most obvious ones. - */ - for (i = 0; i < MAX_IO_WIN; i++) - if ((s->io[i].res) && *base && - ((s->io[i].res->start & (align-1)) == *base)) - return 1; - for (i = 0; i < MAX_IO_WIN; i++) { - if (!s->io[i].res) { - s->io[i].res = pcmcia_find_io_region(*base, num, align, s); - if (s->io[i].res) { - *base = s->io[i].res->start; - s->io[i].res->flags = (s->io[i].res->flags & ~IORESOURCE_BITS) | (attr & IORESOURCE_BITS); - s->io[i].InUse = num; - break; - } else - return 1; - } else if ((s->io[i].res->flags & IORESOURCE_BITS) != (attr & IORESOURCE_BITS)) - continue; - /* Try to extend top of window */ - try = s->io[i].res->end + 1; - if ((*base == 0) || (*base == try)) - if (pcmcia_adjust_io_region(s->io[i].res, s->io[i].res->start, - s->io[i].res->end + num, s) == 0) { - *base = try; - s->io[i].InUse += num; - break; - } - /* Try to extend bottom of window */ - try = s->io[i].res->start - num; - if ((*base == 0) || (*base == try)) - if (pcmcia_adjust_io_region(s->io[i].res, s->io[i].res->start - num, - s->io[i].res->end, s) == 0) { - *base = try; - s->io[i].InUse += num; - break; - } - } - return (i == MAX_IO_WIN); + + return s->resource_ops->find_io(s, attr, base, num, align); } /* alloc_io_space */ @@ -153,15 +123,19 @@ int pcmcia_access_configuration_register(struct pcmcia_device *p_dev, config_t *c; int addr; u_char val; + int ret = 0; if (!p_dev || !p_dev->function_config) return -EINVAL; s = p_dev->socket; + + mutex_lock(&s->ops_mutex); c = p_dev->function_config; if (!(c->state & CONFIG_LOCKED)) { dev_dbg(&s->dev, "Configuration isnt't locked\n"); + mutex_unlock(&s->ops_mutex); return -EACCES; } @@ -169,7 +143,7 @@ int pcmcia_access_configuration_register(struct pcmcia_device *p_dev, switch (reg->Action) { case CS_READ: - pcmcia_read_cis_mem(s, 1, addr, 1, &val); + ret = pcmcia_read_cis_mem(s, 1, addr, 1, &val); reg->Value = val; break; case CS_WRITE: @@ -178,10 +152,11 @@ int pcmcia_access_configuration_register(struct pcmcia_device *p_dev, break; default: dev_dbg(&s->dev, "Invalid conf register request\n"); - return -EINVAL; + ret = -EINVAL; break; } - return 0; + mutex_unlock(&s->ops_mutex); + return ret; } /* pcmcia_access_configuration_register */ EXPORT_SYMBOL(pcmcia_access_configuration_register); @@ -190,6 +165,7 @@ int pcmcia_map_mem_page(struct pcmcia_device *p_dev, window_handle_t wh, memreq_t *req) { struct pcmcia_socket *s = p_dev->socket; + int ret; wh--; if (wh >= MAX_WIN) @@ -198,12 +174,13 @@ int pcmcia_map_mem_page(struct pcmcia_device *p_dev, window_handle_t wh, dev_dbg(&s->dev, "failure: requested page is zero\n"); return -EINVAL; } + mutex_lock(&s->ops_mutex); s->win[wh].card_start = req->CardOffset; - if (s->ops->set_mem_map(s, &s->win[wh]) != 0) { - dev_dbg(&s->dev, "failed to set_mem_map\n"); - return -EIO; - } - return 0; + ret = s->ops->set_mem_map(s, &s->win[wh]); + if (ret) + dev_warn(&s->dev, "failed to set_mem_map\n"); + mutex_unlock(&s->ops_mutex); + return ret; } /* pcmcia_map_mem_page */ EXPORT_SYMBOL(pcmcia_map_mem_page); @@ -217,33 +194,29 @@ int pcmcia_modify_configuration(struct pcmcia_device *p_dev, { struct pcmcia_socket *s; config_t *c; + int ret; s = p_dev->socket; + + mutex_lock(&s->ops_mutex); c = p_dev->function_config; if (!(s->state & SOCKET_PRESENT)) { dev_dbg(&s->dev, "No card present\n"); - return -ENODEV; + ret = -ENODEV; + goto unlock; } if (!(c->state & CONFIG_LOCKED)) { dev_dbg(&s->dev, "Configuration isnt't locked\n"); - return -EACCES; - } - - if (mod->Attributes & CONF_IRQ_CHANGE_VALID) { - if (mod->Attributes & CONF_ENABLE_IRQ) { - c->Attributes |= CONF_ENABLE_IRQ; - s->socket.io_irq = s->irq.AssignedIRQ; - } else { - c->Attributes &= ~CONF_ENABLE_IRQ; - s->socket.io_irq = 0; - } - s->ops->set_socket(s, &s->socket); + ret = -EACCES; + goto unlock; } - if (mod->Attributes & CONF_VCC_CHANGE_VALID) { - dev_dbg(&s->dev, "changing Vcc is not allowed at this time\n"); - return -EINVAL; + if (mod->Attributes & (CONF_IRQ_CHANGE_VALID | CONF_VCC_CHANGE_VALID)) { + dev_dbg(&s->dev, + "changing Vcc or IRQ is not allowed at this time\n"); + ret = -EINVAL; + goto unlock; } /* We only allow changing Vpp1 and Vpp2 to the same value */ @@ -251,18 +224,21 @@ int pcmcia_modify_configuration(struct pcmcia_device *p_dev, (mod->Attributes & CONF_VPP2_CHANGE_VALID)) { if (mod->Vpp1 != mod->Vpp2) { dev_dbg(&s->dev, "Vpp1 and Vpp2 must be the same\n"); - return -EINVAL; + ret = -EINVAL; + goto unlock; } s->socket.Vpp = mod->Vpp1; if (s->ops->set_socket(s, &s->socket)) { dev_printk(KERN_WARNING, &s->dev, "Unable to set VPP\n"); - return -EIO; + ret = -EIO; + goto unlock; } } else if ((mod->Attributes & CONF_VPP1_CHANGE_VALID) || (mod->Attributes & CONF_VPP2_CHANGE_VALID)) { dev_dbg(&s->dev, "changing Vcc is not allowed at this time\n"); - return -EINVAL; + ret = -EINVAL; + goto unlock; } if (mod->Attributes & CONF_IO_CHANGE_WIDTH) { @@ -286,8 +262,11 @@ int pcmcia_modify_configuration(struct pcmcia_device *p_dev, s->ops->set_io_map(s, &io_on); } } + ret = 0; +unlock: + mutex_unlock(&s->ops_mutex); - return 0; + return ret; } /* modify_configuration */ EXPORT_SYMBOL(pcmcia_modify_configuration); @@ -296,9 +275,11 @@ int pcmcia_release_configuration(struct pcmcia_device *p_dev) { pccard_io_map io = { 0, 0, 0, 0, 1 }; struct pcmcia_socket *s = p_dev->socket; - config_t *c = p_dev->function_config; + config_t *c; int i; + mutex_lock(&s->ops_mutex); + c = p_dev->function_config; if (p_dev->_locked) { p_dev->_locked = 0; if (--(s->lock_count) == 0) { @@ -321,6 +302,7 @@ int pcmcia_release_configuration(struct pcmcia_device *p_dev) s->ops->set_io_map(s, &io); } } + mutex_unlock(&s->ops_mutex); return 0; } /* pcmcia_release_configuration */ @@ -337,10 +319,14 @@ int pcmcia_release_configuration(struct pcmcia_device *p_dev) static int pcmcia_release_io(struct pcmcia_device *p_dev, io_req_t *req) { struct pcmcia_socket *s = p_dev->socket; - config_t *c = p_dev->function_config; + int ret = -EINVAL; + config_t *c; + + mutex_lock(&s->ops_mutex); + c = p_dev->function_config; if (!p_dev->_io) - return -EINVAL; + goto out; p_dev->_io = 0; @@ -348,7 +334,7 @@ static int pcmcia_release_io(struct pcmcia_device *p_dev, io_req_t *req) (c->io.NumPorts1 != req->NumPorts1) || (c->io.BasePort2 != req->BasePort2) || (c->io.NumPorts2 != req->NumPorts2)) - return -EINVAL; + goto out; c->state &= ~CONFIG_IO_REQ; @@ -356,43 +342,11 @@ static int pcmcia_release_io(struct pcmcia_device *p_dev, io_req_t *req) if (req->NumPorts2) release_io_space(s, req->BasePort2, req->NumPorts2); - return 0; -} /* pcmcia_release_io */ - - -static int pcmcia_release_irq(struct pcmcia_device *p_dev, irq_req_t *req) -{ - struct pcmcia_socket *s = p_dev->socket; - config_t *c = p_dev->function_config; - - if (!p_dev->_irq) - return -EINVAL; - p_dev->_irq = 0; - - if (c->state & CONFIG_LOCKED) - return -EACCES; - if (c->irq.Attributes != req->Attributes) { - dev_dbg(&s->dev, "IRQ attributes must match assigned ones\n"); - return -EINVAL; - } - if (s->irq.AssignedIRQ != req->AssignedIRQ) { - dev_dbg(&s->dev, "IRQ must match assigned one\n"); - return -EINVAL; - } - if (--s->irq.Config == 0) { - c->state &= ~CONFIG_IRQ_REQ; - s->irq.AssignedIRQ = 0; - } - - if (req->Handler) - free_irq(req->AssignedIRQ, p_dev->priv); +out: + mutex_unlock(&s->ops_mutex); -#ifdef CONFIG_PCMCIA_PROBE - pcmcia_used_irq[req->AssignedIRQ]--; -#endif - - return 0; -} /* pcmcia_release_irq */ + return ret; +} /* pcmcia_release_io */ int pcmcia_release_window(struct pcmcia_device *p_dev, window_handle_t wh) @@ -404,10 +358,12 @@ int pcmcia_release_window(struct pcmcia_device *p_dev, window_handle_t wh) if (wh >= MAX_WIN) return -EINVAL; + mutex_lock(&s->ops_mutex); win = &s->win[wh]; if (!(p_dev->_win & CLIENT_WIN_REQ(wh))) { dev_dbg(&s->dev, "not releasing unknown window\n"); + mutex_unlock(&s->ops_mutex); return -EINVAL; } @@ -423,6 +379,7 @@ int pcmcia_release_window(struct pcmcia_device *p_dev, window_handle_t wh) win->res = NULL; } p_dev->_win &= ~CLIENT_WIN_REQ(wh); + mutex_unlock(&s->ops_mutex); return 0; } /* pcmcia_release_window */ @@ -445,8 +402,11 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev, dev_dbg(&s->dev, "IntType may not be INT_CARDBUS\n"); return -EINVAL; } + + mutex_lock(&s->ops_mutex); c = p_dev->function_config; if (c->state & CONFIG_LOCKED) { + mutex_unlock(&s->ops_mutex); dev_dbg(&s->dev, "Configuration is locked\n"); return -EACCES; } @@ -454,6 +414,7 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev, /* Do power control. We don't allow changes in Vcc. */ s->socket.Vpp = req->Vpp; if (s->ops->set_socket(s, &s->socket)) { + mutex_unlock(&s->ops_mutex); dev_printk(KERN_WARNING, &s->dev, "Unable to set socket state\n"); return -EINVAL; @@ -471,7 +432,7 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev, if (req->Attributes & CONF_ENABLE_SPKR) s->socket.flags |= SS_SPKR_ENA; if (req->Attributes & CONF_ENABLE_IRQ) - s->socket.io_irq = s->irq.AssignedIRQ; + s->socket.io_irq = s->pcmcia_irq; else s->socket.io_irq = 0; s->ops->set_socket(s, &s->socket); @@ -493,9 +454,9 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev, if (req->Present & PRESENT_IOBASE_0) c->Option |= COR_ADDR_DECODE; } - if (c->state & CONFIG_IRQ_REQ) - if (!(c->irq.Attributes & IRQ_FORCED_PULSE)) - c->Option |= COR_LEVEL_REQ; + if ((req->Attributes & CONF_ENABLE_IRQ) && + !(req->Attributes & CONF_ENABLE_PULSE_IRQ)) + c->Option |= COR_LEVEL_REQ; pcmcia_write_cis_mem(s, 1, (base + CISREG_COR)>>1, 1, &c->Option); mdelay(40); } @@ -546,6 +507,7 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev, c->state |= CONFIG_LOCKED; p_dev->_locked = 1; + mutex_unlock(&s->ops_mutex); return 0; } /* pcmcia_request_configuration */ EXPORT_SYMBOL(pcmcia_request_configuration); @@ -560,192 +522,239 @@ int pcmcia_request_io(struct pcmcia_device *p_dev, io_req_t *req) { struct pcmcia_socket *s = p_dev->socket; config_t *c; + int ret = -EINVAL; + + mutex_lock(&s->ops_mutex); if (!(s->state & SOCKET_PRESENT)) { dev_dbg(&s->dev, "No card present\n"); - return -ENODEV; + goto out; } if (!req) - return -EINVAL; + goto out; + c = p_dev->function_config; if (c->state & CONFIG_LOCKED) { dev_dbg(&s->dev, "Configuration is locked\n"); - return -EACCES; + goto out; } if (c->state & CONFIG_IO_REQ) { dev_dbg(&s->dev, "IO already configured\n"); - return -EBUSY; + goto out; } if (req->Attributes1 & (IO_SHARED | IO_FORCE_ALIAS_ACCESS)) { dev_dbg(&s->dev, "bad attribute setting for IO region 1\n"); - return -EINVAL; + goto out; } if ((req->NumPorts2 > 0) && (req->Attributes2 & (IO_SHARED | IO_FORCE_ALIAS_ACCESS))) { dev_dbg(&s->dev, "bad attribute setting for IO region 2\n"); - return -EINVAL; + goto out; } dev_dbg(&s->dev, "trying to allocate resource 1\n"); - if (alloc_io_space(s, req->Attributes1, &req->BasePort1, - req->NumPorts1, req->IOAddrLines)) { + ret = alloc_io_space(s, req->Attributes1, &req->BasePort1, + req->NumPorts1, req->IOAddrLines); + if (ret) { dev_dbg(&s->dev, "allocation of resource 1 failed\n"); - return -EBUSY; + goto out; } if (req->NumPorts2) { dev_dbg(&s->dev, "trying to allocate resource 2\n"); - if (alloc_io_space(s, req->Attributes2, &req->BasePort2, - req->NumPorts2, req->IOAddrLines)) { + ret = alloc_io_space(s, req->Attributes2, &req->BasePort2, + req->NumPorts2, req->IOAddrLines); + if (ret) { dev_dbg(&s->dev, "allocation of resource 2 failed\n"); release_io_space(s, req->BasePort1, req->NumPorts1); - return -EBUSY; + goto out; } } c->io = *req; c->state |= CONFIG_IO_REQ; p_dev->_io = 1; - return 0; + dev_dbg(&s->dev, "allocating resources succeeded: %d\n", ret); + +out: + mutex_unlock(&s->ops_mutex); + + return ret; } /* pcmcia_request_io */ EXPORT_SYMBOL(pcmcia_request_io); -/** pcmcia_request_irq +/** + * pcmcia_request_irq() - attempt to request a IRQ for a PCMCIA device * - * Request_irq() reserves an irq for this client. + * pcmcia_request_irq() is a wrapper around request_irq which will allow + * the PCMCIA core to clean up the registration in pcmcia_disable_device(). + * Drivers are free to use request_irq() directly, but then they need to + * call free_irq themselfves, too. Also, only IRQF_SHARED capable IRQ + * handlers are allowed. + */ +int __must_check pcmcia_request_irq(struct pcmcia_device *p_dev, + irq_handler_t handler) +{ + int ret; + + if (!p_dev->irq) + return -EINVAL; + + ret = request_irq(p_dev->irq, handler, IRQF_SHARED, + p_dev->devname, p_dev->priv); + if (!ret) + p_dev->_irq = 1; + + return ret; +} +EXPORT_SYMBOL(pcmcia_request_irq); + + +/** + * pcmcia_request_exclusive_irq() - attempt to request an exclusive IRQ first * - * Also, since Linux only reserves irq's when they are actually - * hooked, we don't guarantee that an irq will still be available - * when the configuration is locked. Now that I think about it, - * there might be a way to fix this using a dummy handler. + * pcmcia_request_exclusive_irq() is a wrapper around request_irq which + * attempts first to request an exclusive IRQ. If it fails, it also accepts + * a shared IRQ, but prints out a warning. PCMCIA drivers should allow for + * IRQ sharing and either use request_irq directly (then they need to call + * free_irq themselves, too), or the pcmcia_request_irq() function. */ +int __must_check +__pcmcia_request_exclusive_irq(struct pcmcia_device *p_dev, + irq_handler_t handler) +{ + int ret; + + if (!p_dev->irq) + return -EINVAL; + + ret = request_irq(p_dev->irq, handler, 0, p_dev->devname, p_dev->priv); + if (ret) { + ret = pcmcia_request_irq(p_dev, handler); + dev_printk(KERN_WARNING, &p_dev->dev, "pcmcia: " + "request for exclusive IRQ could not be fulfilled.\n"); + dev_printk(KERN_WARNING, &p_dev->dev, "pcmcia: the driver " + "needs updating to supported shared IRQ lines.\n"); + } + if (ret) + dev_printk(KERN_INFO, &p_dev->dev, "request_irq() failed\n"); + else + p_dev->_irq = 1; + + return ret; +} /* pcmcia_request_exclusive_irq */ +EXPORT_SYMBOL(__pcmcia_request_exclusive_irq); + #ifdef CONFIG_PCMCIA_PROBE + +/* mask of IRQs already reserved by other cards, we should avoid using them */ +static u8 pcmcia_used_irq[NR_IRQS]; + static irqreturn_t test_action(int cpl, void *dev_id) { return IRQ_NONE; } -#endif -int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req) +/** + * pcmcia_setup_isa_irq() - determine whether an ISA IRQ can be used + * @p_dev - the associated PCMCIA device + * + * locking note: must be called with ops_mutex locked. + */ +static int pcmcia_setup_isa_irq(struct pcmcia_device *p_dev, int type) { struct pcmcia_socket *s = p_dev->socket; - config_t *c; - int ret = -EINVAL, irq = 0; - int type; + unsigned int try, irq; + u32 mask = s->irq_mask; + int ret = -ENODEV; - if (!(s->state & SOCKET_PRESENT)) { - dev_dbg(&s->dev, "No card present\n"); - return -ENODEV; - } - c = p_dev->function_config; - if (c->state & CONFIG_LOCKED) { - dev_dbg(&s->dev, "Configuration is locked\n"); - return -EACCES; - } - if (c->state & CONFIG_IRQ_REQ) { - dev_dbg(&s->dev, "IRQ already configured\n"); - return -EBUSY; + for (try = 0; try < 64; try++) { + irq = try % 32; + + /* marked as available by driver, not blocked by userspace? */ + if (!((mask >> irq) & 1)) + continue; + + /* avoid an IRQ which is already used by another PCMCIA card */ + if ((try < 32) && pcmcia_used_irq[irq]) + continue; + + /* register the correct driver, if possible, to check whether + * registering a dummy handle works, i.e. if the IRQ isn't + * marked as used by the kernel resource management core */ + ret = request_irq(irq, test_action, type, p_dev->devname, + p_dev); + if (!ret) { + free_irq(irq, p_dev); + p_dev->irq = s->pcmcia_irq = irq; + pcmcia_used_irq[irq]++; + break; + } } - /* Decide what type of interrupt we are registering */ - type = 0; - if (s->functions > 1) /* All of this ought to be handled higher up */ - type = IRQF_SHARED; - else if (req->Attributes & IRQ_TYPE_DYNAMIC_SHARING) - type = IRQF_SHARED; - else - printk(KERN_WARNING "pcmcia: Driver needs updating to support IRQ sharing.\n"); + return ret; +} -#ifdef CONFIG_PCMCIA_PROBE +void pcmcia_cleanup_irq(struct pcmcia_socket *s) +{ + pcmcia_used_irq[s->pcmcia_irq]--; + s->pcmcia_irq = 0; +} -#ifdef IRQ_NOAUTOEN - /* if the underlying IRQ infrastructure allows for it, only allocate - * the IRQ, but do not enable it - */ - if (!(req->Handler)) - type |= IRQ_NOAUTOEN; -#endif /* IRQ_NOAUTOEN */ - - if (s->irq.AssignedIRQ != 0) { - /* If the interrupt is already assigned, it must be the same */ - irq = s->irq.AssignedIRQ; - } else { - int try; - u32 mask = s->irq_mask; - void *data = p_dev; /* something unique to this device */ - - for (try = 0; try < 64; try++) { - irq = try % 32; - - /* marked as available by driver, and not blocked by userspace? */ - if (!((mask >> irq) & 1)) - continue; +#else /* CONFIG_PCMCIA_PROBE */ - /* avoid an IRQ which is already used by a PCMCIA card */ - if ((try < 32) && pcmcia_used_irq[irq]) - continue; +static int pcmcia_setup_isa_irq(struct pcmcia_device *p_dev, int type) +{ + return -EINVAL; +} - /* register the correct driver, if possible, of check whether - * registering a dummy handle works, i.e. if the IRQ isn't - * marked as used by the kernel resource management core */ - ret = request_irq(irq, - (req->Handler) ? req->Handler : test_action, - type, - p_dev->devname, - (req->Handler) ? p_dev->priv : data); - if (!ret) { - if (!req->Handler) - free_irq(irq, data); - break; - } - } - } -#endif - /* only assign PCI irq if no IRQ already assigned */ - if (ret && !s->irq.AssignedIRQ) { - if (!s->pci_irq) { - dev_printk(KERN_INFO, &s->dev, "no IRQ found\n"); - return ret; - } - type = IRQF_SHARED; - irq = s->pci_irq; - } +void pcmcia_cleanup_irq(struct pcmcia_socket *s) +{ + s->pcmcia_irq = 0; + return; +} - if (ret && req->Handler) { - ret = request_irq(irq, req->Handler, type, - p_dev->devname, p_dev->priv); - if (ret) { - dev_printk(KERN_INFO, &s->dev, - "request_irq() failed\n"); - return ret; - } - } +#endif /* CONFIG_PCMCIA_PROBE */ - /* Make sure the fact the request type was overridden is passed back */ - if (type == IRQF_SHARED && !(req->Attributes & IRQ_TYPE_DYNAMIC_SHARING)) { - req->Attributes |= IRQ_TYPE_DYNAMIC_SHARING; - dev_printk(KERN_WARNING, &p_dev->dev, "pcmcia: " - "request for exclusive IRQ could not be fulfilled.\n"); - dev_printk(KERN_WARNING, &p_dev->dev, "pcmcia: the driver " - "needs updating to supported shared IRQ lines.\n"); + +/** + * pcmcia_setup_irq() - determine IRQ to be used for device + * @p_dev - the associated PCMCIA device + * + * locking note: must be called with ops_mutex locked. + */ +int pcmcia_setup_irq(struct pcmcia_device *p_dev) +{ + struct pcmcia_socket *s = p_dev->socket; + + if (p_dev->irq) + return 0; + + /* already assigned? */ + if (s->pcmcia_irq) { + p_dev->irq = s->pcmcia_irq; + return 0; } - c->irq.Attributes = req->Attributes; - s->irq.AssignedIRQ = req->AssignedIRQ = irq; - s->irq.Config++; - c->state |= CONFIG_IRQ_REQ; - p_dev->_irq = 1; + /* prefer an exclusive ISA irq */ + if (!pcmcia_setup_isa_irq(p_dev, 0)) + return 0; -#ifdef CONFIG_PCMCIA_PROBE - pcmcia_used_irq[irq]++; -#endif + /* but accept a shared ISA irq */ + if (!pcmcia_setup_isa_irq(p_dev, IRQF_SHARED)) + return 0; - return 0; -} /* pcmcia_request_irq */ -EXPORT_SYMBOL(pcmcia_request_irq); + /* but use the PCI irq otherwise */ + if (s->pci_irq) { + p_dev->irq = s->pcmcia_irq = s->pci_irq; + return 0; + } + + return -EINVAL; +} /** pcmcia_request_window @@ -796,6 +805,7 @@ int pcmcia_request_window(struct pcmcia_device *p_dev, win_req_t *req, window_ha return -EINVAL; } + mutex_lock(&s->ops_mutex); win = &s->win[w]; if (!(s->features & SS_CAP_STATIC_MAP)) { @@ -803,6 +813,7 @@ int pcmcia_request_window(struct pcmcia_device *p_dev, win_req_t *req, window_ha (req->Attributes & WIN_MAP_BELOW_1MB), s); if (!win->res) { dev_dbg(&s->dev, "allocating mem region failed\n"); + mutex_unlock(&s->ops_mutex); return -EINVAL; } } @@ -821,8 +832,10 @@ int pcmcia_request_window(struct pcmcia_device *p_dev, win_req_t *req, window_ha if (req->Attributes & WIN_USE_WAIT) win->flags |= MAP_USE_WAIT; win->card_start = 0; + if (s->ops->set_mem_map(s, win) != 0) { dev_dbg(&s->dev, "failed to set memory mapping\n"); + mutex_unlock(&s->ops_mutex); return -EIO; } s->state |= SOCKET_WIN_REQ(w); @@ -833,6 +846,7 @@ int pcmcia_request_window(struct pcmcia_device *p_dev, win_req_t *req, window_ha else req->Base = win->res->start; + mutex_unlock(&s->ops_mutex); *wh = w + 1; return 0; @@ -843,237 +857,9 @@ void pcmcia_disable_device(struct pcmcia_device *p_dev) { pcmcia_release_configuration(p_dev); pcmcia_release_io(p_dev, &p_dev->io); - pcmcia_release_irq(p_dev, &p_dev->irq); + if (p_dev->_irq) + free_irq(p_dev->irq, p_dev->priv); if (p_dev->win) pcmcia_release_window(p_dev, p_dev->win); } EXPORT_SYMBOL(pcmcia_disable_device); - - -struct pcmcia_cfg_mem { - struct pcmcia_device *p_dev; - void *priv_data; - int (*conf_check) (struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, - unsigned int vcc, - void *priv_data); - cisparse_t parse; - cistpl_cftable_entry_t dflt; -}; - -/** - * pcmcia_do_loop_config() - internal helper for pcmcia_loop_config() - * - * pcmcia_do_loop_config() is the internal callback for the call from - * pcmcia_loop_config() to pccard_loop_tuple(). Data is transferred - * by a struct pcmcia_cfg_mem. - */ -static int pcmcia_do_loop_config(tuple_t *tuple, cisparse_t *parse, void *priv) -{ - cistpl_cftable_entry_t *cfg = &parse->cftable_entry; - struct pcmcia_cfg_mem *cfg_mem = priv; - - /* default values */ - cfg_mem->p_dev->conf.ConfigIndex = cfg->index; - if (cfg->flags & CISTPL_CFTABLE_DEFAULT) - cfg_mem->dflt = *cfg; - - return cfg_mem->conf_check(cfg_mem->p_dev, cfg, &cfg_mem->dflt, - cfg_mem->p_dev->socket->socket.Vcc, - cfg_mem->priv_data); -} - -/** - * pcmcia_loop_config() - loop over configuration options - * @p_dev: the struct pcmcia_device which we need to loop for. - * @conf_check: function to call for each configuration option. - * It gets passed the struct pcmcia_device, the CIS data - * describing the configuration option, and private data - * being passed to pcmcia_loop_config() - * @priv_data: private data to be passed to the conf_check function. - * - * pcmcia_loop_config() loops over all configuration options, and calls - * the driver-specific conf_check() for each one, checking whether - * it is a valid one. Returns 0 on success or errorcode otherwise. - */ -int pcmcia_loop_config(struct pcmcia_device *p_dev, - int (*conf_check) (struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cfg, - cistpl_cftable_entry_t *dflt, - unsigned int vcc, - void *priv_data), - void *priv_data) -{ - struct pcmcia_cfg_mem *cfg_mem; - int ret; - - cfg_mem = kzalloc(sizeof(struct pcmcia_cfg_mem), GFP_KERNEL); - if (cfg_mem == NULL) - return -ENOMEM; - - cfg_mem->p_dev = p_dev; - cfg_mem->conf_check = conf_check; - cfg_mem->priv_data = priv_data; - - ret = pccard_loop_tuple(p_dev->socket, p_dev->func, - CISTPL_CFTABLE_ENTRY, &cfg_mem->parse, - cfg_mem, pcmcia_do_loop_config); - - kfree(cfg_mem); - return ret; -} -EXPORT_SYMBOL(pcmcia_loop_config); - - -struct pcmcia_loop_mem { - struct pcmcia_device *p_dev; - void *priv_data; - int (*loop_tuple) (struct pcmcia_device *p_dev, - tuple_t *tuple, - void *priv_data); -}; - -/** - * pcmcia_do_loop_tuple() - internal helper for pcmcia_loop_config() - * - * pcmcia_do_loop_tuple() is the internal callback for the call from - * pcmcia_loop_tuple() to pccard_loop_tuple(). Data is transferred - * by a struct pcmcia_cfg_mem. - */ -static int pcmcia_do_loop_tuple(tuple_t *tuple, cisparse_t *parse, void *priv) -{ - struct pcmcia_loop_mem *loop = priv; - - return loop->loop_tuple(loop->p_dev, tuple, loop->priv_data); -}; - -/** - * pcmcia_loop_tuple() - loop over tuples in the CIS - * @p_dev: the struct pcmcia_device which we need to loop for. - * @code: which CIS code shall we look for? - * @priv_data: private data to be passed to the loop_tuple function. - * @loop_tuple: function to call for each CIS entry of type @function. IT - * gets passed the raw tuple and @priv_data. - * - * pcmcia_loop_tuple() loops over all CIS entries of type @function, and - * calls the @loop_tuple function for each entry. If the call to @loop_tuple - * returns 0, the loop exits. Returns 0 on success or errorcode otherwise. - */ -int pcmcia_loop_tuple(struct pcmcia_device *p_dev, cisdata_t code, - int (*loop_tuple) (struct pcmcia_device *p_dev, - tuple_t *tuple, - void *priv_data), - void *priv_data) -{ - struct pcmcia_loop_mem loop = { - .p_dev = p_dev, - .loop_tuple = loop_tuple, - .priv_data = priv_data}; - - return pccard_loop_tuple(p_dev->socket, p_dev->func, code, NULL, - &loop, pcmcia_do_loop_tuple); -} -EXPORT_SYMBOL(pcmcia_loop_tuple); - - -struct pcmcia_loop_get { - size_t len; - cisdata_t **buf; -}; - -/** - * pcmcia_do_get_tuple() - internal helper for pcmcia_get_tuple() - * - * pcmcia_do_get_tuple() is the internal callback for the call from - * pcmcia_get_tuple() to pcmcia_loop_tuple(). As we're only interested in - * the first tuple, return 0 unconditionally. Create a memory buffer large - * enough to hold the content of the tuple, and fill it with the tuple data. - * The caller is responsible to free the buffer. - */ -static int pcmcia_do_get_tuple(struct pcmcia_device *p_dev, tuple_t *tuple, - void *priv) -{ - struct pcmcia_loop_get *get = priv; - - *get->buf = kzalloc(tuple->TupleDataLen, GFP_KERNEL); - if (*get->buf) { - get->len = tuple->TupleDataLen; - memcpy(*get->buf, tuple->TupleData, tuple->TupleDataLen); - } else - dev_dbg(&p_dev->dev, "do_get_tuple: out of memory\n"); - return 0; -} - -/** - * pcmcia_get_tuple() - get first tuple from CIS - * @p_dev: the struct pcmcia_device which we need to loop for. - * @code: which CIS code shall we look for? - * @buf: pointer to store the buffer to. - * - * pcmcia_get_tuple() gets the content of the first CIS entry of type @code. - * It returns the buffer length (or zero). The caller is responsible to free - * the buffer passed in @buf. - */ -size_t pcmcia_get_tuple(struct pcmcia_device *p_dev, cisdata_t code, - unsigned char **buf) -{ - struct pcmcia_loop_get get = { - .len = 0, - .buf = buf, - }; - - *get.buf = NULL; - pcmcia_loop_tuple(p_dev, code, pcmcia_do_get_tuple, &get); - - return get.len; -} -EXPORT_SYMBOL(pcmcia_get_tuple); - - -/** - * pcmcia_do_get_mac() - internal helper for pcmcia_get_mac_from_cis() - * - * pcmcia_do_get_mac() is the internal callback for the call from - * pcmcia_get_mac_from_cis() to pcmcia_loop_tuple(). We check whether the - * tuple contains a proper LAN_NODE_ID of length 6, and copy the data - * to struct net_device->dev_addr[i]. - */ -static int pcmcia_do_get_mac(struct pcmcia_device *p_dev, tuple_t *tuple, - void *priv) -{ - struct net_device *dev = priv; - int i; - - if (tuple->TupleData[0] != CISTPL_FUNCE_LAN_NODE_ID) - return -EINVAL; - if (tuple->TupleDataLen < ETH_ALEN + 2) { - dev_warn(&p_dev->dev, "Invalid CIS tuple length for " - "LAN_NODE_ID\n"); - return -EINVAL; - } - - if (tuple->TupleData[1] != ETH_ALEN) { - dev_warn(&p_dev->dev, "Invalid header for LAN_NODE_ID\n"); - return -EINVAL; - } - for (i = 0; i < 6; i++) - dev->dev_addr[i] = tuple->TupleData[i+2]; - return 0; -} - -/** - * pcmcia_get_mac_from_cis() - read out MAC address from CISTPL_FUNCE - * @p_dev: the struct pcmcia_device for which we want the address. - * @dev: a properly prepared struct net_device to store the info to. - * - * pcmcia_get_mac_from_cis() reads out the hardware MAC address from - * CISTPL_FUNCE and stores it into struct net_device *dev->dev_addr which - * must be set up properly by the driver (see examples!). - */ -int pcmcia_get_mac_from_cis(struct pcmcia_device *p_dev, struct net_device *dev) -{ - return pcmcia_loop_tuple(p_dev, CISTPL_FUNCE, pcmcia_do_get_mac, dev); -} -EXPORT_SYMBOL(pcmcia_get_mac_from_cis); - diff --git a/drivers/pcmcia/pd6729.c b/drivers/pcmcia/pd6729.c index e1741cd875aa..b61a13663a0a 100644 --- a/drivers/pcmcia/pd6729.c +++ b/drivers/pcmcia/pd6729.c @@ -9,18 +9,19 @@ #include <linux/kernel.h> #include <linux/module.h> +#include <linux/slab.h> #include <linux/pci.h> #include <linux/init.h> #include <linux/workqueue.h> #include <linux/interrupt.h> #include <linux/device.h> +#include <linux/io.h> #include <pcmcia/cs_types.h> #include <pcmcia/ss.h> #include <pcmcia/cs.h> #include <asm/system.h> -#include <asm/io.h> #include "pd6729.h" #include "i82365.h" @@ -48,23 +49,13 @@ MODULE_AUTHOR("Jun Komuro <komurojun-mbn@nifty.com>"); * Specifies the interrupt delivery mode. The default (1) is to use PCI * interrupts; a value of 0 selects ISA interrupts. This must be set for * correct operation of PCI card readers. - * - * irq_list=i,j,... - * This list limits the set of interrupts that can be used by PCMCIA - * cards. - * The default list is 3,4,5,7,9,10,11. - * (irq_list parameter is not used, if irq_mode = 1) */ static int irq_mode = 1; /* 0 = ISA interrupt, 1 = PCI interrupt */ -static int irq_list[16]; -static unsigned int irq_list_count = 0; module_param(irq_mode, int, 0444); -module_param_array(irq_list, int, &irq_list_count, 0444); MODULE_PARM_DESC(irq_mode, "interrupt delivery mode. 0 = ISA, 1 = PCI. default is 1"); -MODULE_PARM_DESC(irq_list, "interrupts that can be used by PCMCIA cards"); static DEFINE_SPINLOCK(port_lock); @@ -232,9 +223,9 @@ static irqreturn_t pd6729_interrupt(int irq, void *dev) ? SS_READY : 0; } - if (events) { + if (events) pcmcia_parse_events(&socket[i].socket, events); - } + active |= events; } @@ -266,9 +257,8 @@ static int pd6729_get_status(struct pcmcia_socket *sock, u_int *value) status = indirect_read(socket, I365_STATUS); *value = 0; - if ((status & I365_CS_DETECT) == I365_CS_DETECT) { + if ((status & I365_CS_DETECT) == I365_CS_DETECT) *value |= SS_DETECT; - } /* * IO cards have a different meaning of bits 0,1 @@ -318,7 +308,7 @@ static int pd6729_set_socket(struct pcmcia_socket *sock, socket_state_t *state) socket->card_irq = state->io_irq; reg = 0; - /* The reset bit has "inverse" logic */ + /* The reset bit has "inverse" logic */ if (!(state->flags & SS_RESET)) reg |= I365_PC_RESET; if (state->flags & SS_IOCARD) @@ -390,7 +380,7 @@ static int pd6729_set_socket(struct pcmcia_socket *sock, socket_state_t *state) indirect_write(socket, I365_POWER, reg); if (irq_mode == 1) { - /* all interrupts are to be done as PCI interrupts */ + /* all interrupts are to be done as PCI interrupts */ data = PD67_EC1_INV_MGMT_IRQ | PD67_EC1_INV_CARD_IRQ; } else data = 0; @@ -401,9 +391,9 @@ static int pd6729_set_socket(struct pcmcia_socket *sock, socket_state_t *state) /* Enable specific interrupt events */ reg = 0x00; - if (state->csc_mask & SS_DETECT) { + if (state->csc_mask & SS_DETECT) reg |= I365_CSC_DETECT; - } + if (state->flags & SS_IOCARD) { if (state->csc_mask & SS_STSCHG) reg |= I365_CSC_STSCHG; @@ -460,9 +450,12 @@ static int pd6729_set_io_map(struct pcmcia_socket *sock, ioctl = indirect_read(socket, I365_IOCTL) & ~I365_IOCTL_MASK(map); - if (io->flags & MAP_0WS) ioctl |= I365_IOCTL_0WS(map); - if (io->flags & MAP_16BIT) ioctl |= I365_IOCTL_16BIT(map); - if (io->flags & MAP_AUTOSZ) ioctl |= I365_IOCTL_IOCS16(map); + if (io->flags & MAP_0WS) + ioctl |= I365_IOCTL_0WS(map); + if (io->flags & MAP_16BIT) + ioctl |= I365_IOCTL_16BIT(map); + if (io->flags & MAP_AUTOSZ) + ioctl |= I365_IOCTL_IOCS16(map); indirect_write(socket, I365_IOCTL, ioctl); @@ -507,7 +500,7 @@ static int pd6729_set_mem_map(struct pcmcia_socket *sock, /* write the stop address */ - i= (mem->res->end >> 12) & 0x0fff; + i = (mem->res->end >> 12) & 0x0fff; switch (to_cycles(mem->speed)) { case 0: break; @@ -573,7 +566,7 @@ static int pd6729_init(struct pcmcia_socket *sock) /* the pccard structure and its functions */ static struct pccard_operations pd6729_operations = { - .init = pd6729_init, + .init = pd6729_init, .get_status = pd6729_get_status, .set_socket = pd6729_set_socket, .set_io_map = pd6729_set_io_map, @@ -588,8 +581,13 @@ static irqreturn_t pd6729_test(int irq, void *dev) static int pd6729_check_irq(int irq) { - if (request_irq(irq, pd6729_test, IRQF_PROBE_SHARED, "x", pd6729_test) - != 0) return -1; + int ret; + + ret = request_irq(irq, pd6729_test, IRQF_PROBE_SHARED, "x", + pd6729_test); + if (ret) + return -1; + free_irq(irq, pd6729_test); return 0; } @@ -601,17 +599,11 @@ static u_int __devinit pd6729_isa_scan(void) if (irq_mode == 1) { printk(KERN_INFO "pd6729: PCI card interrupts, " - "PCI status changes\n"); + "PCI status changes\n"); return 0; } - if (irq_list_count == 0) - mask0 = 0xffff; - else - for (i = mask0 = 0; i < irq_list_count; i++) - mask0 |= (1<<irq_list[i]); - - mask0 &= PD67_MASK; + mask0 = PD67_MASK; /* just find interrupts that aren't in use */ for (i = 0; i < 16; i++) @@ -623,9 +615,10 @@ static u_int __devinit pd6729_isa_scan(void) if (mask & (1<<i)) printk("%s%d", ((mask & ((1<<i)-1)) ? "," : ""), i); - if (mask == 0) printk("none!"); - - printk(" polling status changes.\n"); + if (mask == 0) + printk("none!"); + else + printk(" polling status changes.\n"); return mask; } @@ -640,11 +633,16 @@ static int __devinit pd6729_pci_probe(struct pci_dev *dev, socket = kzalloc(sizeof(struct pd6729_socket) * MAX_SOCKETS, GFP_KERNEL); - if (!socket) + if (!socket) { + dev_warn(&dev->dev, "failed to kzalloc socket.\n"); return -ENOMEM; + } - if ((ret = pci_enable_device(dev))) + ret = pci_enable_device(dev); + if (ret) { + dev_warn(&dev->dev, "failed to enable pci_device.\n"); goto err_out_free_mem; + } if (!pci_resource_start(dev, 0)) { dev_warn(&dev->dev, "refusing to load the driver as the " @@ -655,7 +653,7 @@ static int __devinit pd6729_pci_probe(struct pci_dev *dev, dev_info(&dev->dev, "Cirrus PD6729 PCI to PCMCIA Bridge at 0x%llx " "on irq %d\n", (unsigned long long)pci_resource_start(dev, 0), dev->irq); - /* + /* * Since we have no memory BARs some firmware may not * have had PCI_COMMAND_MEMORY enabled, yet the device needs it. */ @@ -687,6 +685,7 @@ static int __devinit pd6729_pci_probe(struct pci_dev *dev, socket[i].socket.map_size = 0x1000; socket[i].socket.irq_mask = mask; socket[i].socket.pci_irq = dev->irq; + socket[i].socket.cb_dev = dev; socket[i].socket.owner = THIS_MODULE; socket[i].number = i; @@ -700,8 +699,9 @@ static int __devinit pd6729_pci_probe(struct pci_dev *dev, pci_set_drvdata(dev, socket); if (irq_mode == 1) { /* Register the interrupt handler */ - if ((ret = request_irq(dev->irq, pd6729_interrupt, IRQF_SHARED, - "pd6729", socket))) { + ret = request_irq(dev->irq, pd6729_interrupt, IRQF_SHARED, + "pd6729", socket); + if (ret) { dev_err(&dev->dev, "Failed to register irq %d\n", dev->irq); goto err_out_free_res; @@ -765,18 +765,6 @@ static void __devexit pd6729_pci_remove(struct pci_dev *dev) kfree(socket); } -#ifdef CONFIG_PM -static int pd6729_socket_suspend(struct pci_dev *dev, pm_message_t state) -{ - return pcmcia_socket_dev_suspend(&dev->dev); -} - -static int pd6729_socket_resume(struct pci_dev *dev) -{ - return pcmcia_socket_dev_resume(&dev->dev); -} -#endif - static struct pci_device_id pd6729_pci_ids[] = { { .vendor = PCI_VENDOR_ID_CIRRUS, @@ -793,10 +781,6 @@ static struct pci_driver pd6729_pci_driver = { .id_table = pd6729_pci_ids, .probe = pd6729_pci_probe, .remove = __devexit_p(pd6729_pci_remove), -#ifdef CONFIG_PM - .suspend = pd6729_socket_suspend, - .resume = pd6729_socket_resume, -#endif }; static int pd6729_module_init(void) diff --git a/drivers/pcmcia/pxa2xx_base.c b/drivers/pcmcia/pxa2xx_base.c index 76e640bccde8..df4532e91b1a 100644 --- a/drivers/pcmcia/pxa2xx_base.c +++ b/drivers/pcmcia/pxa2xx_base.c @@ -17,6 +17,7 @@ ======================================================================*/ #include <linux/module.h> +#include <linux/slab.h> #include <linux/init.h> #include <linux/cpufreq.h> #include <linux/ioport.h> @@ -325,19 +326,13 @@ static int pxa2xx_drv_pcmcia_remove(struct platform_device *dev) return 0; } -static int pxa2xx_drv_pcmcia_suspend(struct device *dev) -{ - return pcmcia_socket_dev_suspend(dev); -} - static int pxa2xx_drv_pcmcia_resume(struct device *dev) { pxa2xx_configure_sockets(dev); - return pcmcia_socket_dev_resume(dev); + return 0; } static const struct dev_pm_ops pxa2xx_drv_pcmcia_pm_ops = { - .suspend = pxa2xx_drv_pcmcia_suspend, .resume = pxa2xx_drv_pcmcia_resume, }; diff --git a/drivers/pcmcia/pxa2xx_vpac270.c b/drivers/pcmcia/pxa2xx_vpac270.c new file mode 100644 index 000000000000..55627eccee8e --- /dev/null +++ b/drivers/pcmcia/pxa2xx_vpac270.c @@ -0,0 +1,229 @@ +/* + * linux/drivers/pcmcia/pxa2xx_vpac270.c + * + * Driver for Voipac PXA270 PCMCIA and CF sockets + * + * Copyright (C) 2010 + * 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 <asm/mach-types.h> + +#include <mach/gpio.h> +#include <mach/vpac270.h> + +#include "soc_common.h" + +static struct pcmcia_irqs cd_irqs[] = { + { + .sock = 0, + .irq = IRQ_GPIO(GPIO84_VPAC270_PCMCIA_CD), + .str = "PCMCIA CD" + }, + { + .sock = 1, + .irq = IRQ_GPIO(GPIO17_VPAC270_CF_CD), + .str = "CF CD" + }, +}; + +static int vpac270_pcmcia_hw_init(struct soc_pcmcia_socket *skt) +{ + int ret; + + if (skt->nr == 0) { + ret = gpio_request(GPIO84_VPAC270_PCMCIA_CD, "PCMCIA CD"); + if (ret) + goto err1; + ret = gpio_direction_input(GPIO84_VPAC270_PCMCIA_CD); + if (ret) + goto err2; + + ret = gpio_request(GPIO35_VPAC270_PCMCIA_RDY, "PCMCIA RDY"); + if (ret) + goto err2; + ret = gpio_direction_input(GPIO35_VPAC270_PCMCIA_RDY); + if (ret) + goto err3; + + ret = gpio_request(GPIO107_VPAC270_PCMCIA_PPEN, "PCMCIA PPEN"); + if (ret) + goto err3; + ret = gpio_direction_output(GPIO107_VPAC270_PCMCIA_PPEN, 0); + if (ret) + goto err4; + + ret = gpio_request(GPIO11_VPAC270_PCMCIA_RESET, "PCMCIA RESET"); + if (ret) + goto err4; + ret = gpio_direction_output(GPIO11_VPAC270_PCMCIA_RESET, 0); + if (ret) + goto err5; + + skt->socket.pci_irq = gpio_to_irq(GPIO35_VPAC270_PCMCIA_RDY); + + return soc_pcmcia_request_irqs(skt, &cd_irqs[0], 1); + +err5: + gpio_free(GPIO11_VPAC270_PCMCIA_RESET); +err4: + gpio_free(GPIO107_VPAC270_PCMCIA_PPEN); +err3: + gpio_free(GPIO35_VPAC270_PCMCIA_RDY); +err2: + gpio_free(GPIO84_VPAC270_PCMCIA_CD); +err1: + return ret; + + } else { + ret = gpio_request(GPIO17_VPAC270_CF_CD, "CF CD"); + if (ret) + goto err6; + ret = gpio_direction_input(GPIO17_VPAC270_CF_CD); + if (ret) + goto err7; + + ret = gpio_request(GPIO12_VPAC270_CF_RDY, "CF RDY"); + if (ret) + goto err7; + ret = gpio_direction_input(GPIO12_VPAC270_CF_RDY); + if (ret) + goto err8; + + ret = gpio_request(GPIO16_VPAC270_CF_RESET, "CF RESET"); + if (ret) + goto err8; + ret = gpio_direction_output(GPIO16_VPAC270_CF_RESET, 0); + if (ret) + goto err9; + + skt->socket.pci_irq = gpio_to_irq(GPIO12_VPAC270_CF_RDY); + + return soc_pcmcia_request_irqs(skt, &cd_irqs[1], 1); + +err9: + gpio_free(GPIO16_VPAC270_CF_RESET); +err8: + gpio_free(GPIO12_VPAC270_CF_RDY); +err7: + gpio_free(GPIO17_VPAC270_CF_CD); +err6: + return ret; + + } +} + +static void vpac270_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) +{ + gpio_free(GPIO11_VPAC270_PCMCIA_RESET); + gpio_free(GPIO107_VPAC270_PCMCIA_PPEN); + gpio_free(GPIO35_VPAC270_PCMCIA_RDY); + gpio_free(GPIO84_VPAC270_PCMCIA_CD); + gpio_free(GPIO16_VPAC270_CF_RESET); + gpio_free(GPIO12_VPAC270_CF_RDY); + gpio_free(GPIO17_VPAC270_CF_CD); +} + +static void vpac270_pcmcia_socket_state(struct soc_pcmcia_socket *skt, + struct pcmcia_state *state) +{ + if (skt->nr == 0) { + state->detect = !gpio_get_value(GPIO84_VPAC270_PCMCIA_CD); + state->ready = !!gpio_get_value(GPIO35_VPAC270_PCMCIA_RDY); + } else { + state->detect = !gpio_get_value(GPIO17_VPAC270_CF_CD); + state->ready = !!gpio_get_value(GPIO12_VPAC270_CF_RDY); + } + state->bvd1 = 1; + state->bvd2 = 1; + state->wrprot = 0; + state->vs_3v = 1; + state->vs_Xv = 0; +} + +static int +vpac270_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, + const socket_state_t *state) +{ + if (skt->nr == 0) { + gpio_set_value(GPIO11_VPAC270_PCMCIA_RESET, + (state->flags & SS_RESET)); + gpio_set_value(GPIO107_VPAC270_PCMCIA_PPEN, + !(state->Vcc == 33 || state->Vcc == 50)); + } else { + gpio_set_value(GPIO16_VPAC270_CF_RESET, + (state->flags & SS_RESET)); + } + + return 0; +} + +static void vpac270_pcmcia_socket_init(struct soc_pcmcia_socket *skt) +{ +} + +static void vpac270_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) +{ +} + +static struct pcmcia_low_level vpac270_pcmcia_ops = { + .owner = THIS_MODULE, + + .first = 0, + .nr = 2, + + .hw_init = vpac270_pcmcia_hw_init, + .hw_shutdown = vpac270_pcmcia_hw_shutdown, + + .socket_state = vpac270_pcmcia_socket_state, + .configure_socket = vpac270_pcmcia_configure_socket, + + .socket_init = vpac270_pcmcia_socket_init, + .socket_suspend = vpac270_pcmcia_socket_suspend, +}; + +static struct platform_device *vpac270_pcmcia_device; + +static int __init vpac270_pcmcia_init(void) +{ + int ret; + + if (!machine_is_vpac270()) + return -ENODEV; + + vpac270_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1); + if (!vpac270_pcmcia_device) + return -ENOMEM; + + ret = platform_device_add_data(vpac270_pcmcia_device, + &vpac270_pcmcia_ops, sizeof(vpac270_pcmcia_ops)); + + if (!ret) + ret = platform_device_add(vpac270_pcmcia_device); + + if (ret) + platform_device_put(vpac270_pcmcia_device); + + return ret; +} + +static void __exit vpac270_pcmcia_exit(void) +{ + platform_device_unregister(vpac270_pcmcia_device); +} + +module_init(vpac270_pcmcia_init); +module_exit(vpac270_pcmcia_exit); + +MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); +MODULE_DESCRIPTION("PCMCIA support for Voipac PXA270"); +MODULE_ALIAS("platform:pxa2xx-pcmcia"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pcmcia/rsrc_iodyn.c b/drivers/pcmcia/rsrc_iodyn.c new file mode 100644 index 000000000000..d0bf35021065 --- /dev/null +++ b/drivers/pcmcia/rsrc_iodyn.c @@ -0,0 +1,172 @@ +/* + * rsrc_iodyn.c -- Resource management routines for MEM-static sockets. + * + * 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. + * + * The initial developer of the original code is David A. Hinds + * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds + * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + * + * (C) 1999 David A. Hinds + */ + +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/kernel.h> + +#include <pcmcia/cs_types.h> +#include <pcmcia/ss.h> +#include <pcmcia/cs.h> +#include <pcmcia/cistpl.h> +#include "cs_internal.h" + + +struct pcmcia_align_data { + unsigned long mask; + unsigned long offset; +}; + +static resource_size_t pcmcia_align(void *align_data, + const struct resource *res, + resource_size_t size, resource_size_t align) +{ + struct pcmcia_align_data *data = align_data; + resource_size_t start; + + start = (res->start & ~data->mask) + data->offset; + if (start < res->start) + start += data->mask + 1; + +#ifdef CONFIG_X86 + if (res->flags & IORESOURCE_IO) { + if (start & 0x300) + start = (start + 0x3ff) & ~0x3ff; + } +#endif + +#ifdef CONFIG_M68K + if (res->flags & IORESOURCE_IO) { + if ((res->start + size - 1) >= 1024) + start = res->end; + } +#endif + + return start; +} + + +static struct resource *__iodyn_find_io_region(struct pcmcia_socket *s, + unsigned long base, int num, + unsigned long align) +{ + struct resource *res = pcmcia_make_resource(0, num, IORESOURCE_IO, + dev_name(&s->dev)); + struct pcmcia_align_data data; + unsigned long min = base; + int ret; + + data.mask = align - 1; + data.offset = base & data.mask; + +#ifdef CONFIG_PCI + if (s->cb_dev) { + ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1, + min, 0, pcmcia_align, &data); + } else +#endif + ret = allocate_resource(&ioport_resource, res, num, min, ~0UL, + 1, pcmcia_align, &data); + + if (ret != 0) { + kfree(res); + res = NULL; + } + return res; +} + +static int iodyn_find_io(struct pcmcia_socket *s, unsigned int attr, + unsigned int *base, unsigned int num, + unsigned int align) +{ + int i, ret = 0; + + /* Check for an already-allocated window that must conflict with + * what was asked for. It is a hack because it does not catch all + * potential conflicts, just the most obvious ones. + */ + for (i = 0; i < MAX_IO_WIN; i++) { + if (!s->io[i].res) + continue; + + if (!*base) + continue; + + if ((s->io[i].res->start & (align-1)) == *base) + return -EBUSY; + } + + for (i = 0; i < MAX_IO_WIN; i++) { + struct resource *res = s->io[i].res; + unsigned int try; + + if (res && (res->flags & IORESOURCE_BITS) != + (attr & IORESOURCE_BITS)) + continue; + + if (!res) { + if (align == 0) + align = 0x10000; + + res = s->io[i].res = __iodyn_find_io_region(s, *base, + num, align); + if (!res) + return -EINVAL; + + *base = res->start; + s->io[i].res->flags = + ((res->flags & ~IORESOURCE_BITS) | + (attr & IORESOURCE_BITS)); + s->io[i].InUse = num; + return 0; + } + + /* Try to extend top of window */ + try = res->end + 1; + if ((*base == 0) || (*base == try)) { + if (adjust_resource(s->io[i].res, res->start, + res->end - res->start + num + 1)) + continue; + *base = try; + s->io[i].InUse += num; + return 0; + } + + /* Try to extend bottom of window */ + try = res->start - num; + if ((*base == 0) || (*base == try)) { + if (adjust_resource(s->io[i].res, + res->start - num, + res->end - res->start + num + 1)) + continue; + *base = try; + s->io[i].InUse += num; + return 0; + } + } + + return -EINVAL; +} + + +struct pccard_resource_ops pccard_iodyn_ops = { + .validate_mem = NULL, + .find_io = iodyn_find_io, + .find_mem = NULL, + .add_io = NULL, + .add_mem = NULL, + .init = static_init, + .exit = NULL, +}; +EXPORT_SYMBOL(pccard_iodyn_ops); diff --git a/drivers/pcmcia/rsrc_mgr.c b/drivers/pcmcia/rsrc_mgr.c index 52db17263d8b..142efac3c387 100644 --- a/drivers/pcmcia/rsrc_mgr.c +++ b/drivers/pcmcia/rsrc_mgr.c @@ -12,6 +12,7 @@ * (C) 1999 David A. Hinds */ +#include <linux/slab.h> #include <linux/module.h> #include <linux/kernel.h> @@ -21,176 +22,54 @@ #include <pcmcia/cistpl.h> #include "cs_internal.h" - -int pcmcia_validate_mem(struct pcmcia_socket *s) -{ - if (s->resource_ops->validate_mem) - return s->resource_ops->validate_mem(s); - /* if there is no callback, we can assume that everything is OK */ - return 0; -} -EXPORT_SYMBOL(pcmcia_validate_mem); - -int pcmcia_adjust_io_region(struct resource *res, unsigned long r_start, - unsigned long r_end, struct pcmcia_socket *s) -{ - if (s->resource_ops->adjust_io_region) - return s->resource_ops->adjust_io_region(res, r_start, r_end, s); - return -ENOMEM; -} -EXPORT_SYMBOL(pcmcia_adjust_io_region); - -struct resource *pcmcia_find_io_region(unsigned long base, int num, - unsigned long align, struct pcmcia_socket *s) -{ - if (s->resource_ops->find_io) - return s->resource_ops->find_io(base, num, align, s); - return NULL; -} -EXPORT_SYMBOL(pcmcia_find_io_region); - -struct resource *pcmcia_find_mem_region(u_long base, u_long num, u_long align, - int low, struct pcmcia_socket *s) -{ - if (s->resource_ops->find_mem) - return s->resource_ops->find_mem(base, num, align, low, s); - return NULL; -} -EXPORT_SYMBOL(pcmcia_find_mem_region); - -void release_resource_db(struct pcmcia_socket *s) +int static_init(struct pcmcia_socket *s) { - if (s->resource_ops->exit) - s->resource_ops->exit(s); -} - - -static int static_init(struct pcmcia_socket *s) -{ - unsigned long flags; - /* the good thing about SS_CAP_STATIC_MAP sockets is * that they don't need a resource database */ - spin_lock_irqsave(&s->lock, flags); s->resource_setup_done = 1; - spin_unlock_irqrestore(&s->lock, flags); return 0; } - -struct pccard_resource_ops pccard_static_ops = { - .validate_mem = NULL, - .adjust_io_region = NULL, - .find_io = NULL, - .find_mem = NULL, - .add_io = NULL, - .add_mem = NULL, - .init = static_init, - .exit = NULL, -}; -EXPORT_SYMBOL(pccard_static_ops); - - -#ifdef CONFIG_PCCARD_IODYN - -static struct resource * -make_resource(unsigned long b, unsigned long n, int flags, char *name) +struct resource *pcmcia_make_resource(unsigned long start, unsigned long end, + int flags, const char *name) { struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL); if (res) { res->name = name; - res->start = b; - res->end = b + n - 1; + res->start = start; + res->end = start + end - 1; res->flags = flags; } return res; } -struct pcmcia_align_data { - unsigned long mask; - unsigned long offset; -}; - -static void pcmcia_align(void *align_data, struct resource *res, - unsigned long size, unsigned long align) +static int static_find_io(struct pcmcia_socket *s, unsigned int attr, + unsigned int *base, unsigned int num, + unsigned int align) { - struct pcmcia_align_data *data = align_data; - unsigned long start; - - start = (res->start & ~data->mask) + data->offset; - if (start < res->start) - start += data->mask + 1; - res->start = start; + if (!s->io_offset) + return -EINVAL; + *base = s->io_offset | (*base & 0x0fff); -#ifdef CONFIG_X86 - if (res->flags & IORESOURCE_IO) { - if (start & 0x300) { - start = (start + 0x3ff) & ~0x3ff; - res->start = start; - } - } -#endif - -#ifdef CONFIG_M68K - if (res->flags & IORESOURCE_IO) { - if ((res->start + size - 1) >= 1024) - res->start = res->end; - } -#endif -} - - -static int iodyn_adjust_io_region(struct resource *res, unsigned long r_start, - unsigned long r_end, struct pcmcia_socket *s) -{ - return adjust_resource(res, r_start, r_end - r_start + 1); + return 0; } -static struct resource *iodyn_find_io_region(unsigned long base, int num, - unsigned long align, struct pcmcia_socket *s) -{ - struct resource *res = make_resource(0, num, IORESOURCE_IO, - dev_name(&s->dev)); - struct pcmcia_align_data data; - unsigned long min = base; - int ret; - - if (align == 0) - align = 0x10000; - - data.mask = align - 1; - data.offset = base & data.mask; - -#ifdef CONFIG_PCI - if (s->cb_dev) { - ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1, - min, 0, pcmcia_align, &data); - } else -#endif - ret = allocate_resource(&ioport_resource, res, num, min, ~0UL, - 1, pcmcia_align, &data); - - if (ret != 0) { - kfree(res); - res = NULL; - } - return res; -} - -struct pccard_resource_ops pccard_iodyn_ops = { +struct pccard_resource_ops pccard_static_ops = { .validate_mem = NULL, - .adjust_io_region = iodyn_adjust_io_region, - .find_io = iodyn_find_io_region, + .find_io = static_find_io, .find_mem = NULL, .add_io = NULL, .add_mem = NULL, .init = static_init, .exit = NULL, }; -EXPORT_SYMBOL(pccard_iodyn_ops); +EXPORT_SYMBOL(pccard_static_ops); + -#endif /* CONFIG_PCCARD_IODYN */ +MODULE_AUTHOR("David A. Hinds, Dominik Brodowski"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("rsrc_nonstatic"); diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c index 9b0dc433a8c3..dcd1a4ad3d63 100644 --- a/drivers/pcmcia/rsrc_nonstatic.c +++ b/drivers/pcmcia/rsrc_nonstatic.c @@ -34,8 +34,10 @@ #include <pcmcia/cistpl.h> #include "cs_internal.h" +/* moved to rsrc_mgr.c MODULE_AUTHOR("David A. Hinds, Dominik Brodowski"); MODULE_LICENSE("GPL"); +*/ /* Parameters that can be set with 'insmod' */ @@ -55,11 +57,10 @@ struct resource_map { struct socket_data { struct resource_map mem_db; + struct resource_map mem_db_valid; struct resource_map io_db; - unsigned int rsrc_mem_probe; }; -static DEFINE_MUTEX(rsrc_mutex); #define MEM_PROBE_LOW (1 << 0) #define MEM_PROBE_HIGH (1 << 1) @@ -71,27 +72,13 @@ static DEFINE_MUTEX(rsrc_mutex); ======================================================================*/ static struct resource * -make_resource(resource_size_t b, resource_size_t n, int flags, const char *name) -{ - struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL); - - if (res) { - res->name = name; - res->start = b; - res->end = b + n - 1; - res->flags = flags; - } - return res; -} - -static struct resource * claim_region(struct pcmcia_socket *s, resource_size_t base, resource_size_t size, int type, char *name) { struct resource *res, *parent; parent = type & IORESOURCE_MEM ? &iomem_resource : &ioport_resource; - res = make_resource(base, size, type | IORESOURCE_BUSY, name); + res = pcmcia_make_resource(base, size, type | IORESOURCE_BUSY, name); if (res) { #ifdef CONFIG_PCI @@ -125,8 +112,10 @@ static int add_interval(struct resource_map *map, u_long base, u_long num) struct resource_map *p, *q; for (p = map; ; p = p->next) { - if ((p != map) && (p->base+p->num-1 >= base)) - return -1; + if ((p != map) && (p->base+p->num >= base)) { + p->num = max(num + base - p->base, p->num); + return 0; + } if ((p->next == map) || (p->next->base > base+num-1)) break; } @@ -213,7 +202,7 @@ static void do_io_probe(struct pcmcia_socket *s, unsigned int base, return; } for (i = base, most = 0; i < base+num; i += 8) { - res = claim_region(NULL, i, 8, IORESOURCE_IO, "PCMCIA ioprobe"); + res = claim_region(s, i, 8, IORESOURCE_IO, "PCMCIA ioprobe"); if (!res) continue; hole = inb(i); @@ -230,9 +219,14 @@ static void do_io_probe(struct pcmcia_socket *s, unsigned int base, bad = any = 0; for (i = base; i < base+num; i += 8) { - res = claim_region(NULL, i, 8, IORESOURCE_IO, "PCMCIA ioprobe"); - if (!res) + res = claim_region(s, i, 8, IORESOURCE_IO, "PCMCIA ioprobe"); + if (!res) { + if (!any) + printk(" excluding"); + if (!bad) + bad = any = i; continue; + } for (j = 0; j < 8; j++) if (inb(i+j) != most) break; @@ -252,6 +246,7 @@ static void do_io_probe(struct pcmcia_socket *s, unsigned int base, } if (bad) { if ((num > 16) && (bad == base) && (i == base+num)) { + sub_interval(&s_data->io_db, bad, i-bad); printk(" nothing: probe failed.\n"); return; } else { @@ -264,36 +259,44 @@ static void do_io_probe(struct pcmcia_socket *s, unsigned int base, } #endif -/*====================================================================== +/*======================================================================*/ - This is tricky... when we set up CIS memory, we try to validate - the memory window space allocations. - -======================================================================*/ - -/* Validation function for cards with a valid CIS */ +/** + * readable() - iomem validation function for cards with a valid CIS + */ static int readable(struct pcmcia_socket *s, struct resource *res, unsigned int *count) { - int ret = -1; + int ret = -EINVAL; + + if (s->fake_cis) { + dev_dbg(&s->dev, "fake CIS is being used: can't validate mem\n"); + return 0; + } s->cis_mem.res = res; s->cis_virt = ioremap(res->start, s->map_size); if (s->cis_virt) { - ret = pccard_validate_cis(s, count); - /* invalidate mapping and CIS cache */ + mutex_unlock(&s->ops_mutex); + /* as we're only called from pcmcia.c, we're safe */ + if (s->callback->validate) + ret = s->callback->validate(s, count); + /* invalidate mapping */ + mutex_lock(&s->ops_mutex); iounmap(s->cis_virt); s->cis_virt = NULL; - destroy_cis_cache(s); } s->cis_mem.res = NULL; - if ((ret != 0) || (*count == 0)) - return 0; - return 1; + if ((ret) || (*count == 0)) + return -EINVAL; + return 0; } -/* Validation function for simple memory cards */ -static int checksum(struct pcmcia_socket *s, struct resource *res) +/** + * checksum() - iomem validation function for simple memory cards + */ +static int checksum(struct pcmcia_socket *s, struct resource *res, + unsigned int *value) { pccard_mem_map map; int i, a = 0, b = -1, d; @@ -321,61 +324,90 @@ static int checksum(struct pcmcia_socket *s, struct resource *res) iounmap(virt); } - return (b == -1) ? -1 : (a>>1); + if (b == -1) + return -EINVAL; + + *value = a; + + return 0; } -static int -cis_readable(struct pcmcia_socket *s, unsigned long base, unsigned long size) +/** + * do_validate_mem() - low level validate a memory region for PCMCIA use + * @s: PCMCIA socket to validate + * @base: start address of resource to check + * @size: size of resource to check + * @validate: validation function to use + * + * do_validate_mem() splits up the memory region which is to be checked + * into two parts. Both are passed to the @validate() function. If + * @validate() returns non-zero, or the value parameter to @validate() + * is zero, or the value parameter is different between both calls, + * the check fails, and -EINVAL is returned. Else, 0 is returned. + */ +static int do_validate_mem(struct pcmcia_socket *s, + unsigned long base, unsigned long size, + int validate (struct pcmcia_socket *s, + struct resource *res, + unsigned int *value)) { + struct socket_data *s_data = s->resource_data; struct resource *res1, *res2; - unsigned int info1, info2; - int ret = 0; + unsigned int info1 = 1, info2 = 1; + int ret = -EINVAL; res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "PCMCIA memprobe"); res2 = claim_region(s, base + size/2, size/2, IORESOURCE_MEM, "PCMCIA memprobe"); if (res1 && res2) { - ret = readable(s, res1, &info1); - ret += readable(s, res2, &info2); + ret = 0; + if (validate) { + ret = validate(s, res1, &info1); + ret += validate(s, res2, &info2); + } } free_region(res2); free_region(res1); - return (ret == 2) && (info1 == info2); -} + dev_dbg(&s->dev, "cs: memory probe 0x%06lx-0x%06lx: %p %p %u %u %u", + base, base+size-1, res1, res2, ret, info1, info2); -static int -checksum_match(struct pcmcia_socket *s, unsigned long base, unsigned long size) -{ - struct resource *res1, *res2; - int a = -1, b = -1; - - res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "PCMCIA memprobe"); - res2 = claim_region(s, base + size/2, size/2, IORESOURCE_MEM, - "PCMCIA memprobe"); + if ((ret) || (info1 != info2) || (info1 == 0)) + return -EINVAL; - if (res1 && res2) { - a = checksum(s, res1); - b = checksum(s, res2); + if (validate && !s->fake_cis) { + /* move it to the validated data set */ + add_interval(&s_data->mem_db_valid, base, size); + sub_interval(&s_data->mem_db, base, size); } - free_region(res2); - free_region(res1); - - return (a == b) && (a >= 0); + return 0; } -/*====================================================================== - - The memory probe. If the memory list includes a 64K-aligned block - below 1MB, we probe in 64K chunks, and as soon as we accumulate at - least mem_limit free space, we quit. - -======================================================================*/ -static int do_mem_probe(u_long base, u_long num, struct pcmcia_socket *s) +/** + * do_mem_probe() - validate a memory region for PCMCIA use + * @s: PCMCIA socket to validate + * @base: start address of resource to check + * @num: size of resource to check + * @validate: validation function to use + * @fallback: validation function to use if validate fails + * + * do_mem_probe() checks a memory region for use by the PCMCIA subsystem. + * To do so, the area is split up into sensible parts, and then passed + * into the @validate() function. Only if @validate() and @fallback() fail, + * the area is marked as unavaibale for use by the PCMCIA subsystem. The + * function returns the size of the usable memory area. + */ +static int do_mem_probe(struct pcmcia_socket *s, u_long base, u_long num, + int validate (struct pcmcia_socket *s, + struct resource *res, + unsigned int *value), + int fallback (struct pcmcia_socket *s, + struct resource *res, + unsigned int *value)) { struct socket_data *s_data = s->resource_data; u_long i, j, bad, fail, step; @@ -393,15 +425,14 @@ static int do_mem_probe(u_long base, u_long num, struct pcmcia_socket *s) for (i = j = base; i < base+num; i = j + step) { if (!fail) { for (j = i; j < base+num; j += step) { - if (cis_readable(s, j, step)) + if (!do_validate_mem(s, j, step, validate)) break; } fail = ((i == base) && (j == base+num)); } - if (fail) { - for (j = i; j < base+num; j += 2*step) - if (checksum_match(s, j, step) && - checksum_match(s, j + step, step)) + if ((fail) && (fallback)) { + for (j = i; j < base+num; j += step) + if (!do_validate_mem(s, j, step, fallback)) break; } if (i != j) { @@ -416,8 +447,14 @@ static int do_mem_probe(u_long base, u_long num, struct pcmcia_socket *s) return num - bad; } + #ifdef CONFIG_PCMCIA_PROBE +/** + * inv_probe() - top-to-bottom search for one usuable high memory area + * @s: PCMCIA socket to validate + * @m: resource_map to check + */ static u_long inv_probe(struct resource_map *m, struct pcmcia_socket *s) { struct socket_data *s_data = s->resource_data; @@ -432,9 +469,18 @@ static u_long inv_probe(struct resource_map *m, struct pcmcia_socket *s) } if (m->base < 0x100000) return 0; - return do_mem_probe(m->base, m->num, s); + return do_mem_probe(s, m->base, m->num, readable, checksum); } +/** + * validate_mem() - memory probe function + * @s: PCMCIA socket to validate + * @probe_mask: MEM_PROBE_LOW | MEM_PROBE_HIGH + * + * The memory probe. If the memory list includes a 64K-aligned block + * below 1MB, we probe in 64K chunks, and as soon as we accumulate at + * least mem_limit free space, we quit. Returns 0 on usuable ports. + */ static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask) { struct resource_map *m, mm; @@ -446,6 +492,8 @@ static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask) if (probe_mask & MEM_PROBE_HIGH) { if (inv_probe(s_data->mem_db.next, s) > 0) return 0; + if (s_data->mem_db_valid.next != &s_data->mem_db_valid) + return 0; dev_printk(KERN_NOTICE, &s->dev, "cs: warning: no high memory space available!\n"); return -ENODEV; @@ -457,7 +505,8 @@ static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask) if (mm.base >= 0x100000) continue; if ((mm.base | mm.num) & 0xffff) { - ok += do_mem_probe(mm.base, mm.num, s); + ok += do_mem_probe(s, mm.base, mm.num, readable, + checksum); continue; } /* Special probe for 64K-aligned block */ @@ -467,7 +516,8 @@ static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask) if (ok >= mem_limit) sub_interval(&s_data->mem_db, b, 0x10000); else - ok += do_mem_probe(b, 0x10000, s); + ok += do_mem_probe(s, b, 0x10000, + readable, checksum); } } } @@ -480,6 +530,13 @@ static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask) #else /* CONFIG_PCMCIA_PROBE */ +/** + * validate_mem() - memory probe function + * @s: PCMCIA socket to validate + * @probe_mask: ignored + * + * Returns 0 on usuable ports. + */ static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask) { struct resource_map *m, mm; @@ -488,7 +545,7 @@ static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask) for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) { mm = *m; - ok += do_mem_probe(mm.base, mm.num, s); + ok += do_mem_probe(s, mm.base, mm.num, readable, checksum); } if (ok > 0) return 0; @@ -498,31 +555,31 @@ static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask) #endif /* CONFIG_PCMCIA_PROBE */ -/* +/** + * pcmcia_nonstatic_validate_mem() - try to validate iomem for PCMCIA use + * @s: PCMCIA socket to validate + * + * This is tricky... when we set up CIS memory, we try to validate + * the memory window space allocations. + * * Locking note: Must be called with skt_mutex held! */ static int pcmcia_nonstatic_validate_mem(struct pcmcia_socket *s) { struct socket_data *s_data = s->resource_data; unsigned int probe_mask = MEM_PROBE_LOW; - int ret = 0; + int ret; - if (!probe_mem) + if (!probe_mem || !(s->state & SOCKET_PRESENT)) return 0; - mutex_lock(&rsrc_mutex); - if (s->features & SS_CAP_PAGE_REGS) probe_mask = MEM_PROBE_HIGH; - if (probe_mask & ~s_data->rsrc_mem_probe) { - if (s->state & SOCKET_PRESENT) - ret = validate_mem(s, probe_mask); - if (!ret) - s_data->rsrc_mem_probe |= probe_mask; - } + ret = validate_mem(s, probe_mask); - mutex_unlock(&rsrc_mutex); + if (s_data->mem_db_valid.next != &s_data->mem_db_valid) + return 0; return ret; } @@ -533,52 +590,49 @@ struct pcmcia_align_data { struct resource_map *map; }; -static void -pcmcia_common_align(void *align_data, struct resource *res, - resource_size_t size, resource_size_t align) +static resource_size_t pcmcia_common_align(struct pcmcia_align_data *align_data, + resource_size_t start) { - struct pcmcia_align_data *data = align_data; - resource_size_t start; + resource_size_t ret; /* * Ensure that we have the correct start address */ - start = (res->start & ~data->mask) + data->offset; - if (start < res->start) - start += data->mask + 1; - res->start = start; + ret = (start & ~align_data->mask) + align_data->offset; + if (ret < start) + ret += align_data->mask + 1; + return ret; } -static void -pcmcia_align(void *align_data, struct resource *res, resource_size_t size, - resource_size_t align) +static resource_size_t +pcmcia_align(void *align_data, const struct resource *res, + resource_size_t size, resource_size_t align) { struct pcmcia_align_data *data = align_data; struct resource_map *m; + resource_size_t start; - pcmcia_common_align(data, res, size, align); + start = pcmcia_common_align(data, res->start); for (m = data->map->next; m != data->map; m = m->next) { - unsigned long start = m->base; - unsigned long end = m->base + m->num - 1; + unsigned long map_start = m->base; + unsigned long map_end = m->base + m->num - 1; /* * If the lower resources are not available, try aligning * to this entry of the resource database to see if it'll * fit here. */ - if (res->start < start) { - res->start = start; - pcmcia_common_align(data, res, size, align); - } + if (start < map_start) + start = pcmcia_common_align(data, map_start); /* * If we're above the area which was passed in, there's * no point proceeding. */ - if (res->start >= res->end) + if (start >= res->end) break; - if ((res->start + size - 1) <= end) + if ((start + size - 1) <= map_end) break; } @@ -586,21 +640,23 @@ pcmcia_align(void *align_data, struct resource *res, resource_size_t size, * If we failed to find something suitable, ensure we fail. */ if (m == data->map) - res->start = res->end; + start = res->end; + + return start; } /* * Adjust an existing IO region allocation, but making sure that we don't * encroach outside the resources which the user supplied. */ -static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_start, - unsigned long r_end, struct pcmcia_socket *s) +static int __nonstatic_adjust_io_region(struct pcmcia_socket *s, + unsigned long r_start, + unsigned long r_end) { struct resource_map *m; struct socket_data *s_data = s->resource_data; int ret = -ENOMEM; - mutex_lock(&rsrc_mutex); for (m = s_data->io_db.next; m != &s_data->io_db; m = m->next) { unsigned long start = m->base; unsigned long end = m->base + m->num - 1; @@ -608,10 +664,8 @@ static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_star if (start > r_start || r_end > end) continue; - ret = adjust_resource(res, r_start, r_end - r_start + 1); - break; + ret = 0; } - mutex_unlock(&rsrc_mutex); return ret; } @@ -629,23 +683,21 @@ static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_star ======================================================================*/ -static struct resource *nonstatic_find_io_region(unsigned long base, int num, - unsigned long align, struct pcmcia_socket *s) +static struct resource *__nonstatic_find_io_region(struct pcmcia_socket *s, + unsigned long base, int num, + unsigned long align) { - struct resource *res = make_resource(0, num, IORESOURCE_IO, dev_name(&s->dev)); + struct resource *res = pcmcia_make_resource(0, num, IORESOURCE_IO, + dev_name(&s->dev)); struct socket_data *s_data = s->resource_data; struct pcmcia_align_data data; unsigned long min = base; int ret; - if (align == 0) - align = 0x10000; - data.mask = align - 1; data.offset = base & data.mask; data.map = &s_data->io_db; - mutex_lock(&rsrc_mutex); #ifdef CONFIG_PCI if (s->cb_dev) { ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1, @@ -654,7 +706,6 @@ static struct resource *nonstatic_find_io_region(unsigned long base, int num, #endif ret = allocate_resource(&ioport_resource, res, num, min, ~0UL, 1, pcmcia_align, &data); - mutex_unlock(&rsrc_mutex); if (ret != 0) { kfree(res); @@ -663,22 +714,109 @@ static struct resource *nonstatic_find_io_region(unsigned long base, int num, return res; } +static int nonstatic_find_io(struct pcmcia_socket *s, unsigned int attr, + unsigned int *base, unsigned int num, + unsigned int align) +{ + int i, ret = 0; + + /* Check for an already-allocated window that must conflict with + * what was asked for. It is a hack because it does not catch all + * potential conflicts, just the most obvious ones. + */ + for (i = 0; i < MAX_IO_WIN; i++) { + if (!s->io[i].res) + continue; + + if (!*base) + continue; + + if ((s->io[i].res->start & (align-1)) == *base) + return -EBUSY; + } + + for (i = 0; i < MAX_IO_WIN; i++) { + struct resource *res = s->io[i].res; + unsigned int try; + + if (res && (res->flags & IORESOURCE_BITS) != + (attr & IORESOURCE_BITS)) + continue; + + if (!res) { + if (align == 0) + align = 0x10000; + + res = s->io[i].res = __nonstatic_find_io_region(s, + *base, num, + align); + if (!res) + return -EINVAL; + + *base = res->start; + s->io[i].res->flags = + ((res->flags & ~IORESOURCE_BITS) | + (attr & IORESOURCE_BITS)); + s->io[i].InUse = num; + return 0; + } + + /* Try to extend top of window */ + try = res->end + 1; + if ((*base == 0) || (*base == try)) { + ret = __nonstatic_adjust_io_region(s, res->start, + res->end + num); + if (!ret) { + ret = adjust_resource(s->io[i].res, res->start, + res->end - res->start + num + 1); + if (ret) + continue; + *base = try; + s->io[i].InUse += num; + return 0; + } + } + + /* Try to extend bottom of window */ + try = res->start - num; + if ((*base == 0) || (*base == try)) { + ret = __nonstatic_adjust_io_region(s, + res->start - num, + res->end); + if (!ret) { + ret = adjust_resource(s->io[i].res, + res->start - num, + res->end - res->start + num + 1); + if (ret) + continue; + *base = try; + s->io[i].InUse += num; + return 0; + } + } + } + + return -EINVAL; +} + + static struct resource *nonstatic_find_mem_region(u_long base, u_long num, u_long align, int low, struct pcmcia_socket *s) { - struct resource *res = make_resource(0, num, IORESOURCE_MEM, dev_name(&s->dev)); + struct resource *res = pcmcia_make_resource(0, num, IORESOURCE_MEM, + dev_name(&s->dev)); struct socket_data *s_data = s->resource_data; struct pcmcia_align_data data; unsigned long min, max; - int ret, i; + int ret, i, j; low = low || !(s->features & SS_CAP_PAGE_REGS); data.mask = align - 1; data.offset = base & data.mask; - data.map = &s_data->mem_db; for (i = 0; i < 2; i++) { + data.map = &s_data->mem_db_valid; if (low) { max = 0x100000UL; min = base < max ? base : 0; @@ -687,17 +825,23 @@ static struct resource *nonstatic_find_mem_region(u_long base, u_long num, min = 0x100000UL + base; } - mutex_lock(&rsrc_mutex); + for (j = 0; j < 2; j++) { #ifdef CONFIG_PCI - if (s->cb_dev) { - ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, - 1, min, 0, - pcmcia_align, &data); - } else + if (s->cb_dev) { + ret = pci_bus_alloc_resource(s->cb_dev->bus, + res, num, 1, min, 0, + pcmcia_align, &data); + } else #endif - ret = allocate_resource(&iomem_resource, res, num, min, - max, 1, pcmcia_align, &data); - mutex_unlock(&rsrc_mutex); + { + ret = allocate_resource(&iomem_resource, + res, num, min, max, 1, + pcmcia_align, &data); + } + if (ret == 0) + break; + data.map = &s_data->mem_db; + } if (ret == 0 || low) break; low = 1; @@ -720,25 +864,18 @@ static int adjust_memory(struct pcmcia_socket *s, unsigned int action, unsigned if (end < start) return -EINVAL; - mutex_lock(&rsrc_mutex); switch (action) { case ADD_MANAGED_RESOURCE: ret = add_interval(&data->mem_db, start, size); + if (!ret) + do_mem_probe(s, start, size, NULL, NULL); break; case REMOVE_MANAGED_RESOURCE: ret = sub_interval(&data->mem_db, start, size); - if (!ret) { - struct pcmcia_socket *socket; - down_read(&pcmcia_socket_list_rwsem); - list_for_each_entry(socket, &pcmcia_socket_list, socket_list) - release_cis_mem(socket); - up_read(&pcmcia_socket_list_rwsem); - } break; default: ret = -EINVAL; } - mutex_unlock(&rsrc_mutex); return ret; } @@ -747,16 +884,24 @@ static int adjust_memory(struct pcmcia_socket *s, unsigned int action, unsigned static int adjust_io(struct pcmcia_socket *s, unsigned int action, unsigned long start, unsigned long end) { struct socket_data *data = s->resource_data; - unsigned long size = end - start + 1; + unsigned long size; int ret = 0; +#if defined(CONFIG_X86) + /* on x86, avoid anything < 0x100 for it is often used for + * legacy platform devices */ + if (start < 0x100) + start = 0x100; +#endif + + size = end - start + 1; + if (end < start) return -EINVAL; if (end > IO_SPACE_LIMIT) return -EINVAL; - mutex_lock(&rsrc_mutex); switch (action) { case ADD_MANAGED_RESOURCE: if (add_interval(&data->io_db, start, size) != 0) { @@ -775,7 +920,6 @@ static int adjust_io(struct pcmcia_socket *s, unsigned int action, unsigned long ret = -EINVAL; break; } - mutex_unlock(&rsrc_mutex); return ret; } @@ -791,42 +935,60 @@ static int nonstatic_autoadd_resources(struct pcmcia_socket *s) return -ENODEV; #if defined(CONFIG_X86) - /* If this is the root bus, the risk of hitting - * some strange system devices which aren't protected - * by either ACPI resource tables or properly requested - * resources is too big. Therefore, don't do auto-adding - * of resources at the moment. + /* If this is the root bus, the risk of hitting some strange + * system devices is too high: If a driver isn't loaded, the + * resources are not claimed; even if a driver is loaded, it + * may not request all resources or even the wrong one. We + * can neither trust the rest of the kernel nor ACPI/PNP and + * CRS parsing to get it right. Therefore, use several + * safeguards: + * + * - Do not auto-add resources if the CardBus bridge is on + * the PCI root bus + * + * - Avoid any I/O ports < 0x100. + * + * - On PCI-PCI bridges, only use resources which are set up + * exclusively for the secondary PCI bus: the risk of hitting + * system devices is quite low, as they usually aren't + * connected to the secondary PCI bus. */ if (s->cb_dev->bus->number == 0) return -EINVAL; -#endif - for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) { + for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++) { res = s->cb_dev->bus->resource[i]; +#else + pci_bus_for_each_resource(s->cb_dev->bus, res, i) { +#endif if (!res) continue; if (res->flags & IORESOURCE_IO) { + /* safeguard against the root resource, where the + * risk of hitting any other device would be too + * high */ if (res == &ioport_resource) continue; + dev_printk(KERN_INFO, &s->cb_dev->dev, - "pcmcia: parent PCI bridge I/O " - "window: 0x%llx - 0x%llx\n", - (unsigned long long)res->start, - (unsigned long long)res->end); + "pcmcia: parent PCI bridge window: %pR\n", + res); if (!adjust_io(s, ADD_MANAGED_RESOURCE, res->start, res->end)) done |= IORESOURCE_IO; } if (res->flags & IORESOURCE_MEM) { + /* safeguard against the root resource, where the + * risk of hitting any other device would be too + * high */ if (res == &iomem_resource) continue; + dev_printk(KERN_INFO, &s->cb_dev->dev, - "pcmcia: parent PCI bridge Memory " - "window: 0x%llx - 0x%llx\n", - (unsigned long long)res->start, - (unsigned long long)res->end); + "pcmcia: parent PCI bridge window: %pR\n", + res); if (!adjust_memory(s, ADD_MANAGED_RESOURCE, res->start, res->end)) done |= IORESOURCE_MEM; } @@ -859,6 +1021,7 @@ static int nonstatic_init(struct pcmcia_socket *s) return -ENOMEM; data->mem_db.next = &data->mem_db; + data->mem_db_valid.next = &data->mem_db_valid; data->io_db.next = &data->io_db; s->resource_data = (void *) data; @@ -873,7 +1036,10 @@ static void nonstatic_release_resource_db(struct pcmcia_socket *s) struct socket_data *data = s->resource_data; struct resource_map *p, *q; - mutex_lock(&rsrc_mutex); + for (p = data->mem_db_valid.next; p != &data->mem_db_valid; p = q) { + q = p->next; + kfree(p); + } for (p = data->mem_db.next; p != &data->mem_db; p = q) { q = p->next; kfree(p); @@ -882,14 +1048,12 @@ static void nonstatic_release_resource_db(struct pcmcia_socket *s) q = p->next; kfree(p); } - mutex_unlock(&rsrc_mutex); } struct pccard_resource_ops pccard_nonstatic_ops = { .validate_mem = pcmcia_nonstatic_validate_mem, - .adjust_io_region = nonstatic_adjust_io_region, - .find_io = nonstatic_find_io_region, + .find_io = nonstatic_find_io, .find_mem = nonstatic_find_mem_region, .add_io = adjust_io, .add_mem = adjust_memory, @@ -909,7 +1073,7 @@ static ssize_t show_io_db(struct device *dev, struct resource_map *p; ssize_t ret = 0; - mutex_lock(&rsrc_mutex); + mutex_lock(&s->ops_mutex); data = s->resource_data; for (p = data->io_db.next; p != &data->io_db; p = p->next) { @@ -921,7 +1085,7 @@ static ssize_t show_io_db(struct device *dev, ((unsigned long) p->base + p->num - 1)); } - mutex_unlock(&rsrc_mutex); + mutex_unlock(&s->ops_mutex); return ret; } @@ -949,9 +1113,11 @@ static ssize_t store_io_db(struct device *dev, if (end_addr < start_addr) return -EINVAL; + mutex_lock(&s->ops_mutex); ret = adjust_io(s, add, start_addr, end_addr); if (!ret) s->resource_setup_new = 1; + mutex_unlock(&s->ops_mutex); return ret ? ret : count; } @@ -965,9 +1131,19 @@ static ssize_t show_mem_db(struct device *dev, struct resource_map *p; ssize_t ret = 0; - mutex_lock(&rsrc_mutex); + mutex_lock(&s->ops_mutex); data = s->resource_data; + for (p = data->mem_db_valid.next; p != &data->mem_db_valid; + p = p->next) { + if (ret > (PAGE_SIZE - 10)) + continue; + ret += snprintf(&buf[ret], (PAGE_SIZE - ret - 1), + "0x%08lx - 0x%08lx\n", + ((unsigned long) p->base), + ((unsigned long) p->base + p->num - 1)); + } + for (p = data->mem_db.next; p != &data->mem_db; p = p->next) { if (ret > (PAGE_SIZE - 10)) continue; @@ -977,7 +1153,7 @@ static ssize_t show_mem_db(struct device *dev, ((unsigned long) p->base + p->num - 1)); } - mutex_unlock(&rsrc_mutex); + mutex_unlock(&s->ops_mutex); return ret; } @@ -1005,9 +1181,11 @@ static ssize_t store_mem_db(struct device *dev, if (end_addr < start_addr) return -EINVAL; + mutex_lock(&s->ops_mutex); ret = adjust_memory(s, add, start_addr, end_addr); if (!ret) s->resource_setup_new = 1; + mutex_unlock(&s->ops_mutex); return ret ? ret : count; } diff --git a/drivers/pcmcia/sa1100_generic.c b/drivers/pcmcia/sa1100_generic.c index 8db86b90c200..edbd8c472628 100644 --- a/drivers/pcmcia/sa1100_generic.c +++ b/drivers/pcmcia/sa1100_generic.c @@ -32,6 +32,7 @@ #include <linux/module.h> #include <linux/init.h> +#include <linux/slab.h> #include <linux/platform_device.h> #include <pcmcia/cs_types.h> @@ -95,17 +96,6 @@ static int sa11x0_drv_pcmcia_remove(struct platform_device *dev) return 0; } -static int sa11x0_drv_pcmcia_suspend(struct platform_device *dev, - pm_message_t state) -{ - return pcmcia_socket_dev_suspend(&dev->dev); -} - -static int sa11x0_drv_pcmcia_resume(struct platform_device *dev) -{ - return pcmcia_socket_dev_resume(&dev->dev); -} - static struct platform_driver sa11x0_pcmcia_driver = { .driver = { .name = "sa11x0-pcmcia", @@ -113,8 +103,6 @@ static struct platform_driver sa11x0_pcmcia_driver = { }, .probe = sa11x0_drv_pcmcia_probe, .remove = sa11x0_drv_pcmcia_remove, - .suspend = sa11x0_drv_pcmcia_suspend, - .resume = sa11x0_drv_pcmcia_resume, }; /* sa11x0_pcmcia_init() diff --git a/drivers/pcmcia/sa1111_generic.c b/drivers/pcmcia/sa1111_generic.c index de6bc333d299..59866905ea37 100644 --- a/drivers/pcmcia/sa1111_generic.c +++ b/drivers/pcmcia/sa1111_generic.c @@ -12,6 +12,7 @@ #include <linux/interrupt.h> #include <linux/init.h> #include <linux/io.h> +#include <linux/slab.h> #include <pcmcia/ss.h> @@ -21,11 +22,18 @@ #include "sa1111_generic.h" +#define IDX_IRQ_S0_READY_NINT (0) +#define IDX_IRQ_S0_CD_VALID (1) +#define IDX_IRQ_S0_BVD1_STSCHG (2) +#define IDX_IRQ_S1_READY_NINT (3) +#define IDX_IRQ_S1_CD_VALID (4) +#define IDX_IRQ_S1_BVD1_STSCHG (5) + static struct pcmcia_irqs irqs[] = { - { 0, IRQ_S0_CD_VALID, "SA1111 PCMCIA card detect" }, - { 0, IRQ_S0_BVD1_STSCHG, "SA1111 PCMCIA BVD1" }, - { 1, IRQ_S1_CD_VALID, "SA1111 CF card detect" }, - { 1, IRQ_S1_BVD1_STSCHG, "SA1111 CF BVD1" }, + { 0, NO_IRQ, "SA1111 PCMCIA card detect" }, + { 0, NO_IRQ, "SA1111 PCMCIA BVD1" }, + { 1, NO_IRQ, "SA1111 CF card detect" }, + { 1, NO_IRQ, "SA1111 CF BVD1" }, }; static int sa1111_pcmcia_hw_init(struct soc_pcmcia_socket *skt) @@ -136,7 +144,9 @@ int sa1111_pcmcia_add(struct sa1111_dev *dev, struct pcmcia_low_level *ops, s->soc.ops = ops; s->soc.socket.owner = ops->owner; s->soc.socket.dev.parent = &dev->dev; - s->soc.socket.pci_irq = s->soc.nr ? IRQ_S1_READY_NINT : IRQ_S0_READY_NINT; + s->soc.socket.pci_irq = s->soc.nr ? + dev->irq[IDX_IRQ_S0_READY_NINT] : + dev->irq[IDX_IRQ_S1_READY_NINT]; s->dev = dev; ret = add(&s->soc); @@ -162,6 +172,12 @@ static int pcmcia_probe(struct sa1111_dev *dev) base = dev->mapbase; + /* Initialize PCMCIA IRQs */ + irqs[0].irq = dev->irq[IDX_IRQ_S0_CD_VALID]; + irqs[1].irq = dev->irq[IDX_IRQ_S0_BVD1_STSCHG]; + irqs[2].irq = dev->irq[IDX_IRQ_S1_CD_VALID]; + irqs[3].irq = dev->irq[IDX_IRQ_S1_BVD1_STSCHG]; + /* * Initialise the suspend state. */ @@ -198,16 +214,6 @@ static int __devexit pcmcia_remove(struct sa1111_dev *dev) return 0; } -static int pcmcia_suspend(struct sa1111_dev *dev, pm_message_t state) -{ - return pcmcia_socket_dev_suspend(&dev->dev); -} - -static int pcmcia_resume(struct sa1111_dev *dev) -{ - return pcmcia_socket_dev_resume(&dev->dev); -} - static struct sa1111_driver pcmcia_driver = { .drv = { .name = "sa1111-pcmcia", @@ -215,8 +221,6 @@ static struct sa1111_driver pcmcia_driver = { .devid = SA1111_DEVID_PCMCIA, .probe = pcmcia_probe, .remove = __devexit_p(pcmcia_remove), - .suspend = pcmcia_suspend, - .resume = pcmcia_resume, }; static int __init sa1111_drv_pcmcia_init(void) diff --git a/drivers/pcmcia/sa11xx_base.c b/drivers/pcmcia/sa11xx_base.c index fc9a6527019b..fa28d8911b00 100644 --- a/drivers/pcmcia/sa11xx_base.c +++ b/drivers/pcmcia/sa11xx_base.c @@ -37,6 +37,7 @@ #include <linux/kernel.h> #include <linux/spinlock.h> #include <linux/io.h> +#include <linux/slab.h> #include <mach/hardware.h> #include <asm/irq.h> diff --git a/drivers/pcmcia/socket_sysfs.c b/drivers/pcmcia/socket_sysfs.c index 7a456000332a..80e36bc407da 100644 --- a/drivers/pcmcia/socket_sysfs.c +++ b/drivers/pcmcia/socket_sysfs.c @@ -15,7 +15,6 @@ #include <linux/string.h> #include <linux/major.h> #include <linux/errno.h> -#include <linux/slab.h> #include <linux/mm.h> #include <linux/interrupt.h> #include <linux/timer.h> @@ -88,15 +87,14 @@ static DEVICE_ATTR(card_vcc, 0444, pccard_show_vcc, NULL); static ssize_t pccard_store_insert(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - ssize_t ret; struct pcmcia_socket *s = to_socket(dev); if (!count) return -EINVAL; - ret = pcmcia_insert_card(s); + pcmcia_parse_uevents(s, PCMCIA_UEVENT_INSERT); - return ret ? ret : count; + return count; } static DEVICE_ATTR(card_insert, 0200, NULL, pccard_store_insert); @@ -113,18 +111,22 @@ static ssize_t pccard_store_card_pm_state(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - ssize_t ret = -EINVAL; struct pcmcia_socket *s = to_socket(dev); + ssize_t ret = count; if (!count) return -EINVAL; - if (!(s->state & SOCKET_SUSPEND) && !strncmp(buf, "off", 3)) - ret = pcmcia_suspend_card(s); - else if ((s->state & SOCKET_SUSPEND) && !strncmp(buf, "on", 2)) - ret = pcmcia_resume_card(s); + if (!strncmp(buf, "off", 3)) + pcmcia_parse_uevents(s, PCMCIA_UEVENT_SUSPEND); + else { + if (!strncmp(buf, "on", 2)) + pcmcia_parse_uevents(s, PCMCIA_UEVENT_RESUME); + else + ret = -EINVAL; + } - return ret ? -ENODEV : count; + return ret; } static DEVICE_ATTR(card_pm_state, 0644, pccard_show_card_pm_state, pccard_store_card_pm_state); @@ -132,15 +134,14 @@ static ssize_t pccard_store_eject(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - ssize_t ret; struct pcmcia_socket *s = to_socket(dev); if (!count) return -EINVAL; - ret = pcmcia_eject_card(s); + pcmcia_parse_uevents(s, PCMCIA_UEVENT_EJECT); - return ret ? ret : count; + return count; } static DEVICE_ATTR(card_eject, 0200, NULL, pccard_store_eject); @@ -167,7 +168,9 @@ static ssize_t pccard_store_irq_mask(struct device *dev, ret = sscanf(buf, "0x%x\n", &mask); if (ret == 1) { + mutex_lock(&s->ops_mutex); s->irq_mask &= mask; + mutex_unlock(&s->ops_mutex); ret = 0; } @@ -187,163 +190,21 @@ static ssize_t pccard_store_resource(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long flags; struct pcmcia_socket *s = to_socket(dev); if (!count) return -EINVAL; - spin_lock_irqsave(&s->lock, flags); + mutex_lock(&s->ops_mutex); if (!s->resource_setup_done) s->resource_setup_done = 1; - spin_unlock_irqrestore(&s->lock, flags); - - mutex_lock(&s->skt_mutex); - if ((s->callback) && - (s->state & SOCKET_PRESENT) && - !(s->state & SOCKET_CARDBUS)) { - if (try_module_get(s->callback->owner)) { - s->callback->requery(s, 0); - module_put(s->callback->owner); - } - } - mutex_unlock(&s->skt_mutex); - - return count; -} -static DEVICE_ATTR(available_resources_setup_done, 0600, pccard_show_resource, pccard_store_resource); + mutex_unlock(&s->ops_mutex); - -static ssize_t pccard_extract_cis(struct pcmcia_socket *s, char *buf, loff_t off, size_t count) -{ - tuple_t tuple; - int status, i; - loff_t pointer = 0; - ssize_t ret = 0; - u_char *tuplebuffer; - u_char *tempbuffer; - - tuplebuffer = kmalloc(sizeof(u_char) * 256, GFP_KERNEL); - if (!tuplebuffer) - return -ENOMEM; - - tempbuffer = kmalloc(sizeof(u_char) * 258, GFP_KERNEL); - if (!tempbuffer) { - ret = -ENOMEM; - goto free_tuple; - } - - memset(&tuple, 0, sizeof(tuple_t)); - - tuple.Attributes = TUPLE_RETURN_LINK | TUPLE_RETURN_COMMON; - tuple.DesiredTuple = RETURN_FIRST_TUPLE; - tuple.TupleOffset = 0; - - status = pccard_get_first_tuple(s, BIND_FN_ALL, &tuple); - while (!status) { - tuple.TupleData = tuplebuffer; - tuple.TupleDataMax = 255; - memset(tuplebuffer, 0, sizeof(u_char) * 255); - - status = pccard_get_tuple_data(s, &tuple); - if (status) - break; - - if (off < (pointer + 2 + tuple.TupleDataLen)) { - tempbuffer[0] = tuple.TupleCode & 0xff; - tempbuffer[1] = tuple.TupleLink & 0xff; - for (i = 0; i < tuple.TupleDataLen; i++) - tempbuffer[i + 2] = tuplebuffer[i] & 0xff; - - for (i = 0; i < (2 + tuple.TupleDataLen); i++) { - if (((i + pointer) >= off) && - (i + pointer) < (off + count)) { - buf[ret] = tempbuffer[i]; - ret++; - } - } - } - - pointer += 2 + tuple.TupleDataLen; - - if (pointer >= (off + count)) - break; - - if (tuple.TupleCode == CISTPL_END) - break; - status = pccard_get_next_tuple(s, BIND_FN_ALL, &tuple); - } - - kfree(tempbuffer); - free_tuple: - kfree(tuplebuffer); - - return ret; -} - -static ssize_t pccard_show_cis(struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t count) -{ - unsigned int size = 0x200; - - if (off >= size) - count = 0; - else { - struct pcmcia_socket *s; - unsigned int chains; - - if (off + count > size) - count = size - off; - - s = to_socket(container_of(kobj, struct device, kobj)); - - if (!(s->state & SOCKET_PRESENT)) - return -ENODEV; - if (pccard_validate_cis(s, &chains)) - return -EIO; - if (!chains) - return -ENODATA; - - count = pccard_extract_cis(s, buf, off, count); - } - - return count; -} - -static ssize_t pccard_store_cis(struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t count) -{ - struct pcmcia_socket *s = to_socket(container_of(kobj, struct device, kobj)); - int error; - - if (off) - return -EINVAL; - - if (count >= CISTPL_MAX_CIS_SIZE) - return -EINVAL; - - if (!(s->state & SOCKET_PRESENT)) - return -ENODEV; - - error = pcmcia_replace_cis(s, buf, count); - if (error) - return -EIO; - - mutex_lock(&s->skt_mutex); - if ((s->callback) && (s->state & SOCKET_PRESENT) && - !(s->state & SOCKET_CARDBUS)) { - if (try_module_get(s->callback->owner)) { - s->callback->requery(s, 1); - module_put(s->callback->owner); - } - } - mutex_unlock(&s->skt_mutex); + pcmcia_parse_uevents(s, PCMCIA_UEVENT_REQUERY); return count; } - +static DEVICE_ATTR(available_resources_setup_done, 0600, pccard_show_resource, pccard_store_resource); static struct attribute *pccard_socket_attributes[] = { &dev_attr_card_type.attr, @@ -362,28 +223,12 @@ static const struct attribute_group socket_attrs = { .attrs = pccard_socket_attributes, }; -static struct bin_attribute pccard_cis_attr = { - .attr = { .name = "cis", .mode = S_IRUGO | S_IWUSR }, - .size = 0x200, - .read = pccard_show_cis, - .write = pccard_store_cis, -}; - int pccard_sysfs_add_socket(struct device *dev) { - int ret = 0; - - ret = sysfs_create_group(&dev->kobj, &socket_attrs); - if (!ret) { - ret = sysfs_create_bin_file(&dev->kobj, &pccard_cis_attr); - if (ret) - sysfs_remove_group(&dev->kobj, &socket_attrs); - } - return ret; + return sysfs_create_group(&dev->kobj, &socket_attrs); } void pccard_sysfs_remove_socket(struct device *dev) { - sysfs_remove_bin_file(&dev->kobj, &pccard_cis_attr); sysfs_remove_group(&dev->kobj, &socket_attrs); } diff --git a/drivers/pcmcia/tcic.c b/drivers/pcmcia/tcic.c index 12c49ee135e1..56004a1b5bba 100644 --- a/drivers/pcmcia/tcic.c +++ b/drivers/pcmcia/tcic.c @@ -39,7 +39,6 @@ #include <linux/string.h> #include <linux/errno.h> #include <linux/interrupt.h> -#include <linux/slab.h> #include <linux/timer.h> #include <linux/ioport.h> #include <linux/delay.h> @@ -348,16 +347,6 @@ static int __init get_tcic_id(void) return id; } -static int tcic_drv_pcmcia_suspend(struct platform_device *dev, - pm_message_t state) -{ - return pcmcia_socket_dev_suspend(&dev->dev); -} - -static int tcic_drv_pcmcia_resume(struct platform_device *dev) -{ - return pcmcia_socket_dev_resume(&dev->dev); -} /*====================================================================*/ static struct platform_driver tcic_driver = { @@ -365,8 +354,6 @@ static struct platform_driver tcic_driver = { .name = "tcic-pcmcia", .owner = THIS_MODULE, }, - .suspend = tcic_drv_pcmcia_suspend, - .resume = tcic_drv_pcmcia_resume, }; static struct platform_device tcic_device = { diff --git a/drivers/pcmcia/ti113x.h b/drivers/pcmcia/ti113x.h index aaa70227bfb0..9ffa97d0b16c 100644 --- a/drivers/pcmcia/ti113x.h +++ b/drivers/pcmcia/ti113x.h @@ -296,7 +296,7 @@ static int ti_init(struct yenta_socket *socket) u8 new, reg = exca_readb(socket, I365_INTCTL); new = reg & ~I365_INTR_ENA; - if (socket->cb_irq) + if (socket->dev->irq) new |= I365_INTR_ENA; if (new != reg) exca_writeb(socket, I365_INTCTL, new); @@ -316,14 +316,47 @@ static int ti_override(struct yenta_socket *socket) return 0; } +static void ti113x_use_isa_irq(struct yenta_socket *socket) +{ + int isa_irq = -1; + u8 intctl; + u32 isa_irq_mask = 0; + + if (!isa_probe) + return; + + /* get a free isa int */ + isa_irq_mask = yenta_probe_irq(socket, isa_interrupts); + if (!isa_irq_mask) + return; /* no useable isa irq found */ + + /* choose highest available */ + for (; isa_irq_mask; isa_irq++) + isa_irq_mask >>= 1; + socket->cb_irq = isa_irq; + + exca_writeb(socket, I365_CSCINT, (isa_irq << 4)); + + intctl = exca_readb(socket, I365_INTCTL); + intctl &= ~(I365_INTR_ENA | I365_IRQ_MASK); /* CSC Enable */ + exca_writeb(socket, I365_INTCTL, intctl); + + dev_info(&socket->dev->dev, + "Yenta TI113x: using isa irq %d for CardBus\n", isa_irq); +} + + static int ti113x_override(struct yenta_socket *socket) { u8 cardctl; cardctl = config_readb(socket, TI113X_CARD_CONTROL); cardctl &= ~(TI113X_CCR_PCI_IRQ_ENA | TI113X_CCR_PCI_IREQ | TI113X_CCR_PCI_CSC); - if (socket->cb_irq) + if (socket->dev->irq) cardctl |= TI113X_CCR_PCI_IRQ_ENA | TI113X_CCR_PCI_CSC | TI113X_CCR_PCI_IREQ; + else + ti113x_use_isa_irq(socket); + config_writeb(socket, TI113X_CARD_CONTROL, cardctl); return ti_override(socket); diff --git a/drivers/pcmcia/vrc4171_card.c b/drivers/pcmcia/vrc4171_card.c index c9fcbdc164ea..86e4a1a3c642 100644 --- a/drivers/pcmcia/vrc4171_card.c +++ b/drivers/pcmcia/vrc4171_card.c @@ -105,6 +105,7 @@ typedef struct vrc4171_socket { char name[24]; int csc_irq; int io_irq; + spinlock_t lock; } vrc4171_socket_t; static vrc4171_socket_t vrc4171_sockets[CARD_MAX_SLOTS]; @@ -327,7 +328,7 @@ static int pccard_set_socket(struct pcmcia_socket *sock, socket_state_t *state) slot = sock->sock; socket = &vrc4171_sockets[slot]; - spin_lock_irq(&sock->lock); + spin_lock_irq(&socket->lock); voltage = set_Vcc_value(state->Vcc); exca_write_byte(slot, CARD_VOLTAGE_SELECT, voltage); @@ -370,7 +371,7 @@ static int pccard_set_socket(struct pcmcia_socket *sock, socket_state_t *state) cscint |= I365_CSC_DETECT; exca_write_byte(slot, I365_CSCINT, cscint); - spin_unlock_irq(&sock->lock); + spin_unlock_irq(&socket->lock); return 0; } @@ -704,24 +705,11 @@ static int __devinit vrc4171_card_setup(char *options) __setup("vrc4171_card=", vrc4171_card_setup); -static int vrc4171_card_suspend(struct platform_device *dev, - pm_message_t state) -{ - return pcmcia_socket_dev_suspend(&dev->dev); -} - -static int vrc4171_card_resume(struct platform_device *dev) -{ - return pcmcia_socket_dev_resume(&dev->dev); -} - static struct platform_driver vrc4171_card_driver = { .driver = { .name = vrc4171_card_name, .owner = THIS_MODULE, }, - .suspend = vrc4171_card_suspend, - .resume = vrc4171_card_resume, }; static int __devinit vrc4171_card_init(void) diff --git a/drivers/pcmcia/xxs1500_ss.c b/drivers/pcmcia/xxs1500_ss.c new file mode 100644 index 000000000000..201ccfa1e97b --- /dev/null +++ b/drivers/pcmcia/xxs1500_ss.c @@ -0,0 +1,341 @@ +/* + * PCMCIA socket code for the MyCable XXS1500 system. + * + * Copyright (c) 2009 Manuel Lauss <manuel.lauss@gmail.com> + * + */ + +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/mm.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/resource.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +#include <pcmcia/cs_types.h> +#include <pcmcia/cs.h> +#include <pcmcia/ss.h> +#include <pcmcia/cistpl.h> + +#include <asm/irq.h> +#include <asm/system.h> +#include <asm/mach-au1x00/au1000.h> + +#define MEM_MAP_SIZE 0x400000 +#define IO_MAP_SIZE 0x1000 + + +/* + * 3.3V cards only; all interfacing is done via gpios: + * + * 0/1: carddetect (00 = card present, xx = huh) + * 4: card irq + * 204: reset (high-act) + * 205: buffer enable (low-act) + * 208/209: card voltage key (00,01,10,11) + * 210: battwarn + * 211: batdead + * 214: power (low-act) + */ +#define GPIO_CDA 0 +#define GPIO_CDB 1 +#define GPIO_CARDIRQ 4 +#define GPIO_RESET 204 +#define GPIO_OUTEN 205 +#define GPIO_VSL 208 +#define GPIO_VSH 209 +#define GPIO_BATTDEAD 210 +#define GPIO_BATTWARN 211 +#define GPIO_POWER 214 + +struct xxs1500_pcmcia_sock { + struct pcmcia_socket socket; + void *virt_io; + + phys_addr_t phys_io; + phys_addr_t phys_attr; + phys_addr_t phys_mem; + + /* previous flags for set_socket() */ + unsigned int old_flags; +}; + +#define to_xxs_socket(x) container_of(x, struct xxs1500_pcmcia_sock, socket) + +static irqreturn_t cdirq(int irq, void *data) +{ + struct xxs1500_pcmcia_sock *sock = data; + + pcmcia_parse_events(&sock->socket, SS_DETECT); + + return IRQ_HANDLED; +} + +static int xxs1500_pcmcia_configure(struct pcmcia_socket *skt, + struct socket_state_t *state) +{ + struct xxs1500_pcmcia_sock *sock = to_xxs_socket(skt); + unsigned int changed; + + /* power control */ + switch (state->Vcc) { + case 0: + gpio_set_value(GPIO_POWER, 1); /* power off */ + break; + case 33: + gpio_set_value(GPIO_POWER, 0); /* power on */ + break; + case 50: + default: + return -EINVAL; + } + + changed = state->flags ^ sock->old_flags; + + if (changed & SS_RESET) { + if (state->flags & SS_RESET) { + gpio_set_value(GPIO_RESET, 1); /* assert reset */ + gpio_set_value(GPIO_OUTEN, 1); /* buffers off */ + } else { + gpio_set_value(GPIO_RESET, 0); /* deassert reset */ + gpio_set_value(GPIO_OUTEN, 0); /* buffers on */ + msleep(500); + } + } + + sock->old_flags = state->flags; + + return 0; +} + +static int xxs1500_pcmcia_get_status(struct pcmcia_socket *skt, + unsigned int *value) +{ + unsigned int status; + int i; + + status = 0; + + /* check carddetects: GPIO[0:1] must both be low */ + if (!gpio_get_value(GPIO_CDA) && !gpio_get_value(GPIO_CDB)) + status |= SS_DETECT; + + /* determine card voltage: GPIO[208:209] binary value */ + i = (!!gpio_get_value(GPIO_VSL)) | ((!!gpio_get_value(GPIO_VSH)) << 1); + + switch (i) { + case 0: + case 1: + case 2: + status |= SS_3VCARD; /* 3V card */ + break; + case 3: /* 5V card, unsupported */ + default: + status |= SS_XVCARD; /* treated as unsupported in core */ + } + + /* GPIO214: low active power switch */ + status |= gpio_get_value(GPIO_POWER) ? 0 : SS_POWERON; + + /* GPIO204: high-active reset line */ + status |= gpio_get_value(GPIO_RESET) ? SS_RESET : SS_READY; + + /* other stuff */ + status |= gpio_get_value(GPIO_BATTDEAD) ? 0 : SS_BATDEAD; + status |= gpio_get_value(GPIO_BATTWARN) ? 0 : SS_BATWARN; + + *value = status; + + return 0; +} + +static int xxs1500_pcmcia_sock_init(struct pcmcia_socket *skt) +{ + gpio_direction_input(GPIO_CDA); + gpio_direction_input(GPIO_CDB); + gpio_direction_input(GPIO_VSL); + gpio_direction_input(GPIO_VSH); + gpio_direction_input(GPIO_BATTDEAD); + gpio_direction_input(GPIO_BATTWARN); + gpio_direction_output(GPIO_RESET, 1); /* assert reset */ + gpio_direction_output(GPIO_OUTEN, 1); /* disable buffers */ + gpio_direction_output(GPIO_POWER, 1); /* power off */ + + return 0; +} + +static int xxs1500_pcmcia_sock_suspend(struct pcmcia_socket *skt) +{ + return 0; +} + +static int au1x00_pcmcia_set_io_map(struct pcmcia_socket *skt, + struct pccard_io_map *map) +{ + struct xxs1500_pcmcia_sock *sock = to_xxs_socket(skt); + + map->start = (u32)sock->virt_io; + map->stop = map->start + IO_MAP_SIZE; + + return 0; +} + +static int au1x00_pcmcia_set_mem_map(struct pcmcia_socket *skt, + struct pccard_mem_map *map) +{ + struct xxs1500_pcmcia_sock *sock = to_xxs_socket(skt); + + if (map->flags & MAP_ATTRIB) + map->static_start = sock->phys_attr + map->card_start; + else + map->static_start = sock->phys_mem + map->card_start; + + return 0; +} + +static struct pccard_operations xxs1500_pcmcia_operations = { + .init = xxs1500_pcmcia_sock_init, + .suspend = xxs1500_pcmcia_sock_suspend, + .get_status = xxs1500_pcmcia_get_status, + .set_socket = xxs1500_pcmcia_configure, + .set_io_map = au1x00_pcmcia_set_io_map, + .set_mem_map = au1x00_pcmcia_set_mem_map, +}; + +static int __devinit xxs1500_pcmcia_probe(struct platform_device *pdev) +{ + struct xxs1500_pcmcia_sock *sock; + struct resource *r; + int ret, irq; + + sock = kzalloc(sizeof(struct xxs1500_pcmcia_sock), GFP_KERNEL); + if (!sock) + return -ENOMEM; + + ret = -ENODEV; + + /* 36bit PCMCIA Attribute area address */ + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-attr"); + if (!r) { + dev_err(&pdev->dev, "missing 'pcmcia-attr' resource!\n"); + goto out0; + } + sock->phys_attr = r->start; + + /* 36bit PCMCIA Memory area address */ + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-mem"); + if (!r) { + dev_err(&pdev->dev, "missing 'pcmcia-mem' resource!\n"); + goto out0; + } + sock->phys_mem = r->start; + + /* 36bit PCMCIA IO area address */ + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-io"); + if (!r) { + dev_err(&pdev->dev, "missing 'pcmcia-io' resource!\n"); + goto out0; + } + sock->phys_io = r->start; + + + /* + * PCMCIA client drivers use the inb/outb macros to access + * the IO registers. Since mips_io_port_base is added + * to the access address of the mips implementation of + * inb/outb, we need to subtract it here because we want + * to access the I/O or MEM address directly, without + * going through this "mips_io_port_base" mechanism. + */ + sock->virt_io = (void *)(ioremap(sock->phys_io, IO_MAP_SIZE) - + mips_io_port_base); + + if (!sock->virt_io) { + dev_err(&pdev->dev, "cannot remap IO area\n"); + ret = -ENOMEM; + goto out0; + } + + sock->socket.ops = &xxs1500_pcmcia_operations; + sock->socket.owner = THIS_MODULE; + sock->socket.pci_irq = gpio_to_irq(GPIO_CARDIRQ); + sock->socket.features = SS_CAP_STATIC_MAP | SS_CAP_PCCARD; + sock->socket.map_size = MEM_MAP_SIZE; + sock->socket.io_offset = (unsigned long)sock->virt_io; + sock->socket.dev.parent = &pdev->dev; + sock->socket.resource_ops = &pccard_static_ops; + + platform_set_drvdata(pdev, sock); + + /* setup carddetect irq: use one of the 2 GPIOs as an + * edge detector. + */ + irq = gpio_to_irq(GPIO_CDA); + set_irq_type(irq, IRQ_TYPE_EDGE_BOTH); + ret = request_irq(irq, cdirq, 0, "pcmcia_carddetect", sock); + if (ret) { + dev_err(&pdev->dev, "cannot setup cd irq\n"); + goto out1; + } + + ret = pcmcia_register_socket(&sock->socket); + if (ret) { + dev_err(&pdev->dev, "failed to register\n"); + goto out2; + } + + printk(KERN_INFO "MyCable XXS1500 PCMCIA socket services\n"); + + return 0; + +out2: + free_irq(gpio_to_irq(GPIO_CDA), sock); +out1: + iounmap((void *)(sock->virt_io + (u32)mips_io_port_base)); +out0: + kfree(sock); + return ret; +} + +static int __devexit xxs1500_pcmcia_remove(struct platform_device *pdev) +{ + struct xxs1500_pcmcia_sock *sock = platform_get_drvdata(pdev); + + pcmcia_unregister_socket(&sock->socket); + free_irq(gpio_to_irq(GPIO_CDA), sock); + iounmap((void *)(sock->virt_io + (u32)mips_io_port_base)); + kfree(sock); + + return 0; +} + +static struct platform_driver xxs1500_pcmcia_socket_driver = { + .driver = { + .name = "xxs1500_pcmcia", + .owner = THIS_MODULE, + }, + .probe = xxs1500_pcmcia_probe, + .remove = __devexit_p(xxs1500_pcmcia_remove), +}; + +int __init xxs1500_pcmcia_socket_load(void) +{ + return platform_driver_register(&xxs1500_pcmcia_socket_driver); +} + +void __exit xxs1500_pcmcia_socket_unload(void) +{ + platform_driver_unregister(&xxs1500_pcmcia_socket_driver); +} + +module_init(xxs1500_pcmcia_socket_load); +module_exit(xxs1500_pcmcia_socket_unload); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("PCMCIA Socket Services for MyCable XXS1500 systems"); +MODULE_AUTHOR("Manuel Lauss"); diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c index e4d12acdd525..424e576f3acb 100644 --- a/drivers/pcmcia/yenta_socket.c +++ b/drivers/pcmcia/yenta_socket.c @@ -17,6 +17,7 @@ #include <linux/delay.h> #include <linux/module.h> #include <linux/io.h> +#include <linux/slab.h> #include <pcmcia/cs_types.h> #include <pcmcia/ss.h> @@ -37,6 +38,23 @@ static int pwr_irqs_off; module_param(pwr_irqs_off, bool, 0644); MODULE_PARM_DESC(pwr_irqs_off, "Force IRQs off during power-on of slot. Use only when seeing IRQ storms!"); +static char o2_speedup[] = "default"; +module_param_string(o2_speedup, o2_speedup, sizeof(o2_speedup), 0444); +MODULE_PARM_DESC(o2_speedup, "Use prefetch/burst for O2-bridges: 'on', 'off' " + "or 'default' (uses recommended behaviour for the detected bridge)"); + +/* + * Only probe "regular" interrupts, don't + * touch dangerous spots like the mouse irq, + * because there are mice that apparently + * get really confused if they get fondled + * too intimately. + * + * Default to 11, 10, 9, 7, 6, 5, 4, 3. + */ +static u32 isa_interrupts = 0x0ef8; + + #define debug(x, s, args...) dev_dbg(&s->dev->dev, x, ##args) /* Don't ask.. */ @@ -49,6 +67,8 @@ MODULE_PARM_DESC(pwr_irqs_off, "Force IRQs off during power-on of slot. Use only */ #ifdef CONFIG_YENTA_TI static int yenta_probe_cb_irq(struct yenta_socket *socket); +static unsigned int yenta_probe_irq(struct yenta_socket *socket, + u32 isa_irq_mask); #endif @@ -324,8 +344,8 @@ static int yenta_set_socket(struct pcmcia_socket *sock, socket_state_t *state) /* ISA interrupt control? */ intr = exca_readb(socket, I365_INTCTL); intr = (intr & ~0xf); - if (!socket->cb_irq) { - intr |= state->io_irq; + if (!socket->dev->irq) { + intr |= socket->cb_irq ? socket->cb_irq : state->io_irq; bridge |= CB_BRIDGE_INTR; } exca_writeb(socket, I365_INTCTL, intr); @@ -335,7 +355,7 @@ static int yenta_set_socket(struct pcmcia_socket *sock, socket_state_t *state) reg = exca_readb(socket, I365_INTCTL) & (I365_RING_ENA | I365_INTR_ENA); reg |= (state->flags & SS_RESET) ? 0 : I365_PC_RESET; reg |= (state->flags & SS_IOCARD) ? I365_PC_IOCARD : 0; - if (state->io_irq != socket->cb_irq) { + if (state->io_irq != socket->dev->irq) { reg |= state->io_irq; bridge |= CB_BRIDGE_INTR; } @@ -351,7 +371,9 @@ static int yenta_set_socket(struct pcmcia_socket *sock, socket_state_t *state) exca_writeb(socket, I365_POWER, reg); /* CSC interrupt: no ISA irq for CSC */ - reg = I365_CSC_DETECT; + reg = exca_readb(socket, I365_CSCINT); + reg &= I365_CSC_IRQ_MASK; + reg |= I365_CSC_DETECT; if (state->flags & SS_IOCARD) { if (state->csc_mask & SS_STSCHG) reg |= I365_CSC_STSCHG; @@ -649,9 +671,10 @@ static int yenta_search_one_res(struct resource *root, struct resource *res, static int yenta_search_res(struct yenta_socket *socket, struct resource *res, u32 min) { + struct resource *root; int i; - for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) { - struct resource *root = socket->dev->bus->resource[i]; + + pci_bus_for_each_resource(socket->dev->bus, root, i) { if (!root) continue; @@ -890,22 +913,12 @@ static struct cardbus_type cardbus_type[] = { }; -/* - * Only probe "regular" interrupts, don't - * touch dangerous spots like the mouse irq, - * because there are mice that apparently - * get really confused if they get fondled - * too intimately. - * - * Default to 11, 10, 9, 7, 6, 5, 4, 3. - */ -static u32 isa_interrupts = 0x0ef8; - static unsigned int yenta_probe_irq(struct yenta_socket *socket, u32 isa_irq_mask) { int i; unsigned long val; u32 mask; + u8 reg; /* * Probe for usable interrupts using the force @@ -913,6 +926,7 @@ static unsigned int yenta_probe_irq(struct yenta_socket *socket, u32 isa_irq_mas */ cb_writel(socket, CB_SOCKET_EVENT, -1); cb_writel(socket, CB_SOCKET_MASK, CB_CSTSMASK); + reg = exca_readb(socket, I365_CSCINT); exca_writeb(socket, I365_CSCINT, 0); val = probe_irq_on() & isa_irq_mask; for (i = 1; i < 16; i++) { @@ -924,7 +938,7 @@ static unsigned int yenta_probe_irq(struct yenta_socket *socket, u32 isa_irq_mas cb_writel(socket, CB_SOCKET_EVENT, -1); } cb_writel(socket, CB_SOCKET_MASK, 0); - exca_writeb(socket, I365_CSCINT, 0); + exca_writeb(socket, I365_CSCINT, reg); mask = probe_irq_mask(val) & 0xffff; @@ -961,6 +975,8 @@ static irqreturn_t yenta_probe_handler(int irq, void *dev_id) /* probes the PCI interrupt, use only on override functions */ static int yenta_probe_cb_irq(struct yenta_socket *socket) { + u8 reg; + if (!socket->cb_irq) return -1; @@ -973,7 +989,8 @@ static int yenta_probe_cb_irq(struct yenta_socket *socket) } /* generate interrupt, wait */ - exca_writeb(socket, I365_CSCINT, I365_CSC_STSCHG); + reg = exca_readb(socket, I365_CSCINT); + exca_writeb(socket, I365_CSCINT, reg | I365_CSC_STSCHG); cb_writel(socket, CB_SOCKET_EVENT, -1); cb_writel(socket, CB_SOCKET_MASK, CB_CSTSMASK); cb_writel(socket, CB_SOCKET_FORCE, CB_FCARDSTS); @@ -982,7 +999,7 @@ static int yenta_probe_cb_irq(struct yenta_socket *socket) /* disable interrupts */ cb_writel(socket, CB_SOCKET_MASK, 0); - exca_writeb(socket, I365_CSCINT, 0); + exca_writeb(socket, I365_CSCINT, reg); cb_writel(socket, CB_SOCKET_EVENT, -1); exca_readb(socket, I365_CSC); @@ -1274,12 +1291,9 @@ static int yenta_dev_suspend_noirq(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); struct yenta_socket *socket = pci_get_drvdata(pdev); - int ret; - - ret = pcmcia_socket_dev_suspend(dev); if (!socket) - return ret; + return 0; if (socket->type && socket->type->save_state) socket->type->save_state(socket); @@ -1289,14 +1303,7 @@ static int yenta_dev_suspend_noirq(struct device *dev) pci_read_config_dword(pdev, 17*4, &socket->saved_state[1]); pci_disable_device(pdev); - /* - * Some laptops (IBM T22) do not like us putting the Cardbus - * bridge into D3. At a guess, some other laptop will - * probably require this, so leave it commented out for now. - */ - /* pci_set_power_state(dev, 3); */ - - return ret; + return 0; } static int yenta_dev_resume_noirq(struct device *dev) @@ -1320,26 +1327,16 @@ static int yenta_dev_resume_noirq(struct device *dev) if (socket->type && socket->type->restore_state) socket->type->restore_state(socket); - pcmcia_socket_dev_early_resume(dev); - return 0; -} - -static int yenta_dev_resume(struct device *dev) -{ - pcmcia_socket_dev_late_resume(dev); return 0; } static const struct dev_pm_ops yenta_pm_ops = { .suspend_noirq = yenta_dev_suspend_noirq, .resume_noirq = yenta_dev_resume_noirq, - .resume = yenta_dev_resume, .freeze_noirq = yenta_dev_suspend_noirq, .thaw_noirq = yenta_dev_resume_noirq, - .thaw = yenta_dev_resume, .poweroff_noirq = yenta_dev_suspend_noirq, .restore_noirq = yenta_dev_resume_noirq, - .restore = yenta_dev_resume, }; #define YENTA_PM_OPS (¥ta_pm_ops) @@ -1402,10 +1399,10 @@ static struct pci_device_id yenta_table[] = { CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_7510, TI12XX), CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_7610, TI12XX), - CB_ID(PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_710, TI12XX), - CB_ID(PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_712, TI12XX), - CB_ID(PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_720, TI12XX), - CB_ID(PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_722, TI12XX), + CB_ID(PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_710, ENE), + CB_ID(PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_712, ENE), + CB_ID(PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_720, ENE), + CB_ID(PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_722, ENE), CB_ID(PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_1211, ENE), CB_ID(PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_1225, ENE), CB_ID(PCI_VENDOR_ID_ENE, PCI_DEVICE_ID_ENE_1410, ENE), |