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        { 0, IRQ_GPIO(GPIO_PCD), "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 = IRQ_GPIO(GPIO_PRDY);
  57                break;
  58
  59#ifndef CONFIG_MACH_TRIZEPS_CONXS
  60        case 1:
  61#endif
  62        default:
  63                break;
  64        }
  65        /* release the reset of this card */
  66        pr_debug("%s: sock %d irq %d\n", __func__, skt->nr, skt->socket.pci_irq);
  67
  68        /* supplementory irqs for the socket */
  69        for (i = 0; i < ARRAY_SIZE(irqs); i++) {
  70                if (irqs[i].sock != skt->nr)
  71                        continue;
  72                if (gpio_request(IRQ_TO_GPIO(irqs[i].irq), irqs[i].str) < 0) {
  73                        pr_err("%s: sock %d unable to request gpio %d\n",
  74                                __func__, skt->nr, IRQ_TO_GPIO(irqs[i].irq));
  75                        ret = -EBUSY;
  76                        goto error;
  77                }
  78                if (gpio_direction_input(IRQ_TO_GPIO(irqs[i].irq)) < 0) {
  79                        pr_err("%s: sock %d unable to set input gpio %d\n",
  80                                __func__, skt->nr, IRQ_TO_GPIO(irqs[i].irq));
  81                        ret = -EINVAL;
  82                        goto error;
  83                }
  84        }
  85        return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
  86
  87error:
  88        for (; i >= 0; i--) {
  89                gpio_free(IRQ_TO_GPIO(irqs[i].irq));
  90        }
  91        return (ret);
  92}
  93
  94static void trizeps_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
  95{
  96        int i;
  97        /* free allocated gpio's */
  98        gpio_free(GPIO_PRDY);
  99        for (i = 0; i < ARRAY_SIZE(irqs); i++)
 100                gpio_free(IRQ_TO_GPIO(irqs[i].irq));
 101}
 102
 103static unsigned long trizeps_pcmcia_status[2];
 104
 105static void trizeps_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
 106                                struct pcmcia_state *state)
 107{
 108        unsigned short status = 0, change;
 109        status = CFSR_readw();
 110        change = (status ^ trizeps_pcmcia_status[skt->nr]) &
 111                                ConXS_CFSR_BVD_MASK;
 112        if (change) {
 113                trizeps_pcmcia_status[skt->nr] = status;
 114                if (status & ConXS_CFSR_BVD1) {
 115                        /* enable_irq empty */
 116                } else {
 117                        /* disable_irq empty */
 118                }
 119        }
 120
 121        switch (skt->nr) {
 122        case 0:
 123                /* just fill in fix states */
 124                state->detect = gpio_get_value(GPIO_PCD) ? 0 : 1;
 125                state->ready  = gpio_get_value(GPIO_PRDY) ? 1 : 0;
 126                state->bvd1   = (status & ConXS_CFSR_BVD1) ? 1 : 0;
 127                state->bvd2   = (status & ConXS_CFSR_BVD2) ? 1 : 0;
 128                state->vs_3v  = (status & ConXS_CFSR_VS1) ? 0 : 1;
 129                state->vs_Xv  = (status & ConXS_CFSR_VS2) ? 0 : 1;
 130                state->wrprot = 0;      /* not available */
 131                break;
 132
 133#ifndef CONFIG_MACH_TRIZEPS_CONXS
 134        /* on ConXS we only have one slot. Second is inactive */
 135        case 1:
 136                state->detect = 0;
 137                state->ready  = 0;
 138                state->bvd1   = 0;
 139                state->bvd2   = 0;
 140                state->vs_3v  = 0;
 141                state->vs_Xv  = 0;
 142                state->wrprot = 0;
 143                break;
 144
 145#endif
 146        }
 147}
 148
 149static int trizeps_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
 150                                const socket_state_t *state)
 151{
 152        int ret = 0;
 153        unsigned short power = 0;
 154
 155        /* we do nothing here just check a bit */
 156        switch (state->Vcc) {
 157        case 0:  power &= 0xfc; break;
 158        case 33: power |= ConXS_BCR_S0_VCC_3V3; break;
 159        case 50:
 160                pr_err("%s(): Vcc 5V not supported in socket\n", __func__);
 161                break;
 162        default:
 163                pr_err("%s(): bad Vcc %u\n", __func__, state->Vcc);
 164                ret = -1;
 165        }
 166
 167        switch (state->Vpp) {
 168        case 0:  power &= 0xf3; break;
 169        case 33: power |= ConXS_BCR_S0_VPP_3V3; break;
 170        case 120:
 171                pr_err("%s(): Vpp 12V not supported in socket\n", __func__);
 172                break;
 173        default:
 174                if (state->Vpp != state->Vcc) {
 175                        pr_err("%s(): bad Vpp %u\n", __func__, state->Vpp);
 176                        ret = -1;
 177                }
 178        }
 179
 180        switch (skt->nr) {
 181        case 0:                  /* we only have 3.3V */
 182                board_pcmcia_power(power);
 183                break;
 184
 185#ifndef CONFIG_MACH_TRIZEPS_CONXS
 186        /* on ConXS we only have one slot. Second is inactive */
 187        case 1:
 188#endif
 189        default:
 190                break;
 191        }
 192
 193        return ret;
 194}
 195
 196static void trizeps_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
 197{
 198        /* default is on */
 199        board_pcmcia_power(0x9);
 200}
 201
 202static void trizeps_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
 203{
 204        board_pcmcia_power(0x0);
 205}
 206
 207static struct pcmcia_low_level trizeps_pcmcia_ops = {
 208        .owner                  = THIS_MODULE,
 209        .hw_init                = trizeps_pcmcia_hw_init,
 210        .hw_shutdown            = trizeps_pcmcia_hw_shutdown,
 211        .socket_state           = trizeps_pcmcia_socket_state,
 212        .configure_socket       = trizeps_pcmcia_configure_socket,
 213        .socket_init            = trizeps_pcmcia_socket_init,
 214        .socket_suspend         = trizeps_pcmcia_socket_suspend,
 215#ifdef CONFIG_MACH_TRIZEPS_CONXS
 216        .nr                     = 1,
 217#else
 218        .nr                     = 2,
 219#endif
 220        .first                  = 0,
 221};
 222
 223static struct platform_device *trizeps_pcmcia_device;
 224
 225static int __init trizeps_pcmcia_init(void)
 226{
 227        int ret;
 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