Messages in this thread Patch in this message | | | From | Bryan Wu <> | Subject | [PATCH 1/1] [PCMCIA]: add support the CF PCMCIA driver for Blackfin | Date | Mon, 12 May 2008 18:46:40 +0800 |
| |
From: Michael Hennerich <michael.hennerich@analog.com>
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com> Signed-off-by: Bryan Wu <cooloney@kernel.org> --- drivers/pcmcia/Kconfig | 7 + drivers/pcmcia/Makefile | 1 + drivers/pcmcia/bfin_cf_pcmcia.c | 324 +++++++++++++++++++++++++++++++++++++++ include/pcmcia/cs_types.h | 2 +- 4 files changed, 333 insertions(+), 1 deletions(-) create mode 100644 drivers/pcmcia/bfin_cf_pcmcia.c
diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index 1b0eb5a..e45402a 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -263,6 +263,13 @@ config OMAP_CF Say Y here to support the CompactFlash controller on OMAP. Note that this doesn't support "True IDE" mode. +config BFIN_CFPCMCIA + tristate "Blackfin CompactFlash PCMCIA Driver" + depends on PCMCIA && BLACKFIN + help + Say Y here to support the CompactFlash PCMCIA driver for Blackfin. + + config AT91_CF tristate "AT91 CompactFlash Controller" depends on PCMCIA && ARCH_AT91RM9200 diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile index 6f6478b..85c6cc9 100644 --- a/drivers/pcmcia/Makefile +++ b/drivers/pcmcia/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_PCMCIA_AU1X00) += au1x00_ss.o obj-$(CONFIG_PCMCIA_VRC4171) += vrc4171_card.o obj-$(CONFIG_PCMCIA_VRC4173) += vrc4173_cardu.o obj-$(CONFIG_OMAP_CF) += omap_cf.o +obj-$(CONFIG_BFIN_CFPCMCIA) += bfin_cf_pcmcia.o obj-$(CONFIG_AT91_CF) += at91_cf.o obj-$(CONFIG_ELECTRA_CF) += electra_cf.o diff --git a/drivers/pcmcia/bfin_cf_pcmcia.c b/drivers/pcmcia/bfin_cf_pcmcia.c new file mode 100644 index 0000000..7964675 --- /dev/null +++ b/drivers/pcmcia/bfin_cf_pcmcia.c @@ -0,0 +1,324 @@ +/* + * file: drivers/pcmcia/bfin_cf.c + * author: Michael Hennerich (hennerich@blackfin.uclinux.org) + * + * based on: drivers/pcmcia/omap_cf.c + * omap_cf.c -- OMAP 16xx CompactFlash controller driver + * + * Copyright (c) 2005 David Brownell + * Copyright (c) 2006 Michael Hennerich Analog Devices Inc. + * + * bugs: enter bugs at http://blackfin.uclinux.org/ + * + * this program is free software; you can redistribute it and/or modify + * it under the terms of the gnu general public license as published by + * the free software foundation; either version 2, or (at your option) + * any later version. + * + * this program is distributed in the hope that 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; see the file copying. + * if not, write to the free software foundation, + * 59 temple place - suite 330, boston, ma 02111-1307, usa. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/platform_device.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/irq.h> + +#include <pcmcia/ss.h> +#include <pcmcia/cisreg.h> +#include <asm/io.h> +#include <asm/gpio.h> + +#define SZ_1K 0x00000400 +#define SZ_8K 0x00002000 +#define SZ_2K (2 * SZ_1K) + +#define POLL_INTERVAL (2 * HZ) + +#define CF_ATASEL_ENA 0x20311802 /* Inverts RESET */ +#define CF_ATASEL_DIS 0x20311800 + +#define bfin_cf_present(pfx) (gpio_get_value(pfx)) + +/*--------------------------------------------------------------------------*/ + +static const char driver_name[] = "bfin_cf_pcmcia"; + +struct bfin_cf_socket { + struct pcmcia_socket socket; + + struct timer_list timer; + unsigned present:1; + unsigned active:1; + + struct platform_device *pdev; + unsigned long phys_cf_io; + unsigned long phys_cf_attr; + u_int irq; + u_short cd_pfx; +}; + +/*--------------------------------------------------------------------------*/ +static int bfin_cf_reset(void) +{ + outw(0, CF_ATASEL_ENA); + mdelay(200); + outw(0, CF_ATASEL_DIS); + + return 0; + +} + +static int bfin_cf_ss_init(struct pcmcia_socket *s) +{ + return 0; +} + +/* the timer is primarily to kick this socket's pccardd */ +static void bfin_cf_timer(unsigned long _cf) +{ + struct bfin_cf_socket *cf = (void *) _cf; + unsigned short present = bfin_cf_present(cf->cd_pfx); + + if (present != cf->present) { + cf->present = present; + pr_debug("%s: card %s\n", driver_name, + present ? "present" : "gone"); + pcmcia_parse_events(&cf->socket, SS_DETECT); + } + + if (cf->active) + mod_timer(&cf->timer, jiffies + POLL_INTERVAL); +} + +static int bfin_cf_get_status(struct pcmcia_socket *s, u_int *sp) +{ + struct bfin_cf_socket *cf; + + if (!sp) + return -EINVAL; + + cf = container_of(s, struct bfin_cf_socket, socket); + + if (bfin_cf_present(cf->cd_pfx)) { + *sp = SS_READY | SS_DETECT | SS_POWERON | SS_3VCARD; + s->irq.AssignedIRQ = 0; + s->pci_irq = cf->irq; + + } else + *sp = 0; + return 0; +} + +static int +bfin_cf_set_socket(struct pcmcia_socket *sock, struct socket_state_t *s) +{ + + struct bfin_cf_socket *cf; + cf = container_of(sock, struct bfin_cf_socket, socket); + + switch (s->Vcc) { + case 0: + case 33: + break; + case 50: + break; + default: + return -EINVAL; + } + + if (s->flags & SS_RESET){ + disable_irq(cf->irq); + bfin_cf_reset(); + enable_irq(cf->irq); + } + + pr_debug("%s: Vcc %d, io_irq %d, flags %04x csc %04x\n", + driver_name, s->Vcc, s->io_irq, s->flags, s->csc_mask); + + return 0; +} + +static int bfin_cf_ss_suspend(struct pcmcia_socket *s) +{ + pr_debug("%s: %s\n", driver_name, __FUNCTION__); + return bfin_cf_set_socket(s, &dead_socket); +} + +/* regions are 2K each: mem, attrib, io (and reserved-for-ide) */ + +static int bfin_cf_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io) +{ struct bfin_cf_socket *cf; + + cf = container_of(s, struct bfin_cf_socket, socket); + io->flags &= MAP_ACTIVE|MAP_ATTRIB|MAP_16BIT; + io->start = cf->phys_cf_io; + io->stop = io->start + SZ_2K - 1; + return 0; +} + +static int +bfin_cf_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *map) +{ + struct bfin_cf_socket *cf; + + if (map->card_start) + return -EINVAL; + cf = container_of(s, struct bfin_cf_socket, socket); + map->static_start = cf->phys_cf_io; + map->flags &= MAP_ACTIVE|MAP_ATTRIB|MAP_16BIT; + if (map->flags & MAP_ATTRIB) + map->static_start = cf->phys_cf_attr; + + return 0; +} + +static struct pccard_operations bfin_cf_ops = { + .init = bfin_cf_ss_init, + .suspend = bfin_cf_ss_suspend, + .get_status = bfin_cf_get_status, + .set_socket = bfin_cf_set_socket, + .set_io_map = bfin_cf_set_io_map, + .set_mem_map = bfin_cf_set_mem_map, +}; + +/*--------------------------------------------------------------------------*/ + +static int __init bfin_cf_probe(struct device *dev) +{ + struct bfin_cf_socket *cf; + struct platform_device *pdev = to_platform_device(dev); + struct resource *io_mem,*attr_mem; + int irq; + unsigned short cd_pfx; + int status = 0; + + printk(KERN_INFO "Blackfin CompactFlash/PCMCIA Socket Driver\n"); + + irq = platform_get_irq(pdev, 0); + if (!irq) + return -EINVAL; + + cd_pfx = platform_get_irq(pdev, 1); /*Card Detect GPIO PIN*/ + if (cd_pfx > MAX_BLACKFIN_GPIOS) + return -EINVAL; + + if (gpio_request(cd_pfx, "pcmcia: CD")) { + printk(KERN_ERR "BF5xx flash: Failed ro request Card Detect GPIO_%d\n", cd_pfx); + return -EBUSY; + } + gpio_direction_input(cd_pfx); + + cf = kcalloc(1, sizeof *cf, GFP_KERNEL); + if (!cf) + return -ENOMEM; + + cf->cd_pfx = cd_pfx; + + init_timer(&cf->timer); + cf->timer.function = bfin_cf_timer; + cf->timer.data = (unsigned long) cf; + + cf->pdev = pdev; + dev_set_drvdata(dev, cf); + + cf->irq = irq; + cf->socket.pci_irq = irq; + + set_irq_type(irq, IRQF_TRIGGER_LOW); + + io_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + attr_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); + + if (!io_mem || !attr_mem) + goto fail0; + + cf->phys_cf_io = io_mem->start; + cf->phys_cf_attr = attr_mem->start; + + /* pcmcia layer only remaps "real" memory */ + cf->socket.io_offset = (unsigned long) + ioremap(cf->phys_cf_io, SZ_2K); + + if (!cf->socket.io_offset) + goto fail0; + + pr_info("%s: on irq %d\n", driver_name, irq); + + pr_debug("%s: %s\n", driver_name, bfin_cf_present(cf->cd_pfx) ? "present" : "(not present)"); + + cf->socket.owner = THIS_MODULE; + cf->socket.dev.parent = dev; + cf->socket.ops = &bfin_cf_ops; + cf->socket.resource_ops = &pccard_static_ops; + cf->socket.features = SS_CAP_PCCARD | SS_CAP_STATIC_MAP + | SS_CAP_MEM_ALIGN; + cf->socket.map_size = SZ_2K; + + status = pcmcia_register_socket(&cf->socket); + if (status < 0) + goto fail2; + + cf->active = 1; + mod_timer(&cf->timer, jiffies + POLL_INTERVAL); + return 0; + +fail2: + iounmap((void __iomem *) cf->socket.io_offset); + release_mem_region(cf->phys_cf_io, SZ_8K); + +fail0: + kfree(cf); + return status; +} + +static int __devexit bfin_cf_remove(struct device *dev) +{ + struct bfin_cf_socket *cf = dev_get_drvdata(dev); + + gpio_free(cf->cd_pfx); + cf->active = 0; + pcmcia_unregister_socket(&cf->socket); + del_timer_sync(&cf->timer); + iounmap((void __iomem *) cf->socket.io_offset); + release_mem_region(cf->phys_cf_io, SZ_8K); + kfree(cf); + return 0; +} + +static struct device_driver bfin_cf_driver = { + .name = (char *) driver_name, + .bus = &platform_bus_type, + .probe = bfin_cf_probe, + .remove = __devexit_p(bfin_cf_remove), + .suspend = pcmcia_socket_dev_suspend, + .resume = pcmcia_socket_dev_resume, +}; + +static int __init bfin_cf_init(void) +{ + return driver_register(&bfin_cf_driver); +} + +static void __exit bfin_cf_exit(void) +{ + driver_unregister(&bfin_cf_driver); +} + +module_init(bfin_cf_init); +module_exit(bfin_cf_exit); + +MODULE_DESCRIPTION("BFIN CF/PCMCIA Driver"); +MODULE_LICENSE("GPL"); diff --git a/include/pcmcia/cs_types.h b/include/pcmcia/cs_types.h index 9a6bcc4..c3257c1 100644 --- a/include/pcmcia/cs_types.h +++ b/include/pcmcia/cs_types.h @@ -21,7 +21,7 @@ #include <sys/types.h> #endif -#if defined(__arm__) || defined(__mips__) || defined(__avr32__) +#if defined(__arm__) || defined(__mips__) || defined(__avr32__) || defined(bfin) /* This (ioaddr_t) is exposed to userspace & hence cannot be changed. */ typedef u_int ioaddr_t; #else -- 1.5.5
| |