linux/drivers/pcmcia/bfin_cf_pcmcia.c
<<
>>
Prefs
   1/*
   2 * file: drivers/pcmcia/bfin_cf.c
   3 *
   4 * based on: drivers/pcmcia/omap_cf.c
   5 * omap_cf.c -- OMAP 16xx CompactFlash controller driver
   6 *
   7 * Copyright (c) 2005 David Brownell
   8 * Copyright (c) 2006-2008 Michael Hennerich Analog Devices Inc.
   9 *
  10 * bugs:         enter bugs at http://blackfin.uclinux.org/
  11 *
  12 * this program is free software; you can redistribute it and/or modify
  13 * it under the terms of the gnu general public license as published by
  14 * the free software foundation; either version 2, or (at your option)
  15 * any later version.
  16 *
  17 * this program is distributed in the hope that it will be useful,
  18 * but without any warranty; without even the implied warranty of
  19 * merchantability or fitness for a particular purpose.  see the
  20 * gnu general public license for more details.
  21 *
  22 * you should have received a copy of the gnu general public license
  23 * along with this program; see the file copying.
  24 * if not, write to the free software foundation,
  25 * 59 temple place - suite 330, boston, ma 02111-1307, usa.
  26 */
  27
  28#include <linux/module.h>
  29#include <linux/kernel.h>
  30#include <linux/sched.h>
  31#include <linux/platform_device.h>
  32#include <linux/errno.h>
  33#include <linux/init.h>
  34#include <linux/slab.h>
  35#include <linux/delay.h>
  36#include <linux/interrupt.h>
  37#include <linux/irq.h>
  38#include <linux/io.h>
  39
  40#include <pcmcia/ss.h>
  41#include <pcmcia/cisreg.h>
  42#include <asm/gpio.h>
  43
  44#define SZ_1K   0x00000400
  45#define SZ_8K   0x00002000
  46#define SZ_2K   (2 * SZ_1K)
  47
  48#define POLL_INTERVAL   (2 * HZ)
  49
  50#define CF_ATASEL_ENA   0x20311802      /* Inverts RESET */
  51#define CF_ATASEL_DIS   0x20311800
  52
  53#define bfin_cf_present(pfx) (gpio_get_value(pfx))
  54
  55/*--------------------------------------------------------------------------*/
  56
  57static const char driver_name[] = "bfin_cf_pcmcia";
  58
  59struct bfin_cf_socket {
  60        struct pcmcia_socket socket;
  61
  62        struct timer_list timer;
  63        unsigned present:1;
  64        unsigned active:1;
  65
  66        struct platform_device *pdev;
  67        unsigned long phys_cf_io;
  68        unsigned long phys_cf_attr;
  69        u_int irq;
  70        u_short cd_pfx;
  71};
  72
  73/*--------------------------------------------------------------------------*/
  74static int bfin_cf_reset(void)
  75{
  76        outw(0, CF_ATASEL_ENA);
  77        mdelay(200);
  78        outw(0, CF_ATASEL_DIS);
  79
  80        return 0;
  81}
  82
  83static int bfin_cf_ss_init(struct pcmcia_socket *s)
  84{
  85        return 0;
  86}
  87
  88/* the timer is primarily to kick this socket's pccardd */
  89static void bfin_cf_timer(unsigned long _cf)
  90{
  91        struct bfin_cf_socket *cf = (void *)_cf;
  92        unsigned short present = bfin_cf_present(cf->cd_pfx);
  93
  94        if (present != cf->present) {
  95                cf->present = present;
  96                dev_dbg(&cf->pdev->dev, ": card %s\n",
  97                         present ? "present" : "gone");
  98                pcmcia_parse_events(&cf->socket, SS_DETECT);
  99        }
 100
 101        if (cf->active)
 102                mod_timer(&cf->timer, jiffies + POLL_INTERVAL);
 103}
 104
 105static int bfin_cf_get_status(struct pcmcia_socket *s, u_int *sp)
 106{
 107        struct bfin_cf_socket *cf;
 108
 109        if (!sp)
 110                return -EINVAL;
 111
 112        cf = container_of(s, struct bfin_cf_socket, socket);
 113
 114        if (bfin_cf_present(cf->cd_pfx)) {
 115                *sp = SS_READY | SS_DETECT | SS_POWERON | SS_3VCARD;
 116                s->pcmcia_irq = 0;
 117                s->pci_irq = cf->irq;
 118
 119        } else
 120                *sp = 0;
 121        return 0;
 122}
 123
 124static int
 125bfin_cf_set_socket(struct pcmcia_socket *sock, struct socket_state_t *s)
 126{
 127
 128        struct bfin_cf_socket *cf;
 129        cf = container_of(sock, struct bfin_cf_socket, socket);
 130
 131        switch (s->Vcc) {
 132        case 0:
 133        case 33:
 134                break;
 135        case 50:
 136                break;
 137        default:
 138                return -EINVAL;
 139        }
 140
 141        if (s->flags & SS_RESET) {
 142                disable_irq(cf->irq);
 143                bfin_cf_reset();
 144                enable_irq(cf->irq);
 145        }
 146
 147        dev_dbg(&cf->pdev->dev, ": Vcc %d, io_irq %d, flags %04x csc %04x\n",
 148                 s->Vcc, s->io_irq, s->flags, s->csc_mask);
 149
 150        return 0;
 151}
 152
 153static int bfin_cf_ss_suspend(struct pcmcia_socket *s)
 154{
 155        return bfin_cf_set_socket(s, &dead_socket);
 156}
 157
 158/* regions are 2K each:  mem, attrib, io (and reserved-for-ide) */
 159
 160static int bfin_cf_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io)
 161{
 162        struct bfin_cf_socket *cf;
 163
 164        cf = container_of(s, struct bfin_cf_socket, socket);
 165        io->flags &= MAP_ACTIVE | MAP_ATTRIB | MAP_16BIT;
 166        io->start = cf->phys_cf_io;
 167        io->stop = io->start + SZ_2K - 1;
 168        return 0;
 169}
 170
 171static int
 172bfin_cf_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *map)
 173{
 174        struct bfin_cf_socket *cf;
 175
 176        if (map->card_start)
 177                return -EINVAL;
 178        cf = container_of(s, struct bfin_cf_socket, socket);
 179        map->static_start = cf->phys_cf_io;
 180        map->flags &= MAP_ACTIVE | MAP_ATTRIB | MAP_16BIT;
 181        if (map->flags & MAP_ATTRIB)
 182                map->static_start = cf->phys_cf_attr;
 183
 184        return 0;
 185}
 186
 187static struct pccard_operations bfin_cf_ops = {
 188        .init = bfin_cf_ss_init,
 189        .suspend = bfin_cf_ss_suspend,
 190        .get_status = bfin_cf_get_status,
 191        .set_socket = bfin_cf_set_socket,
 192        .set_io_map = bfin_cf_set_io_map,
 193        .set_mem_map = bfin_cf_set_mem_map,
 194};
 195
 196/*--------------------------------------------------------------------------*/
 197
 198static int __devinit bfin_cf_probe(struct platform_device *pdev)
 199{
 200        struct bfin_cf_socket *cf;
 201        struct resource *io_mem, *attr_mem;
 202        int irq;
 203        unsigned short cd_pfx;
 204        int status = 0;
 205
 206        dev_info(&pdev->dev, "Blackfin CompactFlash/PCMCIA Socket Driver\n");
 207
 208        irq = platform_get_irq(pdev, 0);
 209        if (irq <= 0)
 210                return -EINVAL;
 211
 212        cd_pfx = platform_get_irq(pdev, 1);     /*Card Detect GPIO PIN */
 213
 214        if (gpio_request(cd_pfx, "pcmcia: CD")) {
 215                dev_err(&pdev->dev,
 216                       "Failed ro request Card Detect GPIO_%d\n",
 217                       cd_pfx);
 218                return -EBUSY;
 219        }
 220        gpio_direction_input(cd_pfx);
 221
 222        cf = kzalloc(sizeof *cf, GFP_KERNEL);
 223        if (!cf) {
 224                gpio_free(cd_pfx);
 225                return -ENOMEM;
 226        }
 227
 228        cf->cd_pfx = cd_pfx;
 229
 230        setup_timer(&cf->timer, bfin_cf_timer, (unsigned long)cf);
 231
 232        cf->pdev = pdev;
 233        platform_set_drvdata(pdev, cf);
 234
 235        cf->irq = irq;
 236        cf->socket.pci_irq = irq;
 237
 238        irq_set_irq_type(irq, IRQF_TRIGGER_LOW);
 239
 240        io_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 241        attr_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 242
 243        if (!io_mem || !attr_mem)
 244                goto fail0;
 245
 246        cf->phys_cf_io = io_mem->start;
 247        cf->phys_cf_attr = attr_mem->start;
 248
 249        /* pcmcia layer only remaps "real" memory */
 250        cf->socket.io_offset = (unsigned long)
 251            ioremap(cf->phys_cf_io, SZ_2K);
 252
 253        if (!cf->socket.io_offset)
 254                goto fail0;
 255
 256        dev_err(&pdev->dev, ": on irq %d\n", irq);
 257
 258        dev_dbg(&pdev->dev, ": %s\n",
 259                 bfin_cf_present(cf->cd_pfx) ? "present" : "(not present)");
 260
 261        cf->socket.owner = THIS_MODULE;
 262        cf->socket.dev.parent = &pdev->dev;
 263        cf->socket.ops = &bfin_cf_ops;
 264        cf->socket.resource_ops = &pccard_static_ops;
 265        cf->socket.features = SS_CAP_PCCARD | SS_CAP_STATIC_MAP
 266            | SS_CAP_MEM_ALIGN;
 267        cf->socket.map_size = SZ_2K;
 268
 269        status = pcmcia_register_socket(&cf->socket);
 270        if (status < 0)
 271                goto fail2;
 272
 273        cf->active = 1;
 274        mod_timer(&cf->timer, jiffies + POLL_INTERVAL);
 275        return 0;
 276
 277fail2:
 278        iounmap((void __iomem *)cf->socket.io_offset);
 279        release_mem_region(cf->phys_cf_io, SZ_8K);
 280
 281fail0:
 282        gpio_free(cf->cd_pfx);
 283        kfree(cf);
 284        platform_set_drvdata(pdev, NULL);
 285
 286        return status;
 287}
 288
 289static int __devexit bfin_cf_remove(struct platform_device *pdev)
 290{
 291        struct bfin_cf_socket *cf = platform_get_drvdata(pdev);
 292
 293        gpio_free(cf->cd_pfx);
 294        cf->active = 0;
 295        pcmcia_unregister_socket(&cf->socket);
 296        del_timer_sync(&cf->timer);
 297        iounmap((void __iomem *)cf->socket.io_offset);
 298        release_mem_region(cf->phys_cf_io, SZ_8K);
 299        platform_set_drvdata(pdev, NULL);
 300        kfree(cf);
 301        return 0;
 302}
 303
 304static struct platform_driver bfin_cf_driver = {
 305        .driver = {
 306                   .name = (char *)driver_name,
 307                   .owner = THIS_MODULE,
 308                   },
 309        .probe = bfin_cf_probe,
 310        .remove = __devexit_p(bfin_cf_remove),
 311};
 312
 313module_platform_driver(bfin_cf_driver);
 314
 315MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
 316MODULE_DESCRIPTION("BFIN CF/PCMCIA Driver");
 317MODULE_LICENSE("GPL");
 318
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.