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-2011 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 struct gpio palmtc_pcmcia_gpios[] = {
  25        { GPIO_NR_PALMTC_PCMCIA_POWER1, GPIOF_INIT_LOW, "PCMCIA Power 1" },
  26        { GPIO_NR_PALMTC_PCMCIA_POWER2, GPIOF_INIT_LOW, "PCMCIA Power 2" },
  27        { GPIO_NR_PALMTC_PCMCIA_POWER3, GPIOF_INIT_LOW, "PCMCIA Power 3" },
  28        { GPIO_NR_PALMTC_PCMCIA_RESET,  GPIOF_INIT_HIGH,"PCMCIA Reset" },
  29        { GPIO_NR_PALMTC_PCMCIA_PWRREADY, GPIOF_IN,     "PCMCIA Power Ready" },
  30};
  31
  32static int palmtc_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
  33{
  34        int ret;
  35
  36        ret = gpio_request_array(palmtc_pcmcia_gpios,
  37                                ARRAY_SIZE(palmtc_pcmcia_gpios));
  38
  39        skt->stat[SOC_STAT_RDY].gpio = GPIO_NR_PALMTC_PCMCIA_READY;
  40        skt->stat[SOC_STAT_RDY].name = "PCMCIA Ready";
  41
  42        return ret;
  43}
  44
  45static void palmtc_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
  46{
  47        gpio_free_array(palmtc_pcmcia_gpios, ARRAY_SIZE(palmtc_pcmcia_gpios));
  48}
  49
  50static void palmtc_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
  51                                        struct pcmcia_state *state)
  52{
  53        state->detect = 1; /* always inserted */
  54        state->vs_3v  = 1;
  55        state->vs_Xv  = 0;
  56}
  57
  58static int palmtc_wifi_powerdown(void)
  59{
  60        gpio_set_value(GPIO_NR_PALMTC_PCMCIA_RESET, 1);
  61        gpio_set_value(GPIO_NR_PALMTC_PCMCIA_POWER2, 0);
  62        mdelay(40);
  63        gpio_set_value(GPIO_NR_PALMTC_PCMCIA_POWER1, 0);
  64        return 0;
  65}
  66
  67static int palmtc_wifi_powerup(void)
  68{
  69        int timeout = 50;
  70
  71        gpio_set_value(GPIO_NR_PALMTC_PCMCIA_POWER3, 1);
  72        mdelay(50);
  73
  74        /* Power up the card, 1.8V first, after a while 3.3V */
  75        gpio_set_value(GPIO_NR_PALMTC_PCMCIA_POWER1, 1);
  76        mdelay(100);
  77        gpio_set_value(GPIO_NR_PALMTC_PCMCIA_POWER2, 1);
  78
  79        /* Wait till the card is ready */
  80        while (!gpio_get_value(GPIO_NR_PALMTC_PCMCIA_PWRREADY) &&
  81                timeout) {
  82                mdelay(1);
  83                timeout--;
  84        }
  85
  86        /* Power down the WiFi in case of error */
  87        if (!timeout) {
  88                palmtc_wifi_powerdown();
  89                return 1;
  90        }
  91
  92        /* Reset the card */
  93        gpio_set_value(GPIO_NR_PALMTC_PCMCIA_RESET, 1);
  94        mdelay(20);
  95        gpio_set_value(GPIO_NR_PALMTC_PCMCIA_RESET, 0);
  96        mdelay(25);
  97
  98        gpio_set_value(GPIO_NR_PALMTC_PCMCIA_POWER3, 0);
  99
 100        return 0;
 101}
 102
 103static int palmtc_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
 104                                        const socket_state_t *state)
 105{
 106        int ret = 1;
 107
 108        if (state->Vcc == 0)
 109                ret = palmtc_wifi_powerdown();
 110        else if (state->Vcc == 33)
 111                ret = palmtc_wifi_powerup();
 112
 113        return ret;
 114}
 115
 116static struct pcmcia_low_level palmtc_pcmcia_ops = {
 117        .owner                  = THIS_MODULE,
 118
 119        .first                  = 0,
 120        .nr                     = 1,
 121
 122        .hw_init                = palmtc_pcmcia_hw_init,
 123        .hw_shutdown            = palmtc_pcmcia_hw_shutdown,
 124
 125        .socket_state           = palmtc_pcmcia_socket_state,
 126        .configure_socket       = palmtc_pcmcia_configure_socket,
 127};
 128
 129static struct platform_device *palmtc_pcmcia_device;
 130
 131static int __init palmtc_pcmcia_init(void)
 132{
 133        int ret;
 134
 135        if (!machine_is_palmtc())
 136                return -ENODEV;
 137
 138        palmtc_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
 139        if (!palmtc_pcmcia_device)
 140                return -ENOMEM;
 141
 142        ret = platform_device_add_data(palmtc_pcmcia_device, &palmtc_pcmcia_ops,
 143                                        sizeof(palmtc_pcmcia_ops));
 144
 145        if (!ret)
 146                ret = platform_device_add(palmtc_pcmcia_device);
 147
 148        if (ret)
 149                platform_device_put(palmtc_pcmcia_device);
 150
 151        return ret;
 152}
 153
 154static void __exit palmtc_pcmcia_exit(void)
 155{
 156        platform_device_unregister(palmtc_pcmcia_device);
 157}
 158
 159module_init(palmtc_pcmcia_init);
 160module_exit(palmtc_pcmcia_exit);
 161
 162MODULE_AUTHOR("Alex Osborne <ato@meshy.org>,"
 163            " Marek Vasut <marek.vasut@gmail.com>");
 164MODULE_DESCRIPTION("PCMCIA support for Palm Tungsten|C");
 165MODULE_ALIAS("platform:pxa2xx-pcmcia");
 166MODULE_LICENSE("GPL");
 167