diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index e0f884034c9f0c3fdfc3f1040506bbe2aac378fd..de57e7e178686ba69057e83914fbe9fa337290ac 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -220,7 +220,7 @@ config PCMCIA_PXA2XX tristate "PXA2xx support" depends on ARM && ARCH_PXA && PCMCIA depends on (ARCH_LUBBOCK || MACH_MAINSTONE || PXA_SHARPSL \ - || MACH_ARMCORE || ARCH_PXA_PALM) + || MACH_ARMCORE || ARCH_PXA_PALM || ARCH_VIPER) help Say Y here to include support for the PXA2xx PCMCIA controller diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile index 269a9e913ba2e9464e69494f618525de8b766b44..76e859073cbbae4548a3d1cd8259b33d014279c9 100644 --- a/drivers/pcmcia/Makefile +++ b/drivers/pcmcia/Makefile @@ -72,5 +72,6 @@ pxa2xx_cs-$(CONFIG_ARCH_LUBBOCK) += pxa2xx_lubbock.o sa1111_generic.o pxa2xx_cs-$(CONFIG_MACH_MAINSTONE) += pxa2xx_mainstone.o pxa2xx_cs-$(CONFIG_PXA_SHARPSL) += pxa2xx_sharpsl.o pxa2xx_cs-$(CONFIG_MACH_ARMCORE) += pxa2xx_cm_x270.o +pxa2xx_cs-$(CONFIG_ARCH_VIPER) += pxa2xx_viper.o pxa2xx_cs-$(CONFIG_MACH_PALMTX) += pxa2xx_palmtx.o diff --git a/drivers/pcmcia/pxa2xx_base.c b/drivers/pcmcia/pxa2xx_base.c index 1b07af5a2ed3c06924fab1652cb997cb873b52d5..13f1e0fd3f31cf096b5c07cf10392278e0308bdc 100644 --- a/drivers/pcmcia/pxa2xx_base.c +++ b/drivers/pcmcia/pxa2xx_base.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -166,18 +167,32 @@ pxa2xx_pcmcia_frequency_change(struct soc_pcmcia_socket *skt, } #endif +static void pxa2xx_configure_sockets(struct device *dev) +{ + struct pcmcia_low_level *ops = dev->platform_data; + + /* + * We have at least one socket, so set MECR:CIT + * (Card Is There) + */ + MECR |= MECR_CIT; + + /* Set MECR:NOS (Number Of Sockets) */ + if (ops->nr > 1 || machine_is_viper()) + MECR |= MECR_NOS; + else + MECR &= ~MECR_NOS; +} + int __pxa2xx_drv_pcmcia_probe(struct device *dev) { int ret; struct pcmcia_low_level *ops; - int first, nr; if (!dev || !dev->platform_data) return -ENODEV; ops = (struct pcmcia_low_level *)dev->platform_data; - first = ops->first; - nr = ops->nr; /* Provide our PXA2xx specific timing routines. */ ops->set_timing = pxa2xx_pcmcia_set_timing; @@ -185,21 +200,10 @@ int __pxa2xx_drv_pcmcia_probe(struct device *dev) ops->frequency_change = pxa2xx_pcmcia_frequency_change; #endif - ret = soc_common_drv_pcmcia_probe(dev, ops, first, nr); + ret = soc_common_drv_pcmcia_probe(dev, ops, ops->first, ops->nr); - if (ret == 0) { - /* - * We have at least one socket, so set MECR:CIT - * (Card Is There) - */ - MECR |= MECR_CIT; - - /* Set MECR:NOS (Number Of Sockets) */ - if (nr > 1) - MECR |= MECR_NOS; - else - MECR &= ~MECR_NOS; - } + if (!ret) + pxa2xx_configure_sockets(dev); return ret; } @@ -223,11 +227,7 @@ static int pxa2xx_drv_pcmcia_suspend(struct platform_device *dev, pm_message_t s static int pxa2xx_drv_pcmcia_resume(struct platform_device *dev) { - struct pcmcia_low_level *ops = dev->dev.platform_data; - int nr = ops ? ops->nr : 0; - - MECR = nr > 1 ? MECR_CIT | MECR_NOS : (nr > 0 ? MECR_CIT : 0); - + pxa2xx_configure_sockets(&dev->dev); return pcmcia_socket_dev_resume(&dev->dev); } diff --git a/drivers/pcmcia/pxa2xx_viper.c b/drivers/pcmcia/pxa2xx_viper.c new file mode 100644 index 0000000000000000000000000000000000000000..dd10481be7bf23fa8c45fc4c01dda38952412ebe --- /dev/null +++ b/drivers/pcmcia/pxa2xx_viper.c @@ -0,0 +1,179 @@ +/* + * VIPER PCMCIA support + * Copyright 2004 Arcom Control Systems + * + * Maintained by Marc Zyngier + * + * + * Based on: + * iPAQ h2200 PCMCIA support + * Copyright 2004 Koen Kooi + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + +#include "soc_common.h" +#include "pxa2xx_base.h" + +static struct pcmcia_irqs irqs[] = { + { 0, gpio_to_irq(VIPER_CF_CD_GPIO), "PCMCIA_CD" } +}; + +static int viper_pcmcia_hw_init(struct soc_pcmcia_socket *skt) +{ + unsigned long flags; + + skt->irq = gpio_to_irq(VIPER_CF_RDY_GPIO); + + if (gpio_request(VIPER_CF_CD_GPIO, "CF detect")) + goto err_request_cd; + + if (gpio_request(VIPER_CF_RDY_GPIO, "CF ready")) + goto err_request_rdy; + + if (gpio_request(VIPER_CF_POWER_GPIO, "CF power")) + goto err_request_pwr; + + local_irq_save(flags); + + /* GPIO 82 is the CF power enable line. initially off */ + if (gpio_direction_output(VIPER_CF_POWER_GPIO, 0) || + gpio_direction_input(VIPER_CF_CD_GPIO) || + gpio_direction_input(VIPER_CF_RDY_GPIO)) { + local_irq_restore(flags); + goto err_dir; + } + + local_irq_restore(flags); + + return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs)); + +err_dir: + gpio_free(VIPER_CF_POWER_GPIO); +err_request_pwr: + gpio_free(VIPER_CF_RDY_GPIO); +err_request_rdy: + gpio_free(VIPER_CF_CD_GPIO); +err_request_cd: + printk(KERN_ERR "viper: Failed to setup PCMCIA GPIOs\n"); + return -1; +} + +/* + * Release all resources. + */ +static void viper_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) +{ + soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs)); + gpio_free(VIPER_CF_POWER_GPIO); + gpio_free(VIPER_CF_RDY_GPIO); + gpio_free(VIPER_CF_CD_GPIO); +} + +static void viper_pcmcia_socket_state(struct soc_pcmcia_socket *skt, + struct pcmcia_state *state) +{ + state->detect = gpio_get_value(VIPER_CF_CD_GPIO) ? 0 : 1; + state->ready = gpio_get_value(VIPER_CF_RDY_GPIO) ? 1 : 0; + state->bvd1 = 1; + state->bvd2 = 1; + state->wrprot = 0; + state->vs_3v = 1; /* Can only apply 3.3V */ + state->vs_Xv = 0; +} + +static int viper_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, + const socket_state_t *state) +{ + /* Silently ignore Vpp, output enable, speaker enable. */ + viper_cf_rst(state->flags & SS_RESET); + + /* Apply socket voltage */ + switch (state->Vcc) { + case 0: + gpio_set_value(VIPER_CF_POWER_GPIO, 0); + break; + case 33: + gpio_set_value(VIPER_CF_POWER_GPIO, 1); + break; + default: + printk(KERN_ERR "%s: Unsupported Vcc:%d\n", + __func__, state->Vcc); + return -1; + } + + return 0; +} + +static void viper_pcmcia_socket_init(struct soc_pcmcia_socket *skt) +{ +} + +static void viper_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) +{ +} + +static struct pcmcia_low_level viper_pcmcia_ops __initdata = { + .owner = THIS_MODULE, + .hw_init = viper_pcmcia_hw_init, + .hw_shutdown = viper_pcmcia_hw_shutdown, + .socket_state = viper_pcmcia_socket_state, + .configure_socket = viper_pcmcia_configure_socket, + .socket_init = viper_pcmcia_socket_init, + .socket_suspend = viper_pcmcia_socket_suspend, + .nr = 1, +}; + +static struct platform_device *viper_pcmcia_device; + +static int __init viper_pcmcia_init(void) +{ + int ret; + + if (!machine_is_viper()) + return -ENODEV; + + viper_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1); + if (!viper_pcmcia_device) + return -ENOMEM; + + ret = platform_device_add_data(viper_pcmcia_device, + &viper_pcmcia_ops, + sizeof(viper_pcmcia_ops)); + + if (!ret) + ret = platform_device_add(viper_pcmcia_device); + + if (ret) + platform_device_put(viper_pcmcia_device); + + return ret; +} + +static void __exit viper_pcmcia_exit(void) +{ + platform_device_unregister(viper_pcmcia_device); +} + +module_init(viper_pcmcia_init); +module_exit(viper_pcmcia_exit); + +MODULE_LICENSE("GPL");