diff options
author | Jürgen Schindele <linux@schindele.name> | 2008-10-07 10:40:18 +0100 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2008-10-07 10:53:56 +0100 |
commit | 2efa42f2fadc06f7ceee14834624d2911a9f11f1 (patch) | |
tree | d6cccdd549328756a3c765300e025a3b87daefc3 /drivers/pcmcia/pxa2xx_trizeps4.c | |
parent | 2532967147f750fa661bdc3d1f890314c37dda28 (diff) |
[ARM] 5294/1: Trizeps4 PCMCIA support
Support for Trizeps4 SOM PCMCIA
with ConXS or other baseboards.
with double GPIO-free fixed (sorry!)
replaces 5279/1
Signed-off-by: Jurgen Schindele <linux@schindele.name>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'drivers/pcmcia/pxa2xx_trizeps4.c')
-rw-r--r-- | drivers/pcmcia/pxa2xx_trizeps4.c | 257 |
1 files changed, 257 insertions, 0 deletions
diff --git a/drivers/pcmcia/pxa2xx_trizeps4.c b/drivers/pcmcia/pxa2xx_trizeps4.c new file mode 100644 index 000000000000..3a9ab4a39652 --- /dev/null +++ b/drivers/pcmcia/pxa2xx_trizeps4.c @@ -0,0 +1,257 @@ +/* + * linux/drivers/pcmcia/pxa2xx_trizeps4.c + * + * TRIZEPS PCMCIA specific routines. + * + * Author: Jürgen Schindele + * Created: 20 02, 2006 + * Copyright: Jürgen Schindele + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> + +#include <asm/mach-types.h> +#include <asm/irq.h> + +#include <mach/hardware.h> +#include <mach/pxa-regs.h> +#include <mach/trizeps4.h> + +#include "soc_common.h" + +extern void board_pcmcia_power(int power); + +static struct pcmcia_irqs irqs[] = { + { 0, IRQ_GPIO(GPIO_PCD), "cs0_cd" } + /* on other baseboards we can have more inputs */ +}; + +static int trizeps_pcmcia_hw_init(struct soc_pcmcia_socket *skt) +{ + int ret, i; + /* we dont have voltage/card/ready detection + * so we dont need interrupts for it + */ + switch (skt->nr) { + case 0: + if (gpio_request(GPIO_PRDY, "cf_irq") < 0) { + pr_err("%s: sock %d unable to request gpio %d\n", __func__, + skt->nr, GPIO_PRDY); + return -EBUSY; + } + if (gpio_direction_input(GPIO_PRDY) < 0) { + pr_err("%s: sock %d unable to set input gpio %d\n", __func__, + skt->nr, GPIO_PRDY); + gpio_free(GPIO_PRDY); + return -EINVAL; + } + skt->irq = IRQ_GPIO(GPIO_PRDY); + break; + +#ifndef CONFIG_MACH_TRIZEPS_CONXS + case 1: +#endif + default: + break; + } + /* release the reset of this card */ + pr_debug("%s: sock %d irq %d\n", __func__, skt->nr, skt->irq); + + /* supplementory irqs for the socket */ + for (i = 0; i < ARRAY_SIZE(irqs); i++) { + if (irqs[i].sock != skt->nr) + continue; + if (gpio_request(IRQ_TO_GPIO(irqs[i].irq), irqs[i].str) < 0) { + pr_err("%s: sock %d unable to request gpio %d\n", + __func__, skt->nr, IRQ_TO_GPIO(irqs[i].irq)); + ret = -EBUSY; + goto error; + } + if (gpio_direction_input(IRQ_TO_GPIO(irqs[i].irq)) < 0) { + pr_err("%s: sock %d unable to set input gpio %d\n", + __func__, skt->nr, IRQ_TO_GPIO(irqs[i].irq)); + ret = -EINVAL; + goto error; + } + } + return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs)); + +error: + for (; i >= 0; i--) { + gpio_free(IRQ_TO_GPIO(irqs[i].irq)); + } + return (ret); +} + +static void trizeps_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) +{ + int i; + /* free allocated gpio's */ + gpio_free(GPIO_PRDY); + for (i = 0; i < ARRAY_SIZE(irqs); i++) + gpio_free(IRQ_TO_GPIO(irqs[i].irq)); +} + +static unsigned long trizeps_pcmcia_status[2]; + +static void trizeps_pcmcia_socket_state(struct soc_pcmcia_socket *skt, + struct pcmcia_state *state) +{ + unsigned short status = 0, change; + status = CFSR_readw(); + change = (status ^ trizeps_pcmcia_status[skt->nr]) & + ConXS_CFSR_BVD_MASK; + if (change) { + trizeps_pcmcia_status[skt->nr] = status; + if (status & ConXS_CFSR_BVD1) { + /* enable_irq empty */ + } else { + /* disable_irq empty */ + } + } + + switch (skt->nr) { + case 0: + /* just fill in fix states */ + state->detect = gpio_get_value(GPIO_PCD) ? 0 : 1; + state->ready = gpio_get_value(GPIO_PRDY) ? 1 : 0; + state->bvd1 = (status & ConXS_CFSR_BVD1) ? 1 : 0; + state->bvd2 = (status & ConXS_CFSR_BVD2) ? 1 : 0; + state->vs_3v = (status & ConXS_CFSR_VS1) ? 0 : 1; + state->vs_Xv = (status & ConXS_CFSR_VS2) ? 0 : 1; + state->wrprot = 0; /* not available */ + break; + +#ifndef CONFIG_MACH_TRIZEPS_CONXS + /* on ConXS we only have one slot. Second is inactive */ + case 1: + state->detect = 0; + state->ready = 0; + state->bvd1 = 0; + state->bvd2 = 0; + state->vs_3v = 0; + state->vs_Xv = 0; + state->wrprot = 0; + break; + +#endif + } +} + +static int trizeps_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, + const socket_state_t *state) +{ + int ret = 0; + unsigned short power = 0; + + /* we do nothing here just check a bit */ + switch (state->Vcc) { + case 0: power &= 0xfc; break; + case 33: power |= ConXS_BCR_S0_VCC_3V3; break; + case 50: + pr_err("%s(): Vcc 5V not supported in socket\n", __func__); + break; + default: + pr_err("%s(): bad Vcc %u\n", __func__, state->Vcc); + ret = -1; + } + + switch (state->Vpp) { + case 0: power &= 0xf3; break; + case 33: power |= ConXS_BCR_S0_VPP_3V3; break; + case 120: + pr_err("%s(): Vpp 12V not supported in socket\n", __func__); + break; + default: + if (state->Vpp != state->Vcc) { + pr_err("%s(): bad Vpp %u\n", __func__, state->Vpp); + ret = -1; + } + } + + switch (skt->nr) { + case 0: /* we only have 3.3V */ + board_pcmcia_power(power); + break; + +#ifndef CONFIG_MACH_TRIZEPS_CONXS + /* on ConXS we only have one slot. Second is inactive */ + case 1: +#endif + default: + break; + } + + return ret; +} + +static void trizeps_pcmcia_socket_init(struct soc_pcmcia_socket *skt) +{ + /* default is on */ + board_pcmcia_power(0x9); +} + +static void trizeps_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) +{ + board_pcmcia_power(0x0); +} + +static struct pcmcia_low_level trizeps_pcmcia_ops = { + .owner = THIS_MODULE, + .hw_init = trizeps_pcmcia_hw_init, + .hw_shutdown = trizeps_pcmcia_hw_shutdown, + .socket_state = trizeps_pcmcia_socket_state, + .configure_socket = trizeps_pcmcia_configure_socket, + .socket_init = trizeps_pcmcia_socket_init, + .socket_suspend = trizeps_pcmcia_socket_suspend, +#ifdef CONFIG_MACH_TRIZEPS_CONXS + .nr = 1, +#else + .nr = 2, +#endif + .first = 0, +}; + +static struct platform_device *trizeps_pcmcia_device; + +static int __init trizeps_pcmcia_init(void) +{ + int ret; + + trizeps_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1); + if (!trizeps_pcmcia_device) + return -ENOMEM; + + ret = platform_device_add_data(trizeps_pcmcia_device, + &trizeps_pcmcia_ops, sizeof(trizeps_pcmcia_ops)); + + if (ret == 0) + ret = platform_device_add(trizeps_pcmcia_device); + + if (ret) + platform_device_put(trizeps_pcmcia_device); + + return ret; +} + +static void __exit trizeps_pcmcia_exit(void) +{ + int i; + platform_device_unregister(trizeps_pcmcia_device); +} + +fs_initcall(trizeps_pcmcia_init); +module_exit(trizeps_pcmcia_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Juergen Schindele"); +MODULE_ALIAS("platform:pxa2xx-pcmcia"); |