[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