linux/drivers/pcmcia/pxa2xx_trizeps4.c
<<
>>
Prefs
   1/*
   2 * linux/drivers/pcmcia/pxa2xx_trizeps4.c
   3 *
   4 * TRIZEPS PCMCIA specific routines.
   5 *
   6 * Author:      J\xC3\xBCrgen Schindele
   7 * Created:     20 02, 2006
   8 * Copyright:   J\xC3\xBCrgen Schindele
   9 *
  10 * This program is free software; you can redistribute it and/or modify
  11 * it under the terms of the GNU General Public License version 2 as
  12 * published by the Free Software Foundation.
  13 */
  14
  15#include <linux/module.h>
  16#include <linux/init.h>
  17#include <linux/kernel.h>
  18#include <linux/gpio.h>
  19#include <linux/interrupt.h>
  20#include <linux/platform_device.h>
  21
  22#include <asm/mach-types.h>
  23#include <asm/irq.h>
  24
  25#include <mach/hardware.h>
  26#include <mach/pxa-regs.h>
  27#include <mach/trizeps4.h>
  28
  29#include "soc_common.h"
  30
  31extern void board_pcmcia_power(int power);
  32
  33static struct pcmcia_irqs irqs[] = {
  34        { 0, IRQ_GPIO(GPIO_PCD), "cs0_cd" }
  35        /* on other baseboards we can have more inputs */
  36};
  37
  38static int trizeps_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
  39{
  40        int ret, i;
  41        /* we dont have voltage/card/ready detection
  42         * so we dont need interrupts for it
  43         */
  44        switch (skt->nr) {
  45        case 0:
  46                if (gpio_request(GPIO_PRDY, "cf_irq") < 0) {
  47                        pr_err("%s: sock %d unable to request gpio %d\n", __func__,
  48                                skt->nr, GPIO_PRDY);
  49                        return -EBUSY;
  50                }
  51                if (gpio_direction_input(GPIO_PRDY) < 0) {
  52                        pr_err("%s: sock %d unable to set input gpio %d\n", __func__,
  53                                skt->nr, GPIO_PRDY);
  54                        gpio_free(GPIO_PRDY);
  55                        return -EINVAL;
  56                }
  57                skt->irq = IRQ_GPIO(GPIO_PRDY);
  58                break;
  59
  60#ifndef CONFIG_MACH_TRIZEPS_CONXS
  61        case 1:
  62#endif
  63        default:
  64                break;
  65        }
  66        /* release the reset of this card */
  67        pr_debug("%s: sock %d irq %d\n", __func__, skt->nr, skt->irq);
  68
  69        /* supplementory irqs for the socket */
  70        for (i = 0; i < ARRAY_SIZE(irqs); i++) {
  71                if (irqs[i].sock != skt->nr)
  72                        continue;
  73                if (gpio_request(IRQ_TO_GPIO(irqs[i].irq), irqs[i].str) < 0) {
  74                        pr_err("%s: sock %d unable to request gpio %d\n",
  75                                __func__, skt->nr, IRQ_TO_GPIO(irqs[i].irq));
  76                        ret = -EBUSY;
  77                        goto error;
  78                }
  79                if (gpio_direction_input(IRQ_TO_GPIO(irqs[i].irq)) < 0) {
  80                        pr_err("%s: sock %d unable to set input gpio %d\n",
  81                                __func__, skt->nr, IRQ_TO_GPIO(irqs[i].irq));
  82                        ret = -EINVAL;
  83                        goto error;
  84                }
  85        }
  86        return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
  87
  88error:
  89        for (; i >= 0; i--) {
  90                gpio_free(IRQ_TO_GPIO(irqs[i].irq));
  91        }
  92        return (ret);
  93}
  94
  95static void trizeps_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
  96{
  97        int i;
  98        /* free allocated gpio's */
  99        gpio_free(GPIO_PRDY);
 100        for (i = 0; i < ARRAY_SIZE(irqs); i++)
 101                gpio_free(IRQ_TO_GPIO(irqs[i].irq));
 102}
 103
 104static unsigned long trizeps_pcmcia_status[2];
 105
 106static void trizeps_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
 107                                struct pcmcia_state *state)
 108{
 109        unsigned short status = 0, change;
 110        status = CFSR_readw();
 111        change = (status ^ trizeps_pcmcia_status[skt->nr]) &
 112                                ConXS_CFSR_BVD_MASK;
 113        if (change) {
 114                trizeps_pcmcia_status[skt->nr] = status;
 115                if (status & ConXS_CFSR_BVD1) {
 116                        /* enable_irq empty */
 117                } else {
 118                        /* disable_irq empty */
 119                }
 120        }
 121
 122        switch (skt->nr) {
 123        case 0:
 124                /* just fill in fix states */
 125                state->detect = gpio_get_value(GPIO_PCD) ? 0 : 1;
 126                state->ready  = gpio_get_value(GPIO_PRDY) ? 1 : 0;
 127                state->bvd1   = (status & ConXS_CFSR_BVD1) ? 1 : 0;
 128                state->bvd2   = (status & ConXS_CFSR_BVD2) ? 1 : 0;
 129                state->vs_3v  = (status & ConXS_CFSR_VS1) ? 0 : 1;
 130                state->vs_Xv  = (status & ConXS_CFSR_VS2) ? 0 : 1;
 131                state->wrprot = 0;      /* not available */
 132                break;
 133
 134#ifndef CONFIG_MACH_TRIZEPS_CONXS
 135        /* on ConXS we only have one slot. Second is inactive */
 136        case 1:
 137                state->detect = 0;
 138                state->ready  = 0;
 139                state->bvd1   = 0;
 140                state->bvd2   = 0;
 141                state->vs_3v  = 0;
 142                state->vs_Xv  = 0;
 143                state->wrprot = 0;
 144                break;
 145
 146#endif
 147        }
 148}
 149
 150static int trizeps_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
 151                                const socket_state_t *state)
 152{
 153        int ret = 0;
 154        unsigned short power = 0;
 155
 156        /* we do nothing here just check a bit */
 157        switch (state->Vcc) {
 158        case 0:  power &= 0xfc; break;
 159        case 33: power |= ConXS_BCR_S0_VCC_3V3; break;
 160        case 50:
 161                pr_err("%s(): Vcc 5V not supported in socket\n", __func__);
 162                break;
 163        default:
 164                pr_err("%s(): bad Vcc %u\n", __func__, state->Vcc);
 165                ret = -1;
 166        }
 167
 168        switch (state->Vpp) {
 169        case 0:  power &= 0xf3; break;
 170        case 33: power |= ConXS_BCR_S0_VPP_3V3; break;
 171        case 120:
 172                pr_err("%s(): Vpp 12V not supported in socket\n", __func__);
 173                break;
 174        default:
 175                if (state->Vpp != state->Vcc) {
 176                        pr_err("%s(): bad Vpp %u\n", __func__, state->Vpp);
 177                        ret = -1;
 178                }
 179        }
 180
 181        switch (skt->nr) {
 182        case 0:                  /* we only have 3.3V */
 183                board_pcmcia_power(power);
 184                break;
 185
 186#ifndef CONFIG_MACH_TRIZEPS_CONXS
 187        /* on ConXS we only have one slot. Second is inactive */
 188        case 1:
 189#endif
 190        default:
 191                break;
 192        }
 193
 194        return ret;
 195}
 196
 197static void trizeps_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
 198{
 199        /* default is on */
 200        board_pcmcia_power(0x9);
 201}
 202
 203static void trizeps_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
 204{
 205        board_pcmcia_power(0x0);
 206}
 207
 208static struct pcmcia_low_level trizeps_pcmcia_ops = {
 209        .owner                  = THIS_MODULE,
 210        .hw_init                = trizeps_pcmcia_hw_init,
 211        .hw_shutdown            = trizeps_pcmcia_hw_shutdown,
 212        .socket_state           = trizeps_pcmcia_socket_state,
 213        .configure_socket       = trizeps_pcmcia_configure_socket,
 214        .socket_init            = trizeps_pcmcia_socket_init,
 215        .socket_suspend         = trizeps_pcmcia_socket_suspend,
 216#ifdef CONFIG_MACH_TRIZEPS_CONXS
 217        .nr                     = 1,
 218#else
 219        .nr                     = 2,
 220#endif
 221        .first                  = 0,
 222};
 223
 224static struct platform_device *trizeps_pcmcia_device;
 225
 226static int __init trizeps_pcmcia_init(void)
 227{
 228        int ret;
 229
 230        trizeps_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
 231        if (!trizeps_pcmcia_device)
 232                return -ENOMEM;
 233
 234        ret = platform_device_add_data(trizeps_pcmcia_device,
 235                        &trizeps_pcmcia_ops, sizeof(trizeps_pcmcia_ops));
 236
 237        if (ret == 0)
 238                ret = platform_device_add(trizeps_pcmcia_device);
 239
 240        if (ret)
 241                platform_device_put(trizeps_pcmcia_device);
 242
 243        return ret;
 244}
 245
 246static void __exit trizeps_pcmcia_exit(void)
 247{
 248        platform_device_unregister(trizeps_pcmcia_device);
 249}
 250
 251fs_initcall(trizeps_pcmcia_init);
 252module_exit(trizeps_pcmcia_exit);
 253
 254MODULE_LICENSE("GPL");
 255MODULE_AUTHOR("Juergen Schindele");
 256MODULE_ALIAS("platform:pxa2xx-pcmcia");
 257