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 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 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 = driver_name, 307 }, 308 .probe = bfin_cf_probe, 309 .remove = bfin_cf_remove, 310}; 311 312module_platform_driver(bfin_cf_driver); 313 314MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); 315MODULE_DESCRIPTION("BFIN CF/PCMCIA Driver"); 316MODULE_LICENSE("GPL"); 317