Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[PATCH] pcmcia: OMAP CF controller

This adds a socket driver for the OMAP CF controller; it's currently in use on
OSK boards.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Dominik Brodowski <linux@dominikbrodowski.net>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

authored by

David Brownell and committed by
Linus Torvalds
f74e48a5 71ed90d8

+381
+7
drivers/pcmcia/Kconfig
··· 221 221 tristate "NEC VRC4173 CARDU support" 222 222 depends on CPU_VR41XX && PCI && PCMCIA 223 223 224 + config OMAP_CF 225 + tristate "OMAP CompactFlash Controller" 226 + depends on PCMCIA && ARCH_OMAP16XX 227 + help 228 + Say Y here to support the CompactFlash controller on OMAP. 229 + Note that this doesn't support "True IDE" mode. 230 + 224 231 config PCCARD_NONSTATIC 225 232 tristate 226 233
+1
drivers/pcmcia/Makefile
··· 34 34 obj-$(CONFIG_PCMCIA_AU1X00) += au1x00_ss.o 35 35 obj-$(CONFIG_PCMCIA_VRC4171) += vrc4171_card.o 36 36 obj-$(CONFIG_PCMCIA_VRC4173) += vrc4173_cardu.o 37 + obj-$(CONFIG_OMAP_CF) += omap_cf.o 37 38 38 39 sa11xx_core-y += soc_common.o sa11xx_base.o 39 40 pxa2xx_core-y += soc_common.o pxa2xx_base.o
+373
drivers/pcmcia/omap_cf.c
··· 1 + /* 2 + * omap_cf.c -- OMAP 16xx CompactFlash controller driver 3 + * 4 + * Copyright (c) 2005 David Brownell 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License as published by 8 + * the Free Software Foundation; either version 2 of the License, or 9 + * (at your option) any later version. 10 + */ 11 + 12 + #include <linux/module.h> 13 + #include <linux/kernel.h> 14 + #include <linux/sched.h> 15 + #include <linux/device.h> 16 + #include <linux/errno.h> 17 + #include <linux/init.h> 18 + #include <linux/delay.h> 19 + #include <linux/interrupt.h> 20 + 21 + #include <pcmcia/ss.h> 22 + 23 + #include <asm/hardware.h> 24 + #include <asm/io.h> 25 + #include <asm/mach-types.h> 26 + #include <asm/sizes.h> 27 + 28 + #include <asm/arch/mux.h> 29 + #include <asm/arch/tc.h> 30 + 31 + 32 + /* NOTE: don't expect this to support many I/O cards. The 16xx chips have 33 + * hard-wired timings to support Compact Flash memory cards; they won't work 34 + * with various other devices (like WLAN adapters) without some external 35 + * logic to help out. 36 + * 37 + * NOTE: CF controller docs disagree with address space docs as to where 38 + * CF_BASE really lives; this is a doc erratum. 39 + */ 40 + #define CF_BASE 0xfffe2800 41 + 42 + /* status; read after IRQ */ 43 + #define CF_STATUS_REG __REG16(CF_BASE + 0x00) 44 + # define CF_STATUS_BAD_READ (1 << 2) 45 + # define CF_STATUS_BAD_WRITE (1 << 1) 46 + # define CF_STATUS_CARD_DETECT (1 << 0) 47 + 48 + /* which chipselect (CS0..CS3) is used for CF (active low) */ 49 + #define CF_CFG_REG __REG16(CF_BASE + 0x02) 50 + 51 + /* card reset */ 52 + #define CF_CONTROL_REG __REG16(CF_BASE + 0x04) 53 + # define CF_CONTROL_RESET (1 << 0) 54 + 55 + #define omap_cf_present() (!(CF_STATUS_REG & CF_STATUS_CARD_DETECT)) 56 + 57 + /*--------------------------------------------------------------------------*/ 58 + 59 + static const char driver_name[] = "omap_cf"; 60 + 61 + struct omap_cf_socket { 62 + struct pcmcia_socket socket; 63 + 64 + struct timer_list timer; 65 + unsigned present:1; 66 + unsigned active:1; 67 + 68 + struct platform_device *pdev; 69 + unsigned long phys_cf; 70 + u_int irq; 71 + }; 72 + 73 + #define POLL_INTERVAL (2 * HZ) 74 + 75 + #define SZ_2K (2 * SZ_1K) 76 + 77 + /*--------------------------------------------------------------------------*/ 78 + 79 + static int omap_cf_ss_init(struct pcmcia_socket *s) 80 + { 81 + return 0; 82 + } 83 + 84 + /* the timer is primarily to kick this socket's pccardd */ 85 + static void omap_cf_timer(unsigned long _cf) 86 + { 87 + struct omap_cf_socket *cf = (void *) _cf; 88 + unsigned present = omap_cf_present(); 89 + 90 + if (present != cf->present) { 91 + cf->present = present; 92 + pr_debug("%s: card %s\n", driver_name, 93 + present ? "present" : "gone"); 94 + pcmcia_parse_events(&cf->socket, SS_DETECT); 95 + } 96 + 97 + if (cf->active) 98 + mod_timer(&cf->timer, jiffies + POLL_INTERVAL); 99 + } 100 + 101 + /* This irq handler prevents "irqNNN: nobody cared" messages as drivers 102 + * claim the card's IRQ. It may also detect some card insertions, but 103 + * not removals; it can't always eliminate timer irqs. 104 + */ 105 + static irqreturn_t omap_cf_irq(int irq, void *_cf, struct pt_regs *r) 106 + { 107 + omap_cf_timer((unsigned long)_cf); 108 + return IRQ_HANDLED; 109 + } 110 + 111 + static int omap_cf_get_status(struct pcmcia_socket *s, u_int *sp) 112 + { 113 + if (!sp) 114 + return -EINVAL; 115 + 116 + /* FIXME power management should probably be board-specific: 117 + * - 3VCARD vs XVCARD (OSK only handles 3VCARD) 118 + * - POWERON (switched on/off by set_socket) 119 + */ 120 + if (omap_cf_present()) { 121 + struct omap_cf_socket *cf; 122 + 123 + *sp = SS_READY | SS_DETECT | SS_POWERON | SS_3VCARD; 124 + cf = container_of(s, struct omap_cf_socket, socket); 125 + s->irq.AssignedIRQ = cf->irq; 126 + } else 127 + *sp = 0; 128 + return 0; 129 + } 130 + 131 + static int 132 + omap_cf_set_socket(struct pcmcia_socket *sock, struct socket_state_t *s) 133 + { 134 + u16 control; 135 + 136 + /* FIXME some non-OSK boards will support power switching */ 137 + switch (s->Vcc) { 138 + case 0: 139 + case 33: 140 + break; 141 + default: 142 + return -EINVAL; 143 + } 144 + 145 + control = CF_CONTROL_REG; 146 + if (s->flags & SS_RESET) 147 + CF_CONTROL_REG = CF_CONTROL_RESET; 148 + else 149 + CF_CONTROL_REG = 0; 150 + 151 + pr_debug("%s: Vcc %d, io_irq %d, flags %04x csc %04x\n", 152 + driver_name, s->Vcc, s->io_irq, s->flags, s->csc_mask); 153 + 154 + return 0; 155 + } 156 + 157 + static int omap_cf_ss_suspend(struct pcmcia_socket *s) 158 + { 159 + pr_debug("%s: %s\n", driver_name, __FUNCTION__); 160 + return omap_cf_set_socket(s, &dead_socket); 161 + } 162 + 163 + /* regions are 2K each: mem, attrib, io (and reserved-for-ide) */ 164 + 165 + static int 166 + omap_cf_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io) 167 + { 168 + struct omap_cf_socket *cf; 169 + 170 + cf = container_of(s, struct omap_cf_socket, socket); 171 + io->flags &= MAP_ACTIVE|MAP_ATTRIB|MAP_16BIT; 172 + io->start = cf->phys_cf + SZ_4K; 173 + io->stop = io->start + SZ_2K - 1; 174 + return 0; 175 + } 176 + 177 + static int 178 + omap_cf_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *map) 179 + { 180 + struct omap_cf_socket *cf; 181 + 182 + if (map->card_start) 183 + return -EINVAL; 184 + cf = container_of(s, struct omap_cf_socket, socket); 185 + map->static_start = cf->phys_cf; 186 + map->flags &= MAP_ACTIVE|MAP_ATTRIB|MAP_16BIT; 187 + if (map->flags & MAP_ATTRIB) 188 + map->static_start += SZ_2K; 189 + return 0; 190 + } 191 + 192 + static struct pccard_operations omap_cf_ops = { 193 + .init = omap_cf_ss_init, 194 + .suspend = omap_cf_ss_suspend, 195 + .get_status = omap_cf_get_status, 196 + .set_socket = omap_cf_set_socket, 197 + .set_io_map = omap_cf_set_io_map, 198 + .set_mem_map = omap_cf_set_mem_map, 199 + }; 200 + 201 + /*--------------------------------------------------------------------------*/ 202 + 203 + /* 204 + * NOTE: right now the only board-specific platform_data is 205 + * "what chipselect is used". Boards could want more. 206 + */ 207 + 208 + static int __init omap_cf_probe(struct device *dev) 209 + { 210 + unsigned seg; 211 + struct omap_cf_socket *cf; 212 + struct platform_device *pdev = to_platform_device(dev); 213 + int irq; 214 + int status; 215 + 216 + seg = (int) dev->platform_data; 217 + if (seg == 0 || seg > 3) 218 + return -ENODEV; 219 + 220 + /* either CFLASH.IREQ (INT_1610_CF) or some GPIO */ 221 + irq = platform_get_irq(pdev, 0); 222 + if (!irq) 223 + return -EINVAL; 224 + 225 + cf = kcalloc(1, sizeof *cf, GFP_KERNEL); 226 + if (!cf) 227 + return -ENOMEM; 228 + init_timer(&cf->timer); 229 + cf->timer.function = omap_cf_timer; 230 + cf->timer.data = (unsigned long) cf; 231 + 232 + cf->pdev = pdev; 233 + dev_set_drvdata(dev, cf); 234 + 235 + /* this primarily just shuts up irq handling noise */ 236 + status = request_irq(irq, omap_cf_irq, SA_SHIRQ, 237 + driver_name, cf); 238 + if (status < 0) 239 + goto fail0; 240 + cf->irq = irq; 241 + cf->socket.pci_irq = irq; 242 + 243 + switch (seg) { 244 + /* NOTE: CS0 could be configured too ... */ 245 + case 1: 246 + cf->phys_cf = OMAP_CS1_PHYS; 247 + break; 248 + case 2: 249 + cf->phys_cf = OMAP_CS2_PHYS; 250 + break; 251 + case 3: 252 + cf->phys_cf = omap_cs3_phys(); 253 + break; 254 + default: 255 + goto fail1; 256 + } 257 + 258 + /* pcmcia layer only remaps "real" memory */ 259 + cf->socket.io_offset = (unsigned long) 260 + ioremap(cf->phys_cf + SZ_4K, SZ_2K); 261 + if (!cf->socket.io_offset) 262 + goto fail1; 263 + 264 + if (!request_mem_region(cf->phys_cf, SZ_8K, driver_name)) 265 + goto fail1; 266 + 267 + /* NOTE: CF conflicts with MMC1 */ 268 + omap_cfg_reg(W11_1610_CF_CD1); 269 + omap_cfg_reg(P11_1610_CF_CD2); 270 + omap_cfg_reg(R11_1610_CF_IOIS16); 271 + omap_cfg_reg(V10_1610_CF_IREQ); 272 + omap_cfg_reg(W10_1610_CF_RESET); 273 + 274 + CF_CFG_REG = ~(1 << seg); 275 + 276 + pr_info("%s: cs%d on irq %d\n", driver_name, seg, irq); 277 + 278 + /* NOTE: better EMIFS setup might support more cards; but the 279 + * TRM only shows how to affect regular flash signals, not their 280 + * CF/PCMCIA variants... 281 + */ 282 + pr_debug("%s: cs%d, previous ccs %08x acs %08x\n", driver_name, 283 + seg, EMIFS_CCS(seg), EMIFS_ACS(seg)); 284 + EMIFS_CCS(seg) = 0x0004a1b3; /* synch mode 4 etc */ 285 + EMIFS_ACS(seg) = 0x00000000; /* OE hold/setup */ 286 + 287 + /* CF uses armxor_ck, which is "always" available */ 288 + 289 + pr_debug("%s: sts %04x cfg %04x control %04x %s\n", driver_name, 290 + CF_STATUS_REG, CF_CFG_REG, CF_CONTROL_REG, 291 + omap_cf_present() ? "present" : "(not present)"); 292 + 293 + cf->socket.owner = THIS_MODULE; 294 + cf->socket.dev.dev = dev; 295 + cf->socket.ops = &omap_cf_ops; 296 + cf->socket.resource_ops = &pccard_static_ops; 297 + cf->socket.features = SS_CAP_PCCARD | SS_CAP_STATIC_MAP 298 + | SS_CAP_MEM_ALIGN; 299 + cf->socket.map_size = SZ_2K; 300 + 301 + status = pcmcia_register_socket(&cf->socket); 302 + if (status < 0) 303 + goto fail2; 304 + 305 + cf->active = 1; 306 + mod_timer(&cf->timer, jiffies + POLL_INTERVAL); 307 + return 0; 308 + 309 + fail2: 310 + iounmap((void __iomem *) cf->socket.io_offset); 311 + release_mem_region(cf->phys_cf, SZ_8K); 312 + fail1: 313 + free_irq(irq, cf); 314 + fail0: 315 + kfree(cf); 316 + return status; 317 + } 318 + 319 + static int __devexit omap_cf_remove(struct device *dev) 320 + { 321 + struct omap_cf_socket *cf = dev_get_drvdata(dev); 322 + 323 + cf->active = 0; 324 + pcmcia_unregister_socket(&cf->socket); 325 + del_timer_sync(&cf->timer); 326 + iounmap((void __iomem *) cf->socket.io_offset); 327 + release_mem_region(cf->phys_cf, SZ_8K); 328 + free_irq(cf->irq, cf); 329 + kfree(cf); 330 + return 0; 331 + } 332 + 333 + static int omap_cf_suspend(struct device *dev, pm_message_t mesg, u32 level) 334 + { 335 + if (level != SUSPEND_SAVE_STATE) 336 + return 0; 337 + return pcmcia_socket_dev_suspend(dev, mesg); 338 + } 339 + 340 + static int omap_cf_resume(struct device *dev, u32 level) 341 + { 342 + if (level != RESUME_RESTORE_STATE) 343 + return 0; 344 + return pcmcia_socket_dev_resume(dev); 345 + } 346 + 347 + static struct device_driver omap_cf_driver = { 348 + .name = (char *) driver_name, 349 + .bus = &platform_bus_type, 350 + .probe = omap_cf_probe, 351 + .remove = __devexit_p(omap_cf_remove), 352 + .suspend = omap_cf_suspend, 353 + .resume = omap_cf_resume, 354 + }; 355 + 356 + static int __init omap_cf_init(void) 357 + { 358 + if (cpu_is_omap16xx()) 359 + driver_register(&omap_cf_driver); 360 + return 0; 361 + } 362 + 363 + static void __exit omap_cf_exit(void) 364 + { 365 + if (cpu_is_omap16xx()) 366 + driver_unregister(&omap_cf_driver); 367 + } 368 + 369 + module_init(omap_cf_init); 370 + module_exit(omap_cf_exit); 371 + 372 + MODULE_DESCRIPTION("OMAP CF Driver"); 373 + MODULE_LICENSE("GPL");