[PATCH] MIPS: BCM63xx: Add PCMCIA & Cardbus support.

Wolfram Sang w.sang at pengutronix.de
Wed Sep 23 08:31:43 EDT 2009


On Fri, Sep 18, 2009 at 01:21:31PM +0200, Maxime Bizon wrote:
> 
> Hi Wolfram & Greg,
> 
> It seems Dominik is busy, and you're the one acking pcmcia patch at the
> moment so I'm sending this to you.
> 
> 
> This is a pcmcia/cardbus driver for the bcm63xx mips SOCs, it was
> already posted twice:
> 
> last year: http://lists.infradead.org/pipermail/linux-pcmcia/2008-October/005942.html
> three months ago: http://lists.infradead.org/pipermail/linux-pcmcia/2009-July/006196.html
> 
> and got no review.
> 
> Could you please have a look at it ?

Okay, here is a fast review. If you fix the mentioned points (or give me good
reasons why not ;)), then you might add my

Reviewed-by: Wolfram Sang <w.sang at pengutronix.de>

I am fine with Ralf picking this up.

> 
> Thanks !
> 
> 
> Signed-off-by: Maxime Bizon <mbizon at freebox.fr>
> Signed-off-by: Ralf Baechle <ralf at linux-mips.org>
> ---
> 
> diff --git a/arch/mips/bcm63xx/Makefile b/arch/mips/bcm63xx/Makefile
> index cff75de..c146d1e 100644
> --- a/arch/mips/bcm63xx/Makefile
> +++ b/arch/mips/bcm63xx/Makefile
> @@ -1,5 +1,5 @@
>  obj-y		+= clk.o cpu.o cs.o gpio.o irq.o prom.o setup.o timer.o \
> -		   dev-dsp.o dev-enet.o dev-uart.o
> +		   dev-dsp.o dev-enet.o dev-pcmcia.o dev-uart.o
>  obj-$(CONFIG_EARLY_PRINTK)	+= early_printk.o
>  
>  obj-y		+= boards/
> diff --git a/arch/mips/bcm63xx/boards/board_bcm963xx.c b/arch/mips/bcm63xx/boards/board_bcm963xx.c
> index d3fdf27..e3e6205 100644
> --- a/arch/mips/bcm63xx/boards/board_bcm963xx.c
> +++ b/arch/mips/bcm63xx/boards/board_bcm963xx.c
> @@ -24,6 +24,7 @@
>  #include <bcm63xx_dev_pci.h>
>  #include <bcm63xx_dev_enet.h>
>  #include <bcm63xx_dev_dsp.h>
> +#include <bcm63xx_dev_pcmcia.h>
>  #include <bcm63xx_dev_uart.h>
>  #include <board_bcm963xx.h>
>  
> @@ -796,6 +797,9 @@ int __init board_register_devices(void)
>  
>  	bcm63xx_uart_register();
>  
> +	if (board.has_pccard)
> +		bcm63xx_pcmcia_register();
> +
>  	if (board.has_enet0 &&
>  	    !board_get_mac_address(board.enet0.mac_addr))
>  		bcm63xx_enet_register(0, &board.enet0);
> diff --git a/arch/mips/bcm63xx/dev-pcmcia.c b/arch/mips/bcm63xx/dev-pcmcia.c
> new file mode 100644
> index 0000000..40ec4bc
> --- /dev/null
> +++ b/arch/mips/bcm63xx/dev-pcmcia.c
> @@ -0,0 +1,135 @@
> +/*
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License.  See the file "COPYING" in the main directory of this archive
> + * for more details.
> + *
> + * Copyright (C) 2008 Maxime Bizon <mbizon at freebox.fr>
> + */
> +
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <asm/bootinfo.h>
> +#include <linux/platform_device.h>
> +#include <bcm63xx_cs.h>
> +#include <bcm63xx_cpu.h>
> +#include <bcm63xx_dev_pcmcia.h>
> +#include <bcm63xx_io.h>
> +#include <bcm63xx_regs.h>
> +
> +static struct resource pcmcia_resources[] = {
> +	/* pcmcia registers */
> +	{
> +		.start		= -1, /* filled at runtime */
> +		.end		= -1, /* filled at runtime */
> +		.flags		= IORESOURCE_MEM,
> +	},
> +
> +	/* pcmcia memory zone resources */
> +	{
> +		.start		= BCM_PCMCIA_COMMON_BASE_PA,
> +		.end		= BCM_PCMCIA_COMMON_END_PA,
> +		.flags		= IORESOURCE_MEM,
> +	},
> +	{
> +		.start		= BCM_PCMCIA_ATTR_BASE_PA,
> +		.end		= BCM_PCMCIA_ATTR_END_PA,
> +		.flags		= IORESOURCE_MEM,
> +	},
> +	{
> +		.start		= BCM_PCMCIA_IO_BASE_PA,
> +		.end		= BCM_PCMCIA_IO_END_PA,
> +		.flags		= IORESOURCE_MEM,
> +	},
> +
> +	/* PCMCIA irq */
> +	{
> +		.start		= -1, /* filled at runtime */
> +		.flags		= IORESOURCE_IRQ,
> +	},
> +
> +	/* declare PCMCIA IO resource also */
> +	{
> +		.start		= BCM_PCMCIA_IO_BASE_PA,
> +		.end		= BCM_PCMCIA_IO_END_PA,
> +		.flags		= IORESOURCE_IO,
> +	},
> +};
> +
> +static struct bcm63xx_pcmcia_platform_data pd;
> +
> +static struct platform_device bcm63xx_pcmcia_device = {
> +	.name		= "bcm63xx_pcmcia",
> +	.id		= 0,
> +	.num_resources	= ARRAY_SIZE(pcmcia_resources),
> +	.resource	= pcmcia_resources,
> +	.dev		= {
> +		.platform_data = &pd,
> +	},
> +};
> +
> +static int __init config_pcmcia_cs(unsigned int cs,
> +				   u32 base, unsigned int size)
> +{
> +	int ret;
> +
> +	ret = bcm63xx_set_cs_status(cs, 0);
> +	if (!ret)
> +		ret = bcm63xx_set_cs_base(cs, base, size);
> +	if (!ret)
> +		ret = bcm63xx_set_cs_status(cs, 1);
> +	return ret;
> +}
> +
> +static const __initdata unsigned int pcmcia_cs[3][3] = {
> +	/* cs, base address, size */
> +	{ MPI_CS_PCMCIA_COMMON, BCM_PCMCIA_COMMON_BASE_PA,
> +	  BCM_PCMCIA_COMMON_SIZE },
> +
> +	{ MPI_CS_PCMCIA_ATTR, BCM_PCMCIA_ATTR_BASE_PA,
> +	  BCM_PCMCIA_ATTR_SIZE },
> +
> +	{ MPI_CS_PCMCIA_IO, BCM_PCMCIA_IO_BASE_PA,
> +	  BCM_PCMCIA_IO_SIZE },
> +};
> +
> +int __init bcm63xx_pcmcia_register(void)
> +{
> +	int ret, i;
> +
> +	if (!BCMCPU_IS_6348() && !BCMCPU_IS_6358())
> +		return 0;
> +
> +	/* use correct pcmcia ready gpio depending on processor */
> +	switch (bcm63xx_get_cpu_id()) {
> +	case BCM6348_CPU_ID:
> +		pd.ready_gpio = 22;
> +		break;
> +
> +	case BCM6358_CPU_ID:
> +		pd.ready_gpio = 22;
> +		break;

Those two cases can be combined.

> +
> +	default:
> +		return -ENODEV;
> +	}
> +
> +	pcmcia_resources[0].start = bcm63xx_regset_address(RSET_PCMCIA);
> +	pcmcia_resources[0].end = pcmcia_resources[0].start;
> +	pcmcia_resources[0].end += RSET_PCMCIA_SIZE - 1;

I'd do the calculation of .end in one line; as it looks a bit confusing on a
first glimpse. Very minor nit, though.

> +	pcmcia_resources[4].start = bcm63xx_get_irq_number(IRQ_PCMCIA);
> +
> +	/* configure pcmcia chip selects */
> +	for (i = 0; i < 3; i++) {
> +		ret = config_pcmcia_cs(pcmcia_cs[i][0],
> +				       pcmcia_cs[i][1],
> +				       pcmcia_cs[i][2]);
> +		if (ret)
> +			goto out_err;
> +	}
> +
> +	return platform_device_register(&bcm63xx_pcmcia_device);
> +
> +out_err:
> +	printk(KERN_ERR "unable to set pcmcia chip select");

Missing "\n"

> +	return ret;
> +}
> diff --git a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_dev_pcmcia.h b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_dev_pcmcia.h
> new file mode 100644
> index 0000000..2beb396
> --- /dev/null
> +++ b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_dev_pcmcia.h
> @@ -0,0 +1,13 @@
> +#ifndef BCM63XX_DEV_PCMCIA_H_
> +#define BCM63XX_DEV_PCMCIA_H_
> +
> +/*
> + * PCMCIA driver platform data
> + */
> +struct bcm63xx_pcmcia_platform_data {
> +	unsigned int ready_gpio;
> +};
> +
> +int bcm63xx_pcmcia_register(void);
> +
> +#endif /* BCM63XX_DEV_PCMCIA_H_ */
> diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig
> index fbf965b..17f38a7 100644
> --- a/drivers/pcmcia/Kconfig
> +++ b/drivers/pcmcia/Kconfig
> @@ -192,6 +192,10 @@ config PCMCIA_AU1X00
>  	tristate "Au1x00 pcmcia support"
>  	depends on SOC_AU1X00 && PCMCIA
>  
> +config PCMCIA_BCM63XX
> +	tristate "bcm63xx pcmcia support"
> +	depends on BCM63XX && PCMCIA
> +
>  config PCMCIA_SA1100
>  	tristate "SA1100 support"
>  	depends on ARM && ARCH_SA1100 && PCMCIA
> diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile
> index 047394d..1ee57f0 100644
> --- a/drivers/pcmcia/Makefile
> +++ b/drivers/pcmcia/Makefile
> @@ -27,6 +27,7 @@ obj-$(CONFIG_PCMCIA_SA1111)			+= sa11xx_core.o sa1111_cs.o
>  obj-$(CONFIG_M32R_PCC)				+= m32r_pcc.o
>  obj-$(CONFIG_M32R_CFC)				+= m32r_cfc.o
>  obj-$(CONFIG_PCMCIA_AU1X00)			+= au1x00_ss.o
> +obj-$(CONFIG_PCMCIA_BCM63XX)			+= bcm63xx_pcmcia.o
>  obj-$(CONFIG_PCMCIA_VRC4171)			+= vrc4171_card.o
>  obj-$(CONFIG_PCMCIA_VRC4173)			+= vrc4173_cardu.o
>  obj-$(CONFIG_OMAP_CF)				+= omap_cf.o
> diff --git a/drivers/pcmcia/bcm63xx_pcmcia.c b/drivers/pcmcia/bcm63xx_pcmcia.c
> new file mode 100644
> index 0000000..2981aff
> --- /dev/null
> +++ b/drivers/pcmcia/bcm63xx_pcmcia.c
> @@ -0,0 +1,537 @@
> +/*
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License.  See the file "COPYING" in the main directory of this archive
> + * for more details.
> + *
> + * Copyright (C) 2008 Maxime Bizon <mbizon at freebox.fr>
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/ioport.h>
> +#include <linux/timer.h>
> +#include <linux/platform_device.h>
> +#include <linux/delay.h>
> +#include <linux/pci.h>
> +#include <linux/gpio.h>
> +
> +#include <bcm63xx_regs.h>
> +#include <bcm63xx_io.h>
> +#include "bcm63xx_pcmcia.h"
> +
> +#define PFX	"bcm63xx_pcmcia: "
> +
> +#ifdef CONFIG_CARDBUS
> +/* if cardbus is used, platform device needs reference to actual pci
> + * device */
> +static struct pci_dev *bcm63xx_cb_dev;
> +#endif
> +
> +/*
> + * read/write helper for pcmcia regs
> + */
> +static inline u32 pcmcia_readl(struct bcm63xx_pcmcia_socket *skt, u32 off)
> +{
> +	return bcm_readl(skt->base + off);
> +}
> +
> +static inline void pcmcia_writel(struct bcm63xx_pcmcia_socket *skt,
> +				 u32 val, u32 off)
> +{
> +	bcm_writel(val, skt->base + off);
> +}
> +
> +/*
> + * (Re-)Initialise the socket, turning on status interrupts and PCMCIA
> + * bus.  This must wait for power to stabilise so that the card status
> + * signals report correctly.
> + */
> +static int bcm63xx_pcmcia_sock_init(struct pcmcia_socket *sock)
> +{
> +	struct bcm63xx_pcmcia_socket *skt;
> +	skt = sock->driver_data;
> +	return 0;
> +}

Comment and function do not match, don't they?

> +
> +/*
> + * Remove power on the socket, disable IRQs from the card.
> + * Turn off status interrupts, and disable the PCMCIA bus.
> + */
> +static int bcm63xx_pcmcia_suspend(struct pcmcia_socket *sock)
> +{
> +	struct bcm63xx_pcmcia_socket *skt;
> +	skt = sock->driver_data;
> +	return 0;
> +}

ditto.

> +
> +/*
> + * Implements the set_socket() operation for the in-kernel PCMCIA
> + * service (formerly SS_SetSocket in Card Services). We more or
> + * less punt all of this work and let the kernel handle the details
> + * of power configuration, reset, &c. We also record the value of
> + * `state' in order to regurgitate it to the PCMCIA core later.
> + */
> +static int bcm63xx_pcmcia_set_socket(struct pcmcia_socket *sock,
> +				     socket_state_t *state)
> +{
> +	struct bcm63xx_pcmcia_socket *skt;
> +	unsigned long flags;
> +	u32 val;
> +
> +	skt = sock->driver_data;
> +
> +	spin_lock_irqsave(&skt->lock, flags);
> +
> +	/* apply requested socket power */
> +	/* FIXME: hardware can't do this */
> +
> +	/* apply socket reset */
> +	val = pcmcia_readl(skt, PCMCIA_C1_REG);
> +	if (state->flags & SS_RESET)
> +		val |= PCMCIA_C1_RESET_MASK;
> +	else
> +		val &= ~PCMCIA_C1_RESET_MASK;
> +
> +	/* reverse reset logic for cardbus card */
> +	if (skt->card_detected && (skt->card_type & CARD_CARDBUS))
> +		val ^= PCMCIA_C1_RESET_MASK;
> +
> +	pcmcia_writel(skt, val, PCMCIA_C1_REG);
> +
> +	/* keep requested state for event reporting */
> +	skt->requested_state = *state;
> +
> +	spin_unlock_irqrestore(&skt->lock, flags);
> +
> +	return 0;
> +}
> +
> +/*
> + * identity cardtype from VS[12] input, CD[12] input while only VS2 is
> + * floating, and CD[12] input while only VS1 is floating
> + */
> +enum {
> +	IN_VS1 = (1 << 0),
> +	IN_VS2 = (1 << 1),
> +	IN_CD1_VS2H = (1 << 2),
> +	IN_CD2_VS2H = (1 << 3),
> +	IN_CD1_VS1H = (1 << 4),
> +	IN_CD2_VS1H = (1 << 5),
> +};
> +
> +static const u8 vscd_to_cardtype[] = {
> +
> +	/* VS1 float, VS2 float */
> +	[IN_VS1 | IN_VS2] = (CARD_PCCARD | CARD_5V),
> +
> +	/* VS1 grounded, VS2 float */
> +	[IN_VS2] = (CARD_PCCARD | CARD_5V | CARD_3V),
> +
> +	/* VS1 grounded, VS2 grounded */
> +	[0] = (CARD_PCCARD | CARD_5V | CARD_3V | CARD_XV),
> +
> +	/* VS1 tied to CD1, VS2 float */
> +	[IN_VS1 | IN_VS2 | IN_CD1_VS1H] = (CARD_CARDBUS | CARD_3V),
> +
> +	/* VS1 grounded, VS2 tied to CD2 */
> +	[IN_VS2 | IN_CD2_VS2H] = (CARD_CARDBUS | CARD_3V | CARD_XV),
> +
> +	/* VS1 tied to CD2, VS2 grounded */
> +	[IN_VS1 | IN_CD2_VS1H] = (CARD_CARDBUS | CARD_3V | CARD_XV | CARD_YV),
> +
> +	/* VS1 float, VS2 grounded */
> +	[IN_VS1] = (CARD_PCCARD | CARD_XV),
> +
> +	/* VS1 float, VS2 tied to CD2 */
> +	[IN_VS1 | IN_VS2 | IN_CD2_VS2H] = (CARD_CARDBUS | CARD_3V),
> +
> +	/* VS1 float, VS2 tied to CD1 */
> +	[IN_VS1 | IN_VS2 | IN_CD1_VS2H] = (CARD_CARDBUS | CARD_XV | CARD_YV),
> +
> +	/* VS1 tied to CD2, VS2 float */
> +	[IN_VS1 | IN_VS2 | IN_CD2_VS1H] = (CARD_CARDBUS | CARD_YV),
> +
> +	/* VS2 grounded, VS1 is tied to CD1, CD2 is grounded */
> +	[IN_VS1 | IN_CD1_VS1H] = 0, /* ignore cardbay */
> +};
> +
> +/*
> + * poll hardware to check card insertion status
> + */
> +static unsigned int __get_socket_status(struct bcm63xx_pcmcia_socket *skt)
> +{
> +	unsigned int stat;
> +	u32 val;
> +
> +	stat = 0;
> +
> +	/* check CD for card presence */
> +	val = pcmcia_readl(skt, PCMCIA_C1_REG);
> +
> +	if (!(val & PCMCIA_C1_CD1_MASK) && !(val & PCMCIA_C1_CD2_MASK))
> +		stat |= SS_DETECT;
> +
> +	/* if new insertion, detect cardtype */
> +	if ((stat & SS_DETECT) && !skt->card_detected) {
> +		unsigned int stat = 0;
> +
> +		/* float VS1, float VS2 */
> +		val |= PCMCIA_C1_VS1OE_MASK;
> +		val |= PCMCIA_C1_VS2OE_MASK;
> +		pcmcia_writel(skt, val, PCMCIA_C1_REG);
> +
> +		/* wait for output to stabilize and read VS[12] */
> +		udelay(10);
> +		val = pcmcia_readl(skt, PCMCIA_C1_REG);
> +		stat |= (val & PCMCIA_C1_VS1_MASK) ? IN_VS1 : 0;
> +		stat |= (val & PCMCIA_C1_VS2_MASK) ? IN_VS2 : 0;
> +
> +		/* drive VS1 low, float VS2 */
> +		val &= ~PCMCIA_C1_VS1OE_MASK;
> +		val |= PCMCIA_C1_VS2OE_MASK;
> +		pcmcia_writel(skt, val, PCMCIA_C1_REG);
> +
> +		/* wait for output to stabilize and read CD[12] */
> +		udelay(10);
> +		val = pcmcia_readl(skt, PCMCIA_C1_REG);
> +		stat |= (val & PCMCIA_C1_CD1_MASK) ? IN_CD1_VS2H : 0;
> +		stat |= (val & PCMCIA_C1_CD2_MASK) ? IN_CD2_VS2H : 0;
> +
> +		/* float VS1, drive VS2 low */
> +		val |= PCMCIA_C1_VS1OE_MASK;
> +		val &= ~PCMCIA_C1_VS2OE_MASK;
> +		pcmcia_writel(skt, val, PCMCIA_C1_REG);
> +
> +		/* wait for output to stabilize and read CD[12] */
> +		udelay(10);
> +		val = pcmcia_readl(skt, PCMCIA_C1_REG);
> +		stat |= (val & PCMCIA_C1_CD1_MASK) ? IN_CD1_VS1H : 0;
> +		stat |= (val & PCMCIA_C1_CD2_MASK) ? IN_CD2_VS1H : 0;
> +
> +		/* guess cardtype from all this */
> +		skt->card_type = vscd_to_cardtype[stat];
> +		if (!skt->card_type)
> +			printk(KERN_ERR PFX "unsupported card type\n");

hmm, maybe dev_err using &skt->socket->dev?

> +
> +		/* drive both VS pin to 0 again */
> +		val &= ~(PCMCIA_C1_VS1OE_MASK | PCMCIA_C1_VS2OE_MASK);
> +
> +		/* enable correct logic */
> +		val &= ~(PCMCIA_C1_EN_PCMCIA_MASK | PCMCIA_C1_EN_CARDBUS_MASK);
> +		if (skt->card_type & CARD_PCCARD)
> +			val |= PCMCIA_C1_EN_PCMCIA_MASK;
> +		else
> +			val |= PCMCIA_C1_EN_CARDBUS_MASK;
> +
> +		pcmcia_writel(skt, val, PCMCIA_C1_REG);
> +	}
> +	skt->card_detected = (stat & SS_DETECT) ? 1 : 0;
> +
> +	/* report card type/voltage */
> +	if (skt->card_type & CARD_CARDBUS)
> +		stat |= SS_CARDBUS;
> +	if (skt->card_type & CARD_3V)
> +		stat |= SS_3VCARD;
> +	if (skt->card_type & CARD_XV)
> +		stat |= SS_XVCARD;
> +	stat |= SS_POWERON;
> +
> +	if (gpio_get_value(skt->pd->ready_gpio))
> +		stat |= SS_READY;
> +
> +	return stat;
> +}
> +
> +/*
> + * core request to get current socket status
> + */
> +static int bcm63xx_pcmcia_get_status(struct pcmcia_socket *sock,
> +				     unsigned int *status)
> +{
> +	struct bcm63xx_pcmcia_socket *skt;
> +
> +	skt = sock->driver_data;
> +
> +	spin_lock_bh(&skt->lock);
> +	*status = __get_socket_status(skt);
> +	spin_unlock_bh(&skt->lock);
> +
> +	return 0;
> +}
> +
> +/*
> + * socket polling timer callback
> + */
> +static void bcm63xx_pcmcia_poll(unsigned long data)
> +{
> +	struct bcm63xx_pcmcia_socket *skt;
> +	unsigned int stat, events;
> +
> +	skt = (struct bcm63xx_pcmcia_socket *)data;
> +
> +	spin_lock_bh(&skt->lock);
> +
> +	stat = __get_socket_status(skt);
> +
> +	/* keep only changed bits, and mask with required one from the
> +	 * core */
> +	events = (stat ^ skt->old_status) & skt->requested_state.csc_mask;
> +	skt->old_status = stat;
> +	spin_unlock_bh(&skt->lock);
> +
> +	if (events)
> +		pcmcia_parse_events(&skt->socket, events);
> +
> +	mod_timer(&skt->timer,
> +		  jiffies + msecs_to_jiffies(BCM63XX_PCMCIA_POLL_RATE));
> +}
> +
> +static int bcm63xx_pcmcia_set_io_map(struct pcmcia_socket *sock,
> +				     struct pccard_io_map *map)
> +{
> +	/* this doesn't seem to be called by pcmcia layer if static
> +	 * mapping is used */
> +	return 0;
> +}
> +
> +static int bcm63xx_pcmcia_set_mem_map(struct pcmcia_socket *sock,
> +				      struct pccard_mem_map *map)
> +{
> +	struct bcm63xx_pcmcia_socket *skt;
> +	struct resource *res;
> +
> +	skt = sock->driver_data;
> +	if (map->flags & MAP_ATTRIB)
> +		res = skt->attr_res;
> +	else
> +		res = skt->common_res;
> +
> +	map->static_start = res->start + map->card_start;
> +	return 0;
> +}
> +
> +static struct pccard_operations bcm63xx_pcmcia_operations = {
> +	.init			= bcm63xx_pcmcia_sock_init,
> +	.suspend		= bcm63xx_pcmcia_suspend,
> +	.get_status		= bcm63xx_pcmcia_get_status,
> +	.set_socket		= bcm63xx_pcmcia_set_socket,
> +	.set_io_map		= bcm63xx_pcmcia_set_io_map,
> +	.set_mem_map		= bcm63xx_pcmcia_set_mem_map,
> +};
> +
> +/*
> + * register pcmcia socket to core
> + */
> +static int bcm63xx_drv_pcmcia_probe(struct platform_device *pdev)
> +{
> +	struct bcm63xx_pcmcia_socket *skt;
> +	struct pcmcia_socket *sock;
> +	struct resource *res, *irq_res;
> +	unsigned int regmem_size = 0, iomem_size = 0;
> +	u32 val;
> +	int ret;
> +
> +	skt = kzalloc(sizeof(*skt), GFP_KERNEL);
> +	if (!skt)
> +		return -ENOMEM;
> +	spin_lock_init(&skt->lock);
> +	sock = &skt->socket;
> +	sock->driver_data = skt;
> +
> +	/* make sure we have all resources we need */
> +	skt->common_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +	skt->attr_res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
> +	irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> +	skt->pd = pdev->dev.platform_data;
> +	if (!skt->common_res || !skt->attr_res || !irq_res || !skt->pd) {
> +		ret = -EINVAL;
> +		goto err;
> +	}
> +
> +	/* remap pcmcia registers */
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	regmem_size = res->end - res->start + 1;

use resource_size()

> +	if (!request_mem_region(res->start, regmem_size, "bcm63xx_pcmcia")) {
> +		ret = -EINVAL;
> +		goto err;
> +	}
> +	skt->reg_res = res;
> +
> +	skt->base = ioremap(res->start, regmem_size);
> +	if (!skt->base) {
> +		ret = -ENOMEM;
> +		goto err;
> +	}
> +
> +	/* remap io registers */
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 3);
> +	iomem_size = res->end - res->start + 1;

ditto

> +	skt->io_base = ioremap(res->start, iomem_size);
> +	if (!skt->io_base) {
> +		ret = -ENOMEM;
> +		goto err;
> +	}
> +
> +	/* resources are static */
> +	sock->resource_ops = &pccard_static_ops;
> +	sock->ops = &bcm63xx_pcmcia_operations;
> +	sock->owner = THIS_MODULE;
> +	sock->dev.parent = &pdev->dev;
> +	sock->features = SS_CAP_STATIC_MAP | SS_CAP_PCCARD;
> +	sock->io_offset = (unsigned long)skt->io_base;
> +	sock->pci_irq = irq_res->start;
> +
> +#ifdef CONFIG_CARDBUS
> +	sock->cb_dev = bcm63xx_cb_dev;
> +	if (bcm63xx_cb_dev)
> +		sock->features |= SS_CAP_CARDBUS;
> +#endif
> +
> +	/* assume common & attribute memory have the same size */
> +	sock->map_size = skt->common_res->end - skt->common_res->start + 1;
> +
> +	/* initialize polling timer */
> +	setup_timer(&skt->timer, bcm63xx_pcmcia_poll, (unsigned long)skt);
> +
> +	/* initialize  pcmcia  control register,  drive  VS[12] to  0,
> +	 * leave CB IDSEL to the old  value since it is set by the PCI
> +	 * layer */
> +	val = pcmcia_readl(skt, PCMCIA_C1_REG);
> +	val &= PCMCIA_C1_CBIDSEL_MASK;
> +	val |= PCMCIA_C1_EN_PCMCIA_GPIO_MASK;
> +	pcmcia_writel(skt, val, PCMCIA_C1_REG);
> +
> +	/* FIXME set correct pcmcia timings */

We already talked about this one...

> +	val = PCMCIA_C2_DATA16_MASK;
> +	val |= 10 << PCMCIA_C2_RWCOUNT_SHIFT;
> +	val |= 6 << PCMCIA_C2_INACTIVE_SHIFT;
> +	val |= 3 << PCMCIA_C2_SETUP_SHIFT;
> +	val |= 3 << PCMCIA_C2_HOLD_SHIFT;
> +	pcmcia_writel(skt, val, PCMCIA_C2_REG);
> +
> +	/* request and setup ready gpio */
> +	ret = gpio_request(skt->pd->ready_gpio, "bcm63xx_pcmcia");
> +	if (ret < 0)
> +		goto err;
> +
> +	ret = gpio_direction_input(skt->pd->ready_gpio);
> +	if (ret < 0)
> +		goto err_gpio;
> +
> +	ret = pcmcia_register_socket(sock);
> +	if (ret)
> +		goto err_gpio;
> +
> +	/* start polling socket */
> +	mod_timer(&skt->timer,
> +		  jiffies + msecs_to_jiffies(BCM63XX_PCMCIA_POLL_RATE));
> +
> +	platform_set_drvdata(pdev, skt);
> +	return 0;
> +
> +err_gpio:
> +	gpio_free(skt->pd->ready_gpio);
> +
> +err:
> +	if (skt->io_base)
> +		iounmap(skt->io_base);
> +	if (skt->base)
> +		iounmap(skt->base);
> +	if (skt->reg_res)
> +		release_mem_region(skt->reg_res->start, regmem_size);
> +	kfree(skt);
> +	return ret;
> +}
> +
> +static int bcm63xx_drv_pcmcia_remove(struct platform_device *pdev)
> +{
> +	struct bcm63xx_pcmcia_socket *skt;
> +	struct resource *res;
> +
> +	skt = platform_get_drvdata(pdev);
> +	del_timer_sync(&skt->timer);
> +	iounmap(skt->base);
> +	iounmap(skt->io_base);
> +	res = skt->reg_res;
> +	release_mem_region(res->start, res->end - res->start + 1);

use resource size()

> +	gpio_free(skt->pd->ready_gpio);
> +	platform_set_drvdata(pdev, NULL);
> +	kfree(skt);
> +	return 0;
> +}
> +
> +struct platform_driver bcm63xx_pcmcia_driver = {
> +	.probe	= bcm63xx_drv_pcmcia_probe,
> +	.remove	= __devexit_p(bcm63xx_drv_pcmcia_remove),

It is marked as __devexit_p, but the function not as __devexit.

> +	.driver	= {
> +		.name	= "bcm63xx_pcmcia",
> +		.owner  = THIS_MODULE,
> +	},
> +};
> +
> +#ifdef CONFIG_CARDBUS
> +static int __devinit bcm63xx_cb_probe(struct pci_dev *dev,
> +				      const struct pci_device_id *id)
> +{
> +	/* keep pci device */
> +	bcm63xx_cb_dev = dev;
> +	return platform_driver_register(&bcm63xx_pcmcia_driver);
> +}
> +
> +static void __devexit bcm63xx_cb_exit(struct pci_dev *dev)
> +{
> +	platform_driver_unregister(&bcm63xx_pcmcia_driver);
> +	bcm63xx_cb_dev = NULL;
> +}
> +
> +static struct pci_device_id bcm63xx_cb_table[] = {
> +	{
> +		.vendor		= PCI_VENDOR_ID_BROADCOM,
> +		.device		= PCI_ANY_ID,
> +		.subvendor	= PCI_VENDOR_ID_BROADCOM,
> +		.subdevice	= PCI_ANY_ID,
> +		.class		= PCI_CLASS_BRIDGE_CARDBUS << 8,
> +		.class_mask	= ~0,
> +	},
> +
> +	{ },
> +};
> +
> +MODULE_DEVICE_TABLE(pci, bcm63xx_cb_table);
> +
> +static struct pci_driver bcm63xx_cardbus_driver = {
> +	.name		= "yenta_cardbus",
> +	.id_table	= bcm63xx_cb_table,
> +	.probe		= bcm63xx_cb_probe,
> +	.remove		= __devexit_p(bcm63xx_cb_exit),
> +};
> +#endif
> +
> +/*
> + * if cardbus support is enabled, register our platform device after
> + * our fake cardbus bridge has been registered
> + */
> +static int __init bcm63xx_pcmcia_init(void)
> +{
> +#ifdef CONFIG_CARDBUS
> +	return pci_register_driver(&bcm63xx_cardbus_driver);
> +#else
> +	return platform_driver_register(&bcm63xx_pcmcia_driver);
> +#endif
> +}
> +
> +static void __exit bcm63xx_pcmcia_exit(void)
> +{
> +#ifdef CONFIG_CARDBUS
> +	return pci_unregister_driver(&bcm63xx_cardbus_driver);
> +#else
> +	platform_driver_unregister(&bcm63xx_pcmcia_driver);
> +#endif
> +}
> +
> +module_init(bcm63xx_pcmcia_init);
> +module_exit(bcm63xx_pcmcia_exit);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Maxime Bizon <mbizon at freebox.fr>");
> +MODULE_DESCRIPTION("Linux PCMCIA Card Services: bcm63xx Socket Controller");
> diff --git a/drivers/pcmcia/bcm63xx_pcmcia.h b/drivers/pcmcia/bcm63xx_pcmcia.h
> new file mode 100644
> index 0000000..85de866
> --- /dev/null
> +++ b/drivers/pcmcia/bcm63xx_pcmcia.h
> @@ -0,0 +1,65 @@
> +#ifndef BCM63XX_PCMCIA_H_
> +#define BCM63XX_PCMCIA_H_
> +
> +#include <linux/types.h>
> +#include <linux/timer.h>
> +#include <pcmcia/ss.h>
> +#include <bcm63xx_dev_pcmcia.h>
> +
> +/* socket polling rate in ms */
> +#define BCM63XX_PCMCIA_POLL_RATE	500
> +
> +enum {
> +	CARD_CARDBUS = (1 << 0),
> +
> +	CARD_PCCARD = (1 << 1),
> +
> +	CARD_5V = (1 << 2),
> +
> +	CARD_3V = (1 << 3),
> +
> +	CARD_XV = (1 << 4),
> +
> +	CARD_YV = (1 << 5),
> +};
> +
> +struct bcm63xx_pcmcia_socket {
> +	struct pcmcia_socket socket;
> +
> +	/* platform specific data */
> +	struct bcm63xx_pcmcia_platform_data *pd;
> +
> +	/* all regs access are protected by this spinlock */
> +	spinlock_t lock;
> +
> +	/* pcmcia registers resource */
> +	struct resource *reg_res;
> +
> +	/* base remapped address of registers */
> +	void __iomem *base;
> +
> +	/* whether a card is detected at the moment */
> +	int card_detected;
> +
> +	/* type of detected card (mask of above enum) */
> +	u8 card_type;
> +
> +	/* keep last socket status to implement event reporting */
> +	unsigned int old_status;
> +
> +	/* backup of requested socket state */
> +	socket_state_t requested_state;
> +
> +	/* timer used for socket status polling */
> +	struct timer_list timer;
> +
> +	/* attribute/common memory resources */
> +	struct resource *attr_res;
> +	struct resource *common_res;
> +	struct resource *io_res;
> +
> +	/* base address of io memory */
> +	void __iomem *io_base;
> +};
> +
> +#endif /* BCM63XX_PCMCIA_H_ */
> 
> 
> -- 
> Maxime
> 
> 

-- 
Pengutronix e.K.                           | Wolfram Sang                |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-5064 |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 197 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-pcmcia/attachments/20090923/42c6197f/attachment-0001.sig>


More information about the linux-pcmcia mailing list