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 <[email protected]> I am fine with Ralf picking this up. > > Thanks ! > > > Signed-off-by: Maxime Bizon <[email protected]> > Signed-off-by: Ralf Baechle <[email protected]> > --- > > 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 <[email protected]> > + */ > + > +#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 <[email protected]> > + */ > + > +#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 <[email protected]>"); > +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 |
signature.asc
Description: Digital signature
_______________________________________________ Linux PCMCIA reimplementation list http://lists.infradead.org/mailman/listinfo/linux-pcmcia
