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