linux/drivers/pcmcia/pxa2xx_palmtc.c
<<
>>
Prefs
   1/*
   2 * linux/drivers/pcmcia/pxa2xx_palmtc.c
   3 *
   4 * Driver for Palm Tungsten|C PCMCIA
   5 *
   6 * Copyright (C) 2008 Alex Osborne <ato@meshy.org>
   7 * Copyright (C) 2009 Marek Vasut <marek.vasut@gmail.com>
   8 *
   9 * This program is free software; you can redistribute it and/or modify
  10 * it under the terms of the GNU General Public License version 2 as
  11 * published by the Free Software Foundation.
  12 *
  13 */
  14
  15#include <linux/module.h>
  16#include <linux/platform_device.h>
  17#include <linux/gpio.h>
  18#include <linux/delay.h>
  19
  20#include <asm/mach-types.h>
  21#include <mach/palmtc.h>
  22#include "soc_common.h"
  23
  24static int palmtc_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
  25{
  26        int ret;
  27
  28        ret = gpio_request(GPIO_NR_PALMTC_PCMCIA_POWER1, "PCMCIA PWR1");
  29        if (ret)
  30                goto err1;
  31        ret = gpio_direction_output(GPIO_NR_PALMTC_PCMCIA_POWER1, 0);
  32        if (ret)
  33                goto err2;
  34
  35        ret = gpio_request(GPIO_NR_PALMTC_PCMCIA_POWER2, "PCMCIA PWR2");
  36        if (ret)
  37                goto err2;
  38        ret = gpio_direction_output(GPIO_NR_PALMTC_PCMCIA_POWER2, 0);
  39        if (ret)
  40                goto err3;
  41
  42        ret = gpio_request(GPIO_NR_PALMTC_PCMCIA_POWER3, "PCMCIA PWR3");
  43        if (ret)
  44                goto err3;
  45        ret = gpio_direction_output(GPIO_NR_PALMTC_PCMCIA_POWER3, 0);
  46        if (ret)
  47                goto err4;
  48
  49        ret = gpio_request(GPIO_NR_PALMTC_PCMCIA_RESET, "PCMCIA RST");
  50        if (ret)
  51                goto err4;
  52        ret = gpio_direction_output(GPIO_NR_PALMTC_PCMCIA_RESET, 1);
  53        if (ret)
  54                goto err5;
  55
  56        ret = gpio_request(GPIO_NR_PALMTC_PCMCIA_READY, "PCMCIA RDY");
  57        if (ret)
  58                goto err5;
  59        ret = gpio_direction_input(GPIO_NR_PALMTC_PCMCIA_READY);
  60        if (ret)
  61                goto err6;
  62
  63        ret = gpio_request(GPIO_NR_PALMTC_PCMCIA_PWRREADY, "PCMCIA PWRRDY");
  64        if (ret)
  65                goto err6;
  66        ret = gpio_direction_input(GPIO_NR_PALMTC_PCMCIA_PWRREADY);
  67        if (ret)
  68                goto err7;
  69
  70        skt->irq = IRQ_GPIO(GPIO_NR_PALMTC_PCMCIA_READY);
  71        return 0;
  72
  73err7:
  74        gpio_free(GPIO_NR_PALMTC_PCMCIA_PWRREADY);
  75err6:
  76        gpio_free(GPIO_NR_PALMTC_PCMCIA_READY);
  77err5:
  78        gpio_free(GPIO_NR_PALMTC_PCMCIA_RESET);
  79err4:
  80        gpio_free(GPIO_NR_PALMTC_PCMCIA_POWER3);
  81err3:
  82        gpio_free(GPIO_NR_PALMTC_PCMCIA_POWER2);
  83err2:
  84        gpio_free(GPIO_NR_PALMTC_PCMCIA_POWER1);
  85err1:
  86        return ret;
  87}
  88
  89static void palmtc_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
  90{
  91        gpio_free(GPIO_NR_PALMTC_PCMCIA_PWRREADY);
  92        gpio_free(GPIO_NR_PALMTC_PCMCIA_READY);
  93        gpio_free(GPIO_NR_PALMTC_PCMCIA_RESET);
  94        gpio_free(GPIO_NR_PALMTC_PCMCIA_POWER3);
  95        gpio_free(GPIO_NR_PALMTC_PCMCIA_POWER2);
  96        gpio_free(GPIO_NR_PALMTC_PCMCIA_POWER1);
  97}
  98
  99static void palmtc_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
 100                                        struct pcmcia_state *state)
 101{
 102        state->detect = 1; /* always inserted */
 103        state->ready  = !!gpio_get_value(GPIO_NR_PALMTC_PCMCIA_READY);
 104        state->bvd1   = 1;
 105        state->bvd2   = 1;
 106        state->wrprot = 0;
 107        state->vs_3v  = 1;
 108        state->vs_Xv  = 0;
 109}
 110
 111static int palmtc_wifi_powerdown(void)
 112{
 113        gpio_set_value(GPIO_NR_PALMTC_PCMCIA_RESET, 1);
 114        gpio_set_value(GPIO_NR_PALMTC_PCMCIA_POWER2, 0);
 115        mdelay(40);
 116        gpio_set_value(GPIO_NR_PALMTC_PCMCIA_POWER1, 0);
 117        return 0;
 118}
 119
 120static int palmtc_wifi_powerup(void)
 121{
 122        int timeout = 50;
 123
 124        gpio_set_value(GPIO_NR_PALMTC_PCMCIA_POWER3, 1);
 125        mdelay(50);
 126
 127        /* Power up the card, 1.8V first, after a while 3.3V */
 128        gpio_set_value(GPIO_NR_PALMTC_PCMCIA_POWER1, 1);
 129        mdelay(100);
 130        gpio_set_value(GPIO_NR_PALMTC_PCMCIA_POWER2, 1);
 131
 132        /* Wait till the card is ready */
 133        while (!gpio_get_value(GPIO_NR_PALMTC_PCMCIA_PWRREADY) &&
 134                timeout) {
 135                mdelay(1);
 136                timeout--;
 137        }
 138
 139        /* Power down the WiFi in case of error */
 140        if (!timeout) {
 141                palmtc_wifi_powerdown();
 142                return 1;
 143        }
 144
 145        /* Reset the card */
 146        gpio_set_value(GPIO_NR_PALMTC_PCMCIA_RESET, 1);
 147        mdelay(20);
 148        gpio_set_value(GPIO_NR_PALMTC_PCMCIA_RESET, 0);
 149        mdelay(25);
 150
 151        gpio_set_value(GPIO_NR_PALMTC_PCMCIA_POWER3, 0);
 152
 153        return 0;
 154}
 155
 156static int palmtc_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
 157                                        const socket_state_t *state)
 158{
 159        int ret = 1;
 160
 161        if (state->Vcc == 0)
 162                ret = palmtc_wifi_powerdown();
 163        else if (state->Vcc == 33)
 164                ret = palmtc_wifi_powerup();
 165
 166        return ret;
 167}
 168
 169static void palmtc_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
 170{
 171}
 172
 173static void palmtc_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
 174{
 175}
 176
 177static struct pcmcia_low_level palmtc_pcmcia_ops = {
 178        .owner                  = THIS_MODULE,
 179
 180        .first                  = 0,
 181        .nr                     = 1,
 182
 183        .hw_init                = palmtc_pcmcia_hw_init,
 184        .hw_shutdown            = palmtc_pcmcia_hw_shutdown,
 185
 186        .socket_state           = palmtc_pcmcia_socket_state,
 187        .configure_socket       = palmtc_pcmcia_configure_socket,
 188
 189        .socket_init            = palmtc_pcmcia_socket_init,
 190        .socket_suspend         = palmtc_pcmcia_socket_suspend,
 191};
 192
 193static struct platform_device *palmtc_pcmcia_device;
 194
 195static int __init palmtc_pcmcia_init(void)
 196{
 197        int ret;
 198
 199        if (!machine_is_palmtc())
 200                return -ENODEV;
 201
 202        palmtc_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
 203        if (!palmtc_pcmcia_device)
 204                return -ENOMEM;
 205
 206        ret = platform_device_add_data(palmtc_pcmcia_device, &palmtc_pcmcia_ops,
 207                                        sizeof(palmtc_pcmcia_ops));
 208
 209        if (!ret)
 210                ret = platform_device_add(palmtc_pcmcia_device);
 211
 212        if (ret)
 213                platform_device_put(palmtc_pcmcia_device);
 214
 215        return ret;
 216}
 217
 218static void __exit palmtc_pcmcia_exit(void)
 219{
 220        platform_device_unregister(palmtc_pcmcia_device);
 221}
 222
 223module_init(palmtc_pcmcia_init);
 224module_exit(palmtc_pcmcia_exit);
 225
 226MODULE_AUTHOR("Alex Osborne <ato@meshy.org>,"
 227            " Marek Vasut <marek.vasut@gmail.com>");
 228MODULE_DESCRIPTION("PCMCIA support for Palm Tungsten|C");
 229MODULE_ALIAS("platform:pxa2xx-pcmcia");
 230MODULE_LICENSE("GPL");
 231