linux/drivers/pcmcia/sa1100_nanoengine.c
<<
>>
Prefs
   1/*
   2 * drivers/pcmcia/sa1100_nanoengine.c
   3 *
   4 * PCMCIA implementation routines for BSI nanoEngine.
   5 *
   6 * In order to have a fully functional pcmcia subsystem in a BSE nanoEngine
   7 * board you should carefully read this:
   8 * http://cambuca.ldhs.cetuc.puc-rio.br/nanoengine/
   9 *
  10 * Copyright (C) 2010 Marcelo Roberto Jimenez <mroberto@cpti.cetuc.puc-rio.br>
  11 *
  12 * Based on original work for kernel 2.4 by
  13 * Miguel Freitas <miguel@cpti.cetuc.puc-rio.br>
  14 *
  15 * This program is free software; you can redistribute it and/or modify
  16 * it under the terms of the GNU General Public License version 2 as
  17 * published by the Free Software Foundation.
  18 *
  19 */
  20#include <linux/device.h>
  21#include <linux/errno.h>
  22#include <linux/interrupt.h>
  23#include <linux/irq.h>
  24#include <linux/init.h>
  25#include <linux/kernel.h>
  26#include <linux/module.h>
  27#include <linux/signal.h>
  28
  29#include <asm/mach-types.h>
  30#include <asm/irq.h>
  31
  32#include <mach/hardware.h>
  33#include <mach/nanoengine.h>
  34
  35#include "sa1100_generic.h"
  36
  37static struct pcmcia_irqs irqs_skt0[] = {
  38        /* socket, IRQ, name */
  39        { 0, NANOENGINE_IRQ_GPIO_PC_CD0, "PC CD0" },
  40};
  41
  42static struct pcmcia_irqs irqs_skt1[] = {
  43        /* socket, IRQ, name */
  44        { 1, NANOENGINE_IRQ_GPIO_PC_CD1, "PC CD1" },
  45};
  46
  47struct nanoengine_pins {
  48        unsigned input_pins;
  49        unsigned output_pins;
  50        unsigned clear_outputs;
  51        unsigned transition_pins;
  52        unsigned pci_irq;
  53        struct pcmcia_irqs *pcmcia_irqs;
  54        unsigned pcmcia_irqs_size;
  55};
  56
  57static struct nanoengine_pins nano_skts[] = {
  58        {
  59                .input_pins             = GPIO_PC_READY0 | GPIO_PC_CD0,
  60                .output_pins            = GPIO_PC_RESET0,
  61                .clear_outputs          = GPIO_PC_RESET0,
  62                .transition_pins        = NANOENGINE_IRQ_GPIO_PC_CD0,
  63                .pci_irq                = NANOENGINE_IRQ_GPIO_PC_READY0,
  64                .pcmcia_irqs            = irqs_skt0,
  65                .pcmcia_irqs_size       = ARRAY_SIZE(irqs_skt0)
  66        }, {
  67                .input_pins             = GPIO_PC_READY1 | GPIO_PC_CD1,
  68                .output_pins            = GPIO_PC_RESET1,
  69                .clear_outputs          = GPIO_PC_RESET1,
  70                .transition_pins        = NANOENGINE_IRQ_GPIO_PC_CD1,
  71                .pci_irq                = NANOENGINE_IRQ_GPIO_PC_READY1,
  72                .pcmcia_irqs            = irqs_skt1,
  73                .pcmcia_irqs_size       = ARRAY_SIZE(irqs_skt1)
  74        }
  75};
  76
  77unsigned num_nano_pcmcia_sockets = ARRAY_SIZE(nano_skts);
  78
  79static int nanoengine_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
  80{
  81        unsigned i = skt->nr;
  82
  83        if (i >= num_nano_pcmcia_sockets)
  84                return -ENXIO;
  85
  86        GPDR &= ~nano_skts[i].input_pins;
  87        GPDR |= nano_skts[i].output_pins;
  88        GPCR = nano_skts[i].clear_outputs;
  89        set_irq_type(nano_skts[i].transition_pins, IRQ_TYPE_EDGE_BOTH);
  90        skt->socket.pci_irq = nano_skts[i].pci_irq;
  91
  92        return soc_pcmcia_request_irqs(skt,
  93                nano_skts[i].pcmcia_irqs, nano_skts[i].pcmcia_irqs_size);
  94}
  95
  96/*
  97 * Release all resources.
  98 */
  99static void nanoengine_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
 100{
 101        unsigned i = skt->nr;
 102
 103        if (i >= num_nano_pcmcia_sockets)
 104                return;
 105
 106        soc_pcmcia_free_irqs(skt,
 107                nano_skts[i].pcmcia_irqs, nano_skts[i].pcmcia_irqs_size);
 108}
 109
 110static int nanoengine_pcmcia_configure_socket(
 111        struct soc_pcmcia_socket *skt, const socket_state_t *state)
 112{
 113        unsigned reset;
 114        unsigned i = skt->nr;
 115
 116        if (i >= num_nano_pcmcia_sockets)
 117                return -ENXIO;
 118
 119        switch (i) {
 120        case 0:
 121                reset = GPIO_PC_RESET0;
 122                break;
 123        case 1:
 124                reset = GPIO_PC_RESET1;
 125                break;
 126        default:
 127                return -ENXIO;
 128        }
 129
 130        if (state->flags & SS_RESET)
 131                GPSR = reset;
 132        else
 133                GPCR = reset;
 134
 135        return 0;
 136}
 137
 138static void nanoengine_pcmcia_socket_state(
 139        struct soc_pcmcia_socket *skt, struct pcmcia_state *state)
 140{
 141        unsigned long levels = GPLR;
 142        unsigned i = skt->nr;
 143
 144        if (i >= num_nano_pcmcia_sockets)
 145                return;
 146
 147        memset(state, 0, sizeof(struct pcmcia_state));
 148        switch (i) {
 149        case 0:
 150                state->ready = (levels & GPIO_PC_READY0) ? 1 : 0;
 151                state->detect = !(levels & GPIO_PC_CD0) ? 1 : 0;
 152                break;
 153        case 1:
 154                state->ready = (levels & GPIO_PC_READY1) ? 1 : 0;
 155                state->detect = !(levels & GPIO_PC_CD1) ? 1 : 0;
 156                break;
 157        default:
 158                return;
 159        }
 160        state->bvd1 = 1;
 161        state->bvd2 = 1;
 162        state->wrprot = 0; /* Not available */
 163        state->vs_3v = 1; /* Can only apply 3.3V */
 164        state->vs_Xv = 0;
 165}
 166
 167/*
 168 * Enable card status IRQs on (re-)initialisation.  This can
 169 * be called at initialisation, power management event, or
 170 * pcmcia event.
 171 */
 172static void nanoengine_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
 173{
 174        unsigned i = skt->nr;
 175
 176        if (i >= num_nano_pcmcia_sockets)
 177                return;
 178
 179        soc_pcmcia_enable_irqs(skt,
 180                nano_skts[i].pcmcia_irqs, nano_skts[i].pcmcia_irqs_size);
 181}
 182
 183/*
 184 * Disable card status IRQs on suspend.
 185 */
 186static void nanoengine_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
 187{
 188        unsigned i = skt->nr;
 189
 190        if (i >= num_nano_pcmcia_sockets)
 191                return;
 192
 193        soc_pcmcia_disable_irqs(skt,
 194                nano_skts[i].pcmcia_irqs, nano_skts[i].pcmcia_irqs_size);
 195}
 196
 197static struct pcmcia_low_level nanoengine_pcmcia_ops = {
 198        .owner                  = THIS_MODULE,
 199
 200        .hw_init                = nanoengine_pcmcia_hw_init,
 201        .hw_shutdown            = nanoengine_pcmcia_hw_shutdown,
 202
 203        .configure_socket       = nanoengine_pcmcia_configure_socket,
 204        .socket_state           = nanoengine_pcmcia_socket_state,
 205        .socket_init            = nanoengine_pcmcia_socket_init,
 206        .socket_suspend         = nanoengine_pcmcia_socket_suspend,
 207};
 208
 209int pcmcia_nanoengine_init(struct device *dev)
 210{
 211        int ret = -ENODEV;
 212
 213        if (machine_is_nanoengine())
 214                ret = sa11xx_drv_pcmcia_probe(
 215                        dev, &nanoengine_pcmcia_ops, 0, 2);
 216
 217        return ret;
 218}
 219
 220