linux/drivers/pcmcia/pxa2xx_viper.c
<<
>>
Prefs
   1/*
   2 * Viper/Zeus PCMCIA support
   3 *   Copyright 2004 Arcom Control Systems
   4 *
   5 * Maintained by Marc Zyngier <maz@misterjones.org>
   6 *
   7 * Based on:
   8 *   iPAQ h2200 PCMCIA support
   9 *   Copyright 2004 Koen Kooi <koen@vestingbar.nl>
  10 *
  11 * This file is subject to the terms and conditions of the GNU General Public
  12 * License.  See the file COPYING in the main directory of this archive for
  13 * more details.
  14 */
  15
  16#include <linux/module.h>
  17#include <linux/init.h>
  18#include <linux/kernel.h>
  19#include <linux/errno.h>
  20#include <linux/interrupt.h>
  21#include <linux/platform_device.h>
  22#include <linux/gpio.h>
  23
  24#include <pcmcia/ss.h>
  25
  26#include <asm/irq.h>
  27
  28#include <linux/platform_data/pcmcia-pxa2xx_viper.h>
  29
  30#include "soc_common.h"
  31#include "pxa2xx_base.h"
  32
  33static struct platform_device *arcom_pcmcia_dev;
  34
  35static inline struct arcom_pcmcia_pdata *viper_get_pdata(void)
  36{
  37        return arcom_pcmcia_dev->dev.platform_data;
  38}
  39
  40static int viper_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
  41{
  42        struct arcom_pcmcia_pdata *pdata = viper_get_pdata();
  43        unsigned long flags;
  44
  45        skt->stat[SOC_STAT_CD].gpio = pdata->cd_gpio;
  46        skt->stat[SOC_STAT_CD].name = "PCMCIA_CD";
  47        skt->stat[SOC_STAT_RDY].gpio = pdata->rdy_gpio;
  48        skt->stat[SOC_STAT_RDY].name = "CF ready";
  49
  50        if (gpio_request(pdata->pwr_gpio, "CF power"))
  51                goto err_request_pwr;
  52
  53        local_irq_save(flags);
  54
  55        if (gpio_direction_output(pdata->pwr_gpio, 0)) {
  56                local_irq_restore(flags);
  57                goto err_dir;
  58        }
  59
  60        local_irq_restore(flags);
  61
  62        return 0;
  63
  64err_dir:
  65        gpio_free(pdata->pwr_gpio);
  66err_request_pwr:
  67        dev_err(&arcom_pcmcia_dev->dev, "Failed to setup PCMCIA GPIOs\n");
  68        return -1;
  69}
  70
  71/*
  72 * Release all resources.
  73 */
  74static void viper_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
  75{
  76        struct arcom_pcmcia_pdata *pdata = viper_get_pdata();
  77
  78        gpio_free(pdata->pwr_gpio);
  79}
  80
  81static void viper_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
  82                                      struct pcmcia_state *state)
  83{
  84        state->vs_3v  = 1; /* Can only apply 3.3V */
  85        state->vs_Xv  = 0;
  86}
  87
  88static int viper_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
  89                                         const socket_state_t *state)
  90{
  91        struct arcom_pcmcia_pdata *pdata = viper_get_pdata();
  92
  93        /* Silently ignore Vpp, output enable, speaker enable. */
  94        pdata->reset(state->flags & SS_RESET);
  95
  96        /* Apply socket voltage */
  97        switch (state->Vcc) {
  98        case 0:
  99                gpio_set_value(pdata->pwr_gpio, 0);
 100                break;
 101        case 33:
 102                gpio_set_value(pdata->pwr_gpio, 1);
 103                break;
 104        default:
 105                dev_err(&arcom_pcmcia_dev->dev, "Unsupported Vcc:%d\n", state->Vcc);
 106                return -1;
 107        }
 108
 109        return 0;
 110}
 111
 112static struct pcmcia_low_level viper_pcmcia_ops = {
 113        .owner                  = THIS_MODULE,
 114        .hw_init                = viper_pcmcia_hw_init,
 115        .hw_shutdown            = viper_pcmcia_hw_shutdown,
 116        .socket_state           = viper_pcmcia_socket_state,
 117        .configure_socket       = viper_pcmcia_configure_socket,
 118        .nr                     = 1,
 119};
 120
 121static struct platform_device *viper_pcmcia_device;
 122
 123static int viper_pcmcia_probe(struct platform_device *pdev)
 124{
 125        int ret;
 126
 127        /* I can't imagine more than one device, but you never know... */
 128        if (arcom_pcmcia_dev)
 129                return -EEXIST;
 130
 131        if (!pdev->dev.platform_data)
 132                return -EINVAL;
 133
 134        viper_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
 135        if (!viper_pcmcia_device)
 136                return -ENOMEM;
 137
 138        arcom_pcmcia_dev = pdev;
 139
 140        viper_pcmcia_device->dev.parent = &pdev->dev;
 141
 142        ret = platform_device_add_data(viper_pcmcia_device,
 143                                       &viper_pcmcia_ops,
 144                                       sizeof(viper_pcmcia_ops));
 145
 146        if (!ret)
 147                ret = platform_device_add(viper_pcmcia_device);
 148
 149        if (ret) {
 150                platform_device_put(viper_pcmcia_device);
 151                arcom_pcmcia_dev = NULL;
 152        }
 153
 154        return ret;
 155}
 156
 157static int viper_pcmcia_remove(struct platform_device *pdev)
 158{
 159        platform_device_unregister(viper_pcmcia_device);
 160        arcom_pcmcia_dev = NULL;
 161        return 0;
 162}
 163
 164static struct platform_device_id viper_pcmcia_id_table[] = {
 165        { .name = "viper-pcmcia", },
 166        { .name = "zeus-pcmcia",  },
 167        { },
 168};
 169
 170static struct platform_driver viper_pcmcia_driver = {
 171        .probe          = viper_pcmcia_probe,
 172        .remove         = viper_pcmcia_remove,
 173        .driver         = {
 174                .name   = "arcom-pcmcia",
 175        },
 176        .id_table       = viper_pcmcia_id_table,
 177};
 178
 179module_platform_driver(viper_pcmcia_driver);
 180
 181MODULE_DEVICE_TABLE(platform, viper_pcmcia_id_table);
 182MODULE_LICENSE("GPL");
 183