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/delay.h>
  35#include <linux/interrupt.h>
  36#include <linux/irq.h>
  37#include <linux/io.h>
  38
  39#include <pcmcia/ss.h>
  40#include <pcmcia/cisreg.h>
  41#include <asm/gpio.h>
  42
  43#define SZ_1K   0x00000400
  44#define SZ_8K   0x00002000
  45#define SZ_2K   (2 * SZ_1K)
  46
  47#define POLL_INTERVAL   (2 * HZ)
  48
  49#define CF_ATASEL_ENA   0x20311802      /* Inverts RESET */
  50#define CF_ATASEL_DIS   0x20311800
  51
  52#define bfin_cf_present(pfx) (gpio_get_value(pfx))
  53
  54/*--------------------------------------------------------------------------*/
  55
  56static const char driver_name[] = "bfin_cf_pcmcia";
  57
  58struct bfin_cf_socket {
  59        struct pcmcia_socket socket;
  60
  61        struct timer_list timer;
  62        unsigned present:1;
  63        unsigned active:1;
  64
  65        struct platform_device *pdev;
  66        unsigned long phys_cf_io;
  67        unsigned long phys_cf_attr;
  68        u_int irq;
  69        u_short cd_pfx;
  70};
  71
  72/*--------------------------------------------------------------------------*/
  73static int bfin_cf_reset(void)
  74{
  75        outw(0, CF_ATASEL_ENA);
  76        mdelay(200);
  77        outw(0, CF_ATASEL_DIS);
  78
  79        return 0;
  80}
  81
  82static int bfin_cf_ss_init(struct pcmcia_socket *s)
  83{
  84        return 0;
  85}
  86
  87/* the timer is primarily to kick this socket's pccardd */
  88static void bfin_cf_timer(unsigned long _cf)
  89{
  90        struct bfin_cf_socket *cf = (void *)_cf;
  91        unsigned short present = bfin_cf_present(cf->cd_pfx);
  92
  93        if (present != cf->present) {
  94                cf->present = present;
  95                dev_dbg(&cf->pdev->dev, ": card %s\n",
  96                         present ? "present" : "gone");
  97                pcmcia_parse_events(&cf->socket, SS_DETECT);
  98        }
  99
 100        if (cf->active)
 101                mod_timer(&cf->timer, jiffies + POLL_INTERVAL);
 102}
 103
 104static int bfin_cf_get_status(struct pcmcia_socket *s, u_int *sp)
 105{
 106        struct bfin_cf_socket *cf;
 107
 108        if (!sp)
 109                return -EINVAL;
 110
 111        cf = container_of(s, struct bfin_cf_socket, socket);
 112
 113        if (bfin_cf_present(cf->cd_pfx)) {
 114                *sp = SS_READY | SS_DETECT | SS_POWERON | SS_3VCARD;
 115                s->irq.AssignedIRQ = 0;
 116                s->pci_irq = cf->irq;
 117
 118        } else
 119                *sp = 0;
 120        return 0;
 121}
 122
 123static int
 124bfin_cf_set_socket(struct pcmcia_socket *sock, struct socket_state_t *s)
 125{
 126
 127        struct bfin_cf_socket *cf;
 128        cf = container_of(sock, struct bfin_cf_socket, socket);
 129
 130        switch (s->Vcc) {
 131        case 0:
 132        case 33:
 133                break;
 134        case 50:
 135                break;
 136        default:
 137                return -EINVAL;
 138        }
 139
 140        if (s->flags & SS_RESET) {
 141                disable_irq(cf->irq);
 142                bfin_cf_reset();
 143                enable_irq(cf->irq);
 144        }
 145
 146        dev_dbg(&cf->pdev->dev, ": Vcc %d, io_irq %d, flags %04x csc %04x\n",
 147                 s->Vcc, s->io_irq, s->flags, s->csc_mask);
 148
 149        return 0;
 150}
 151
 152static int bfin_cf_ss_suspend(struct pcmcia_socket *s)
 153{
 154        return bfin_cf_set_socket(s, &dead_socket);
 155}
 156
 157/* regions are 2K each:  mem, attrib, io (and reserved-for-ide) */
 158
 159static int bfin_cf_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io)
 160{
 161        struct bfin_cf_socket *cf;
 162
 163        cf = container_of(s, struct bfin_cf_socket, socket);
 164        io->flags &= MAP_ACTIVE | MAP_ATTRIB | MAP_16BIT;
 165        io->start = cf->phys_cf_io;
 166        io->stop = io->start + SZ_2K - 1;
 167        return 0;
 168}
 169
 170static int
 171bfin_cf_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *map)
 172{
 173        struct bfin_cf_socket *cf;
 174
 175        if (map->card_start)
 176                return -EINVAL;
 177        cf = container_of(s, struct bfin_cf_socket, socket);
 178        map->static_start = cf->phys_cf_io;
 179        map->flags &= MAP_ACTIVE | MAP_ATTRIB | MAP_16BIT;
 180        if (map->flags & MAP_ATTRIB)
 181                map->static_start = cf->phys_cf_attr;
 182
 183        return 0;
 184}
 185
 186static struct pccard_operations bfin_cf_ops = {
 187        .init = bfin_cf_ss_init,
 188        .suspend = bfin_cf_ss_suspend,
 189        .get_status = bfin_cf_get_status,
 190        .set_socket = bfin_cf_set_socket,
 191        .set_io_map = bfin_cf_set_io_map,
 192        .set_mem_map = bfin_cf_set_mem_map,
 193};
 194
 195/*--------------------------------------------------------------------------*/
 196
 197static int __devinit bfin_cf_probe(struct platform_device *pdev)
 198{
 199        struct bfin_cf_socket *cf;
 200        struct resource *io_mem, *attr_mem;
 201        int irq;
 202        unsigned short cd_pfx;
 203        int status = 0;
 204
 205        dev_info(&pdev->dev, "Blackfin CompactFlash/PCMCIA Socket Driver\n");
 206
 207        irq = platform_get_irq(pdev, 0);
 208        if (!irq)
 209                return -EINVAL;
 210
 211        cd_pfx = platform_get_irq(pdev, 1);     /*Card Detect GPIO PIN */
 212
 213        if (gpio_request(cd_pfx, "pcmcia: CD")) {
 214                dev_err(&pdev->dev,
 215                       "Failed ro request Card Detect GPIO_%d\n",
 216                       cd_pfx);
 217                return -EBUSY;
 218        }
 219        gpio_direction_input(cd_pfx);
 220
 221        cf = kzalloc(sizeof *cf, GFP_KERNEL);
 222        if (!cf) {
 223                gpio_free(cd_pfx);
 224                return -ENOMEM;
 225        }
 226
 227        cf->cd_pfx = cd_pfx;
 228
 229        setup_timer(&cf->timer, bfin_cf_timer, (unsigned long)cf);
 230
 231        cf->pdev = pdev;
 232        platform_set_drvdata(pdev, cf);
 233
 234        cf->irq = irq;
 235        cf->socket.pci_irq = irq;
 236
 237        set_irq_type(irq, IRQF_TRIGGER_LOW);
 238
 239        io_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 240        attr_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 241
 242        if (!io_mem || !attr_mem)
 243                goto fail0;
 244
 245        cf->phys_cf_io = io_mem->start;
 246        cf->phys_cf_attr = attr_mem->start;
 247
 248        /* pcmcia layer only remaps "real" memory */
 249        cf->socket.io_offset = (unsigned long)
 250            ioremap(cf->phys_cf_io, SZ_2K);
 251
 252        if (!cf->socket.io_offset)
 253                goto fail0;
 254
 255        dev_err(&pdev->dev, ": on irq %d\n", irq);
 256
 257        dev_dbg(&pdev->dev, ": %s\n",
 258                 bfin_cf_present(cf->cd_pfx) ? "present" : "(not present)");
 259
 260        cf->socket.owner = THIS_MODULE;
 261        cf->socket.dev.parent = &pdev->dev;
 262        cf->socket.ops = &bfin_cf_ops;
 263        cf->socket.resource_ops = &pccard_static_ops;
 264        cf->socket.features = SS_CAP_PCCARD | SS_CAP_STATIC_MAP
 265            | SS_CAP_MEM_ALIGN;
 266        cf->socket.map_size = SZ_2K;
 267
 268        status = pcmcia_register_socket(&cf->socket);
 269        if (status < 0)
 270                goto fail2;
 271
 272        cf->active = 1;
 273        mod_timer(&cf->timer, jiffies + POLL_INTERVAL);
 274        return 0;
 275
 276fail2:
 277        iounmap((void __iomem *)cf->socket.io_offset);
 278        release_mem_region(cf->phys_cf_io, SZ_8K);
 279
 280fail0:
 281        gpio_free(cf->cd_pfx);
 282        kfree(cf);
 283        platform_set_drvdata(pdev, NULL);
 284
 285        return status;
 286}
 287
 288static int __devexit bfin_cf_remove(struct platform_device *pdev)
 289{
 290        struct bfin_cf_socket *cf = platform_get_drvdata(pdev);
 291
 292        gpio_free(cf->cd_pfx);
 293        cf->active = 0;
 294        pcmcia_unregister_socket(&cf->socket);
 295        del_timer_sync(&cf->timer);
 296        iounmap((void __iomem *)cf->socket.io_offset);
 297        release_mem_region(cf->phys_cf_io, SZ_8K);
 298        platform_set_drvdata(pdev, NULL);
 299        kfree(cf);
 300        return 0;
 301}
 302
 303static int bfin_cf_suspend(struct platform_device *pdev, pm_message_t mesg)
 304{
 305        return pcmcia_socket_dev_suspend(&pdev->dev);
 306}
 307
 308static int bfin_cf_resume(struct platform_device *pdev)
 309{
 310        return pcmcia_socket_dev_resume(&pdev->dev);
 311}
 312
 313static struct platform_driver bfin_cf_driver = {
 314        .driver = {
 315                   .name = (char *)driver_name,
 316                   .owner = THIS_MODULE,
 317                   },
 318        .probe = bfin_cf_probe,
 319        .remove = __devexit_p(bfin_cf_remove),
 320        .suspend = bfin_cf_suspend,
 321        .resume = bfin_cf_resume,
 322};
 323
 324static int __init bfin_cf_init(void)
 325{
 326        return platform_driver_register(&bfin_cf_driver);
 327}
 328
 329static void __exit bfin_cf_exit(void)
 330{
 331        platform_driver_unregister(&bfin_cf_driver);
 332}
 333
 334module_init(bfin_cf_init);
 335module_exit(bfin_cf_exit);
 336
 337MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
 338MODULE_DESCRIPTION("BFIN CF/PCMCIA Driver");
 339MODULE_LICENSE("GPL");
 340