diff options
| author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
|---|---|---|
| committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
| commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
| tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/pcmcia | |
| parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) | |
Diffstat (limited to 'drivers/pcmcia')
| -rw-r--r-- | drivers/pcmcia/au1000_generic.c | 545 | ||||
| -rw-r--r-- | drivers/pcmcia/au1000_generic.h | 135 | ||||
| -rw-r--r-- | drivers/pcmcia/au1000_pb1x00.c | 294 | ||||
| -rw-r--r-- | drivers/pcmcia/pxa2xx_lubbock.c | 237 | ||||
| -rw-r--r-- | drivers/pcmcia/sa1100_badge4.c | 167 | ||||
| -rw-r--r-- | drivers/pcmcia/sa1100_jornada720.c | 120 | ||||
| -rw-r--r-- | drivers/pcmcia/sa1100_neponset.c | 143 |
7 files changed, 1641 insertions, 0 deletions
diff --git a/drivers/pcmcia/au1000_generic.c b/drivers/pcmcia/au1000_generic.c new file mode 100644 index 00000000000..95dd7c62741 --- /dev/null +++ b/drivers/pcmcia/au1000_generic.c | |||
| @@ -0,0 +1,545 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * Alchemy Semi Au1000 pcmcia driver | ||
| 4 | * | ||
| 5 | * Copyright 2001-2003 MontaVista Software Inc. | ||
| 6 | * Author: MontaVista Software, Inc. | ||
| 7 | * ppopov@embeddedalley.com or source@mvista.com | ||
| 8 | * | ||
| 9 | * Copyright 2004 Pete Popov, Embedded Alley Solutions, Inc. | ||
| 10 | * Updated the driver to 2.6. Followed the sa11xx API and largely | ||
| 11 | * copied many of the hardware independent functions. | ||
| 12 | * | ||
| 13 | * ######################################################################## | ||
| 14 | * | ||
| 15 | * This program is free software; you can distribute it and/or modify it | ||
| 16 | * under the terms of the GNU General Public License (Version 2) as | ||
| 17 | * published by the Free Software Foundation. | ||
| 18 | * | ||
| 19 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 20 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 21 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 22 | * for more details. | ||
| 23 | * | ||
| 24 | * You should have received a copy of the GNU General Public License along | ||
| 25 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 26 | * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. | ||
| 27 | * | ||
| 28 | * ######################################################################## | ||
| 29 | * | ||
| 30 | * | ||
| 31 | */ | ||
| 32 | |||
| 33 | #include <linux/module.h> | ||
| 34 | #include <linux/moduleparam.h> | ||
| 35 | #include <linux/init.h> | ||
| 36 | #include <linux/cpufreq.h> | ||
| 37 | #include <linux/ioport.h> | ||
| 38 | #include <linux/kernel.h> | ||
| 39 | #include <linux/timer.h> | ||
| 40 | #include <linux/mm.h> | ||
| 41 | #include <linux/notifier.h> | ||
| 42 | #include <linux/interrupt.h> | ||
| 43 | #include <linux/spinlock.h> | ||
| 44 | #include <linux/mutex.h> | ||
| 45 | #include <linux/platform_device.h> | ||
| 46 | #include <linux/slab.h> | ||
| 47 | |||
| 48 | #include <asm/io.h> | ||
| 49 | #include <asm/irq.h> | ||
| 50 | #include <asm/system.h> | ||
| 51 | |||
| 52 | #include <asm/mach-au1x00/au1000.h> | ||
| 53 | #include "au1000_generic.h" | ||
| 54 | |||
| 55 | MODULE_LICENSE("GPL"); | ||
| 56 | MODULE_AUTHOR("Pete Popov <ppopov@embeddedalley.com>"); | ||
| 57 | MODULE_DESCRIPTION("Linux PCMCIA Card Services: Au1x00 Socket Controller"); | ||
| 58 | |||
| 59 | #if 0 | ||
| 60 | #define debug(x,args...) printk(KERN_DEBUG "%s: " x, __func__ , ##args) | ||
| 61 | #else | ||
| 62 | #define debug(x,args...) | ||
| 63 | #endif | ||
| 64 | |||
| 65 | #define MAP_SIZE 0x100000 | ||
| 66 | extern struct au1000_pcmcia_socket au1000_pcmcia_socket[]; | ||
| 67 | #define PCMCIA_SOCKET(x) (au1000_pcmcia_socket + (x)) | ||
| 68 | #define to_au1000_socket(x) container_of(x, struct au1000_pcmcia_socket, socket) | ||
| 69 | |||
| 70 | /* Some boards like to support CF cards as IDE root devices, so they | ||
| 71 | * grab pcmcia sockets directly. | ||
| 72 | */ | ||
| 73 | u32 *pcmcia_base_vaddrs[2]; | ||
| 74 | extern const unsigned long mips_io_port_base; | ||
| 75 | |||
| 76 | static DEFINE_MUTEX(pcmcia_sockets_lock); | ||
| 77 | |||
| 78 | static int (*au1x00_pcmcia_hw_init[])(struct device *dev) = { | ||
| 79 | au1x_board_init, | ||
| 80 | }; | ||
| 81 | |||
| 82 | static int | ||
| 83 | au1x00_pcmcia_skt_state(struct au1000_pcmcia_socket *skt) | ||
| 84 | { | ||
| 85 | struct pcmcia_state state; | ||
| 86 | unsigned int stat; | ||
| 87 | |||
| 88 | memset(&state, 0, sizeof(struct pcmcia_state)); | ||
| 89 | |||
| 90 | skt->ops->socket_state(skt, &state); | ||
| 91 | |||
| 92 | stat = state.detect ? SS_DETECT : 0; | ||
| 93 | stat |= state.ready ? SS_READY : 0; | ||
| 94 | stat |= state.wrprot ? SS_WRPROT : 0; | ||
| 95 | stat |= state.vs_3v ? SS_3VCARD : 0; | ||
| 96 | stat |= state.vs_Xv ? SS_XVCARD : 0; | ||
| 97 | stat |= skt->cs_state.Vcc ? SS_POWERON : 0; | ||
| 98 | |||
| 99 | if (skt->cs_state.flags & SS_IOCARD) | ||
| 100 | stat |= state.bvd1 ? SS_STSCHG : 0; | ||
| 101 | else { | ||
| 102 | if (state.bvd1 == 0) | ||
| 103 | stat |= SS_BATDEAD; | ||
| 104 | else if (state.bvd2 == 0) | ||
| 105 | stat |= SS_BATWARN; | ||
| 106 | } | ||
| 107 | return stat; | ||
| 108 | } | ||
| 109 | |||
| 110 | /* | ||
| 111 | * au100_pcmcia_config_skt | ||
| 112 | * | ||
| 113 | * Convert PCMCIA socket state to our socket configure structure. | ||
| 114 | */ | ||
| 115 | static int | ||
| 116 | au1x00_pcmcia_config_skt(struct au1000_pcmcia_socket *skt, socket_state_t *state) | ||
| 117 | { | ||
| 118 | int ret; | ||
| 119 | |||
| 120 | ret = skt->ops->configure_socket(skt, state); | ||
| 121 | if (ret == 0) { | ||
| 122 | skt->cs_state = *state; | ||
| 123 | } | ||
| 124 | |||
| 125 | if (ret < 0) | ||
| 126 | debug("unable to configure socket %d\n", skt->nr); | ||
| 127 | |||
| 128 | return ret; | ||
| 129 | } | ||
| 130 | |||
| 131 | /* au1x00_pcmcia_sock_init() | ||
| 132 | * | ||
| 133 | * (Re-)Initialise the socket, turning on status interrupts | ||
| 134 | * and PCMCIA bus. This must wait for power to stabilise | ||
| 135 | * so that the card status signals report correctly. | ||
| 136 | * | ||
| 137 | * Returns: 0 | ||
| 138 | */ | ||
| 139 | static int au1x00_pcmcia_sock_init(struct pcmcia_socket *sock) | ||
| 140 | { | ||
| 141 | struct au1000_pcmcia_socket *skt = to_au1000_socket(sock); | ||
| 142 | |||
| 143 | debug("initializing socket %u\n", skt->nr); | ||
| 144 | |||
| 145 | skt->ops->socket_init(skt); | ||
| 146 | return 0; | ||
| 147 | } | ||
| 148 | |||
| 149 | /* | ||
| 150 | * au1x00_pcmcia_suspend() | ||
| 151 | * | ||
| 152 | * Remove power on the socket, disable IRQs from the card. | ||
| 153 | * Turn off status interrupts, and disable the PCMCIA bus. | ||
| 154 | * | ||
| 155 | * Returns: 0 | ||
| 156 | */ | ||
| 157 | static int au1x00_pcmcia_suspend(struct pcmcia_socket *sock) | ||
| 158 | { | ||
| 159 | struct au1000_pcmcia_socket *skt = to_au1000_socket(sock); | ||
| 160 | |||
| 161 | debug("suspending socket %u\n", skt->nr); | ||
| 162 | |||
| 163 | skt->ops->socket_suspend(skt); | ||
| 164 | |||
| 165 | return 0; | ||
| 166 | } | ||
| 167 | |||
| 168 | static DEFINE_SPINLOCK(status_lock); | ||
| 169 | |||
| 170 | /* | ||
| 171 | * au1x00_check_status() | ||
| 172 | */ | ||
| 173 | static void au1x00_check_status(struct au1000_pcmcia_socket *skt) | ||
| 174 | { | ||
| 175 | unsigned int events; | ||
| 176 | |||
| 177 | debug("entering PCMCIA monitoring thread\n"); | ||
| 178 | |||
| 179 | do { | ||
| 180 | unsigned int status; | ||
| 181 | unsigned long flags; | ||
| 182 | |||
| 183 | status = au1x00_pcmcia_skt_state(skt); | ||
| 184 | |||
| 185 | spin_lock_irqsave(&status_lock, flags); | ||
| 186 | events = (status ^ skt->status) & skt->cs_state.csc_mask; | ||
| 187 | skt->status = status; | ||
| 188 | spin_unlock_irqrestore(&status_lock, flags); | ||
| 189 | |||
| 190 | debug("events: %s%s%s%s%s%s\n", | ||
| 191 | events == 0 ? "<NONE>" : "", | ||
| 192 | events & SS_DETECT ? "DETECT " : "", | ||
| 193 | events & SS_READY ? "READY " : "", | ||
| 194 | events & SS_BATDEAD ? "BATDEAD " : "", | ||
| 195 | events & SS_BATWARN ? "BATWARN " : "", | ||
| 196 | events & SS_STSCHG ? "STSCHG " : ""); | ||
| 197 | |||
| 198 | if (events) | ||
| 199 | pcmcia_parse_events(&skt->socket, events); | ||
| 200 | } while (events); | ||
| 201 | } | ||
| 202 | |||
| 203 | /* | ||
| 204 | * au1x00_pcmcia_poll_event() | ||
| 205 | * Let's poll for events in addition to IRQs since IRQ only is unreliable... | ||
| 206 | */ | ||
| 207 | static void au1x00_pcmcia_poll_event(unsigned long dummy) | ||
| 208 | { | ||
| 209 | struct au1000_pcmcia_socket *skt = (struct au1000_pcmcia_socket *)dummy; | ||
| 210 | debug("polling for events\n"); | ||
| 211 | |||
| 212 | mod_timer(&skt->poll_timer, jiffies + AU1000_PCMCIA_POLL_PERIOD); | ||
| 213 | |||
| 214 | au1x00_check_status(skt); | ||
| 215 | } | ||
| 216 | |||
| 217 | /* au1x00_pcmcia_get_status() | ||
| 218 | * | ||
| 219 | * From the sa11xx_core.c: | ||
| 220 | * Implements the get_status() operation for the in-kernel PCMCIA | ||
| 221 | * service (formerly SS_GetStatus in Card Services). Essentially just | ||
| 222 | * fills in bits in `status' according to internal driver state or | ||
| 223 | * the value of the voltage detect chipselect register. | ||
| 224 | * | ||
| 225 | * As a debugging note, during card startup, the PCMCIA core issues | ||
| 226 | * three set_socket() commands in a row the first with RESET deasserted, | ||
| 227 | * the second with RESET asserted, and the last with RESET deasserted | ||
| 228 | * again. Following the third set_socket(), a get_status() command will | ||
| 229 | * be issued. The kernel is looking for the SS_READY flag (see | ||
| 230 | * setup_socket(), reset_socket(), and unreset_socket() in cs.c). | ||
| 231 | * | ||
| 232 | * Returns: 0 | ||
| 233 | */ | ||
| 234 | static int | ||
| 235 | au1x00_pcmcia_get_status(struct pcmcia_socket *sock, unsigned int *status) | ||
| 236 | { | ||
| 237 | struct au1000_pcmcia_socket *skt = to_au1000_socket(sock); | ||
| 238 | |||
| 239 | skt->status = au1x00_pcmcia_skt_state(skt); | ||
| 240 | *status = skt->status; | ||
| 241 | |||
| 242 | return 0; | ||
| 243 | } | ||
| 244 | |||
| 245 | /* au1x00_pcmcia_set_socket() | ||
| 246 | * Implements the set_socket() operation for the in-kernel PCMCIA | ||
| 247 | * service (formerly SS_SetSocket in Card Services). We more or | ||
| 248 | * less punt all of this work and let the kernel handle the details | ||
| 249 | * of power configuration, reset, &c. We also record the value of | ||
| 250 | * `state' in order to regurgitate it to the PCMCIA core later. | ||
| 251 | * | ||
| 252 | * Returns: 0 | ||
| 253 | */ | ||
| 254 | static int | ||
| 255 | au1x00_pcmcia_set_socket(struct pcmcia_socket *sock, socket_state_t *state) | ||
| 256 | { | ||
| 257 | struct au1000_pcmcia_socket *skt = to_au1000_socket(sock); | ||
| 258 | |||
| 259 | debug("for sock %u\n", skt->nr); | ||
| 260 | |||
| 261 | debug("\tmask: %s%s%s%s%s%s\n\tflags: %s%s%s%s%s%s\n", | ||
| 262 | (state->csc_mask==0)?"<NONE>":"", | ||
| 263 | (state->csc_mask&SS_DETECT)?"DETECT ":"", | ||
| 264 | (state->csc_mask&SS_READY)?"READY ":"", | ||
| 265 | (state->csc_mask&SS_BATDEAD)?"BATDEAD ":"", | ||
| 266 | (state->csc_mask&SS_BATWARN)?"BATWARN ":"", | ||
| 267 | (state->csc_mask&SS_STSCHG)?"STSCHG ":"", | ||
| 268 | (state->flags==0)?"<NONE>":"", | ||
| 269 | (state->flags&SS_PWR_AUTO)?"PWR_AUTO ":"", | ||
| 270 | (state->flags&SS_IOCARD)?"IOCARD ":"", | ||
| 271 | (state->flags&SS_RESET)?"RESET ":"", | ||
| 272 | (state->flags&SS_SPKR_ENA)?"SPKR_ENA ":"", | ||
| 273 | (state->flags&SS_OUTPUT_ENA)?"OUTPUT_ENA ":""); | ||
| 274 | debug("\tVcc %d Vpp %d irq %d\n", | ||
| 275 | state->Vcc, state->Vpp, state->io_irq); | ||
| 276 | |||
| 277 | return au1x00_pcmcia_config_skt(skt, state); | ||
| 278 | } | ||
| 279 | |||
| 280 | int | ||
| 281 | au1x00_pcmcia_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *map) | ||
| 282 | { | ||
| 283 | struct au1000_pcmcia_socket *skt = to_au1000_socket(sock); | ||
| 284 | unsigned int speed; | ||
| 285 | |||
| 286 | if(map->map>=MAX_IO_WIN){ | ||
| 287 | debug("map (%d) out of range\n", map->map); | ||
| 288 | return -1; | ||
| 289 | } | ||
| 290 | |||
| 291 | if(map->flags&MAP_ACTIVE){ | ||
| 292 | speed=(map->speed>0)?map->speed:AU1000_PCMCIA_IO_SPEED; | ||
| 293 | skt->spd_io[map->map] = speed; | ||
| 294 | } | ||
| 295 | |||
| 296 | map->start=(unsigned int)(u32)skt->virt_io; | ||
| 297 | map->stop=map->start+MAP_SIZE; | ||
| 298 | return 0; | ||
| 299 | |||
| 300 | } /* au1x00_pcmcia_set_io_map() */ | ||
| 301 | |||
| 302 | |||
| 303 | static int | ||
| 304 | au1x00_pcmcia_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map *map) | ||
| 305 | { | ||
| 306 | struct au1000_pcmcia_socket *skt = to_au1000_socket(sock); | ||
| 307 | unsigned short speed = map->speed; | ||
| 308 | |||
| 309 | if(map->map>=MAX_WIN){ | ||
| 310 | debug("map (%d) out of range\n", map->map); | ||
| 311 | return -1; | ||
| 312 | } | ||
| 313 | |||
| 314 | if (map->flags & MAP_ATTRIB) { | ||
| 315 | skt->spd_attr[map->map] = speed; | ||
| 316 | skt->spd_mem[map->map] = 0; | ||
| 317 | } else { | ||
| 318 | skt->spd_attr[map->map] = 0; | ||
| 319 | skt->spd_mem[map->map] = speed; | ||
| 320 | } | ||
| 321 | |||
| 322 | if (map->flags & MAP_ATTRIB) { | ||
| 323 | map->static_start = skt->phys_attr + map->card_start; | ||
| 324 | } | ||
| 325 | else { | ||
| 326 | map->static_start = skt->phys_mem + map->card_start; | ||
| 327 | } | ||
| 328 | |||
| 329 | debug("set_mem_map %d start %08lx card_start %08x\n", | ||
| 330 | map->map, map->static_start, map->card_start); | ||
| 331 | return 0; | ||
| 332 | |||
| 333 | } /* au1x00_pcmcia_set_mem_map() */ | ||
| 334 | |||
| 335 | static struct pccard_operations au1x00_pcmcia_operations = { | ||
| 336 | .init = au1x00_pcmcia_sock_init, | ||
| 337 | .suspend = au1x00_pcmcia_suspend, | ||
| 338 | .get_status = au1x00_pcmcia_get_status, | ||
| 339 | .set_socket = au1x00_pcmcia_set_socket, | ||
| 340 | .set_io_map = au1x00_pcmcia_set_io_map, | ||
| 341 | .set_mem_map = au1x00_pcmcia_set_mem_map, | ||
| 342 | }; | ||
| 343 | |||
| 344 | static const char *skt_names[] = { | ||
| 345 | "PCMCIA socket 0", | ||
| 346 | "PCMCIA socket 1", | ||
| 347 | }; | ||
| 348 | |||
| 349 | struct skt_dev_info { | ||
| 350 | int nskt; | ||
| 351 | }; | ||
| 352 | |||
| 353 | int au1x00_pcmcia_socket_probe(struct device *dev, struct pcmcia_low_level *ops, int first, int nr) | ||
| 354 | { | ||
| 355 | struct skt_dev_info *sinfo; | ||
| 356 | struct au1000_pcmcia_socket *skt; | ||
| 357 | int ret, i; | ||
| 358 | |||
| 359 | sinfo = kzalloc(sizeof(struct skt_dev_info), GFP_KERNEL); | ||
| 360 | if (!sinfo) { | ||
| 361 | ret = -ENOMEM; | ||
| 362 | goto out; | ||
| 363 | } | ||
| 364 | |||
| 365 | sinfo->nskt = nr; | ||
| 366 | |||
| 367 | /* | ||
| 368 | * Initialise the per-socket structure. | ||
| 369 | */ | ||
| 370 | for (i = 0; i < nr; i++) { | ||
| 371 | skt = PCMCIA_SOCKET(i); | ||
| 372 | memset(skt, 0, sizeof(*skt)); | ||
| 373 | |||
| 374 | skt->socket.resource_ops = &pccard_static_ops; | ||
| 375 | skt->socket.ops = &au1x00_pcmcia_operations; | ||
| 376 | skt->socket.owner = ops->owner; | ||
| 377 | skt->socket.dev.parent = dev; | ||
| 378 | |||
| 379 | init_timer(&skt->poll_timer); | ||
| 380 | skt->poll_timer.function = au1x00_pcmcia_poll_event; | ||
| 381 | skt->poll_timer.data = (unsigned long)skt; | ||
| 382 | skt->poll_timer.expires = jiffies + AU1000_PCMCIA_POLL_PERIOD; | ||
| 383 | |||
| 384 | skt->nr = first + i; | ||
| 385 | skt->irq = 255; | ||
| 386 | skt->dev = dev; | ||
| 387 | skt->ops = ops; | ||
| 388 | |||
| 389 | skt->res_skt.name = skt_names[skt->nr]; | ||
| 390 | skt->res_io.name = "io"; | ||
| 391 | skt->res_io.flags = IORESOURCE_MEM | IORESOURCE_BUSY; | ||
| 392 | skt->res_mem.name = "memory"; | ||
| 393 | skt->res_mem.flags = IORESOURCE_MEM; | ||
| 394 | skt->res_attr.name = "attribute"; | ||
| 395 | skt->res_attr.flags = IORESOURCE_MEM; | ||
| 396 | |||
| 397 | /* | ||
| 398 | * PCMCIA client drivers use the inb/outb macros to access the | ||
| 399 | * IO registers. Since mips_io_port_base is added to the | ||
| 400 | * access address of the mips implementation of inb/outb, | ||
| 401 | * we need to subtract it here because we want to access the | ||
| 402 | * I/O or MEM address directly, without going through this | ||
| 403 | * "mips_io_port_base" mechanism. | ||
| 404 | */ | ||
| 405 | if (i == 0) { | ||
| 406 | skt->virt_io = (void *) | ||
| 407 | (ioremap((phys_t)AU1X_SOCK0_IO, 0x1000) - | ||
| 408 | (u32)mips_io_port_base); | ||
| 409 | skt->phys_attr = AU1X_SOCK0_PHYS_ATTR; | ||
| 410 | skt->phys_mem = AU1X_SOCK0_PHYS_MEM; | ||
| 411 | } | ||
| 412 | else { | ||
| 413 | skt->virt_io = (void *) | ||
| 414 | (ioremap((phys_t)AU1X_SOCK1_IO, 0x1000) - | ||
| 415 | (u32)mips_io_port_base); | ||
| 416 | skt->phys_attr = AU1X_SOCK1_PHYS_ATTR; | ||
| 417 | skt->phys_mem = AU1X_SOCK1_PHYS_MEM; | ||
| 418 | } | ||
| 419 | pcmcia_base_vaddrs[i] = (u32 *)skt->virt_io; | ||
| 420 | ret = ops->hw_init(skt); | ||
| 421 | |||
| 422 | skt->socket.features = SS_CAP_STATIC_MAP|SS_CAP_PCCARD; | ||
| 423 | skt->socket.irq_mask = 0; | ||
| 424 | skt->socket.map_size = MAP_SIZE; | ||
| 425 | skt->socket.pci_irq = skt->irq; | ||
| 426 | skt->socket.io_offset = (unsigned long)skt->virt_io; | ||
| 427 | |||
| 428 | skt->status = au1x00_pcmcia_skt_state(skt); | ||
| 429 | |||
| 430 | ret = pcmcia_register_socket(&skt->socket); | ||
| 431 | if (ret) | ||
| 432 | goto out_err; | ||
| 433 | |||
| 434 | WARN_ON(skt->socket.sock != i); | ||
| 435 | |||
| 436 | add_timer(&skt->poll_timer); | ||
| 437 | } | ||
| 438 | |||
| 439 | dev_set_drvdata(dev, sinfo); | ||
| 440 | return 0; | ||
| 441 | |||
| 442 | |||
| 443 | out_err: | ||
| 444 | ops->hw_shutdown(skt); | ||
| 445 | while (i-- > 0) { | ||
| 446 | skt = PCMCIA_SOCKET(i); | ||
| 447 | |||
| 448 | del_timer_sync(&skt->poll_timer); | ||
| 449 | pcmcia_unregister_socket(&skt->socket); | ||
| 450 | if (i == 0) { | ||
| 451 | iounmap(skt->virt_io + (u32)mips_io_port_base); | ||
| 452 | skt->virt_io = NULL; | ||
| 453 | } | ||
| 454 | #ifndef CONFIG_MIPS_XXS1500 | ||
| 455 | else { | ||
| 456 | iounmap(skt->virt_io + (u32)mips_io_port_base); | ||
| 457 | skt->virt_io = NULL; | ||
| 458 | } | ||
| 459 | #endif | ||
| 460 | ops->hw_shutdown(skt); | ||
| 461 | |||
| 462 | } | ||
| 463 | kfree(sinfo); | ||
| 464 | out: | ||
| 465 | return ret; | ||
| 466 | } | ||
| 467 | |||
| 468 | int au1x00_drv_pcmcia_remove(struct platform_device *dev) | ||
| 469 | { | ||
| 470 | struct skt_dev_info *sinfo = platform_get_drvdata(dev); | ||
| 471 | int i; | ||
| 472 | |||
| 473 | mutex_lock(&pcmcia_sockets_lock); | ||
| 474 | platform_set_drvdata(dev, NULL); | ||
| 475 | |||
| 476 | for (i = 0; i < sinfo->nskt; i++) { | ||
| 477 | struct au1000_pcmcia_socket *skt = PCMCIA_SOCKET(i); | ||
| 478 | |||
| 479 | del_timer_sync(&skt->poll_timer); | ||
| 480 | pcmcia_unregister_socket(&skt->socket); | ||
| 481 | skt->ops->hw_shutdown(skt); | ||
| 482 | au1x00_pcmcia_config_skt(skt, &dead_socket); | ||
| 483 | iounmap(skt->virt_io + (u32)mips_io_port_base); | ||
| 484 | skt->virt_io = NULL; | ||
| 485 | } | ||
| 486 | |||
| 487 | kfree(sinfo); | ||
| 488 | mutex_unlock(&pcmcia_sockets_lock); | ||
| 489 | return 0; | ||
| 490 | } | ||
| 491 | |||
| 492 | |||
| 493 | /* | ||
| 494 | * PCMCIA "Driver" API | ||
| 495 | */ | ||
| 496 | |||
| 497 | static int au1x00_drv_pcmcia_probe(struct platform_device *dev) | ||
| 498 | { | ||
| 499 | int i, ret = -ENODEV; | ||
| 500 | |||
| 501 | mutex_lock(&pcmcia_sockets_lock); | ||
| 502 | for (i=0; i < ARRAY_SIZE(au1x00_pcmcia_hw_init); i++) { | ||
| 503 | ret = au1x00_pcmcia_hw_init[i](&dev->dev); | ||
| 504 | if (ret == 0) | ||
| 505 | break; | ||
| 506 | } | ||
| 507 | mutex_unlock(&pcmcia_sockets_lock); | ||
| 508 | return ret; | ||
| 509 | } | ||
| 510 | |||
| 511 | static struct platform_driver au1x00_pcmcia_driver = { | ||
| 512 | .driver = { | ||
| 513 | .name = "au1x00-pcmcia", | ||
| 514 | .owner = THIS_MODULE, | ||
| 515 | }, | ||
| 516 | .probe = au1x00_drv_pcmcia_probe, | ||
| 517 | .remove = au1x00_drv_pcmcia_remove, | ||
| 518 | }; | ||
| 519 | |||
| 520 | |||
| 521 | /* au1x00_pcmcia_init() | ||
| 522 | * | ||
| 523 | * This routine performs low-level PCMCIA initialization and then | ||
| 524 | * registers this socket driver with Card Services. | ||
| 525 | * | ||
| 526 | * Returns: 0 on success, -ve error code on failure | ||
| 527 | */ | ||
| 528 | static int __init au1x00_pcmcia_init(void) | ||
| 529 | { | ||
| 530 | int error = 0; | ||
| 531 | error = platform_driver_register(&au1x00_pcmcia_driver); | ||
| 532 | return error; | ||
| 533 | } | ||
| 534 | |||
| 535 | /* au1x00_pcmcia_exit() | ||
| 536 | * Invokes the low-level kernel service to free IRQs associated with this | ||
| 537 | * socket controller and reset GPIO edge detection. | ||
| 538 | */ | ||
| 539 | static void __exit au1x00_pcmcia_exit(void) | ||
| 540 | { | ||
| 541 | platform_driver_unregister(&au1x00_pcmcia_driver); | ||
| 542 | } | ||
| 543 | |||
| 544 | module_init(au1x00_pcmcia_init); | ||
| 545 | module_exit(au1x00_pcmcia_exit); | ||
diff --git a/drivers/pcmcia/au1000_generic.h b/drivers/pcmcia/au1000_generic.h new file mode 100644 index 00000000000..5c36bda2963 --- /dev/null +++ b/drivers/pcmcia/au1000_generic.h | |||
| @@ -0,0 +1,135 @@ | |||
| 1 | /* | ||
| 2 | * Alchemy Semi Au1000 pcmcia driver include file | ||
| 3 | * | ||
| 4 | * Copyright 2001 MontaVista Software Inc. | ||
| 5 | * Author: MontaVista Software, Inc. | ||
| 6 | * ppopov@mvista.com or source@mvista.com | ||
| 7 | * | ||
| 8 | * This program is free software; you can distribute it and/or modify it | ||
| 9 | * under the terms of the GNU General Public License (Version 2) as | ||
| 10 | * published by the Free Software Foundation. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 15 | * for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License along | ||
| 18 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 19 | * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. | ||
| 20 | */ | ||
| 21 | #ifndef __ASM_AU1000_PCMCIA_H | ||
| 22 | #define __ASM_AU1000_PCMCIA_H | ||
| 23 | |||
| 24 | /* include the world */ | ||
| 25 | |||
| 26 | #include <pcmcia/ss.h> | ||
| 27 | #include <pcmcia/cistpl.h> | ||
| 28 | #include "cs_internal.h" | ||
| 29 | |||
| 30 | #define AU1000_PCMCIA_POLL_PERIOD (2*HZ) | ||
| 31 | #define AU1000_PCMCIA_IO_SPEED (255) | ||
| 32 | #define AU1000_PCMCIA_MEM_SPEED (300) | ||
| 33 | |||
| 34 | #define AU1X_SOCK0_IO 0xF00000000ULL | ||
| 35 | #define AU1X_SOCK0_PHYS_ATTR 0xF40000000ULL | ||
| 36 | #define AU1X_SOCK0_PHYS_MEM 0xF80000000ULL | ||
| 37 | |||
| 38 | /* pcmcia socket 1 needs external glue logic so the memory map | ||
| 39 | * differs from board to board. | ||
| 40 | */ | ||
| 41 | #if defined(CONFIG_MIPS_PB1000) | ||
| 42 | #define AU1X_SOCK1_IO 0xF08000000ULL | ||
| 43 | #define AU1X_SOCK1_PHYS_ATTR 0xF48000000ULL | ||
| 44 | #define AU1X_SOCK1_PHYS_MEM 0xF88000000ULL | ||
| 45 | #endif | ||
| 46 | |||
| 47 | struct pcmcia_state { | ||
| 48 | unsigned detect: 1, | ||
| 49 | ready: 1, | ||
| 50 | wrprot: 1, | ||
| 51 | bvd1: 1, | ||
| 52 | bvd2: 1, | ||
| 53 | vs_3v: 1, | ||
| 54 | vs_Xv: 1; | ||
| 55 | }; | ||
| 56 | |||
| 57 | struct pcmcia_configure { | ||
| 58 | unsigned sock: 8, | ||
| 59 | vcc: 8, | ||
| 60 | vpp: 8, | ||
| 61 | output: 1, | ||
| 62 | speaker: 1, | ||
| 63 | reset: 1; | ||
| 64 | }; | ||
| 65 | |||
| 66 | struct pcmcia_irqs { | ||
| 67 | int sock; | ||
| 68 | int irq; | ||
| 69 | const char *str; | ||
| 70 | }; | ||
| 71 | |||
| 72 | |||
| 73 | struct au1000_pcmcia_socket { | ||
| 74 | struct pcmcia_socket socket; | ||
| 75 | |||
| 76 | /* | ||
| 77 | * Info from low level handler | ||
| 78 | */ | ||
| 79 | struct device *dev; | ||
| 80 | unsigned int nr; | ||
| 81 | unsigned int irq; | ||
| 82 | |||
| 83 | /* | ||
| 84 | * Core PCMCIA state | ||
| 85 | */ | ||
| 86 | struct pcmcia_low_level *ops; | ||
| 87 | |||
| 88 | unsigned int status; | ||
| 89 | socket_state_t cs_state; | ||
| 90 | |||
| 91 | unsigned short spd_io[MAX_IO_WIN]; | ||
| 92 | unsigned short spd_mem[MAX_WIN]; | ||
| 93 | unsigned short spd_attr[MAX_WIN]; | ||
| 94 | |||
| 95 | struct resource res_skt; | ||
| 96 | struct resource res_io; | ||
| 97 | struct resource res_mem; | ||
| 98 | struct resource res_attr; | ||
| 99 | |||
| 100 | void * virt_io; | ||
| 101 | unsigned int phys_io; | ||
| 102 | unsigned int phys_attr; | ||
| 103 | unsigned int phys_mem; | ||
| 104 | unsigned short speed_io, speed_attr, speed_mem; | ||
| 105 | |||
| 106 | unsigned int irq_state; | ||
| 107 | |||
| 108 | struct timer_list poll_timer; | ||
| 109 | }; | ||
| 110 | |||
| 111 | struct pcmcia_low_level { | ||
| 112 | struct module *owner; | ||
| 113 | |||
| 114 | int (*hw_init)(struct au1000_pcmcia_socket *); | ||
| 115 | void (*hw_shutdown)(struct au1000_pcmcia_socket *); | ||
| 116 | |||
| 117 | void (*socket_state)(struct au1000_pcmcia_socket *, struct pcmcia_state *); | ||
| 118 | int (*configure_socket)(struct au1000_pcmcia_socket *, struct socket_state_t *); | ||
| 119 | |||
| 120 | /* | ||
| 121 | * Enable card status IRQs on (re-)initialisation. This can | ||
| 122 | * be called at initialisation, power management event, or | ||
| 123 | * pcmcia event. | ||
| 124 | */ | ||
| 125 | void (*socket_init)(struct au1000_pcmcia_socket *); | ||
| 126 | |||
| 127 | /* | ||
| 128 | * Disable card status IRQs and PCMCIA bus on suspend. | ||
| 129 | */ | ||
| 130 | void (*socket_suspend)(struct au1000_pcmcia_socket *); | ||
| 131 | }; | ||
| 132 | |||
| 133 | extern int au1x_board_init(struct device *dev); | ||
| 134 | |||
| 135 | #endif /* __ASM_AU1000_PCMCIA_H */ | ||
diff --git a/drivers/pcmcia/au1000_pb1x00.c b/drivers/pcmcia/au1000_pb1x00.c new file mode 100644 index 00000000000..b2396647a16 --- /dev/null +++ b/drivers/pcmcia/au1000_pb1x00.c | |||
| @@ -0,0 +1,294 @@ | |||
| 1 | /* | ||
| 2 | * | ||
| 3 | * Alchemy Semi Pb1000 boards specific pcmcia routines. | ||
| 4 | * | ||
| 5 | * Copyright 2002 MontaVista Software Inc. | ||
| 6 | * Author: MontaVista Software, Inc. | ||
| 7 | * ppopov@mvista.com or source@mvista.com | ||
| 8 | * | ||
| 9 | * ######################################################################## | ||
| 10 | * | ||
| 11 | * This program is free software; you can distribute it and/or modify it | ||
| 12 | * under the terms of the GNU General Public License (Version 2) as | ||
| 13 | * published by the Free Software Foundation. | ||
| 14 | * | ||
| 15 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 16 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 17 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 18 | * for more details. | ||
| 19 | * | ||
| 20 | * You should have received a copy of the GNU General Public License along | ||
| 21 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 22 | * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. | ||
| 23 | */ | ||
| 24 | #include <linux/module.h> | ||
| 25 | #include <linux/init.h> | ||
| 26 | #include <linux/delay.h> | ||
| 27 | #include <linux/ioport.h> | ||
| 28 | #include <linux/kernel.h> | ||
| 29 | #include <linux/timer.h> | ||
| 30 | #include <linux/mm.h> | ||
| 31 | #include <linux/proc_fs.h> | ||
| 32 | #include <linux/types.h> | ||
| 33 | |||
| 34 | #include <pcmcia/ss.h> | ||
| 35 | #include <pcmcia/cistpl.h> | ||
| 36 | |||
| 37 | #include <asm/io.h> | ||
| 38 | #include <asm/irq.h> | ||
| 39 | #include <asm/system.h> | ||
| 40 | |||
| 41 | #include <asm/au1000.h> | ||
| 42 | #include <asm/au1000_pcmcia.h> | ||
| 43 | |||
| 44 | #define debug(fmt, arg...) do { } while (0) | ||
| 45 | |||
| 46 | #include <asm/pb1000.h> | ||
| 47 | #define PCMCIA_IRQ AU1000_GPIO_15 | ||
| 48 | |||
| 49 | static int pb1x00_pcmcia_init(struct pcmcia_init *init) | ||
| 50 | { | ||
| 51 | u16 pcr; | ||
| 52 | pcr = PCR_SLOT_0_RST | PCR_SLOT_1_RST; | ||
| 53 | |||
| 54 | au_writel(0x8000, PB1000_MDR); /* clear pcmcia interrupt */ | ||
| 55 | au_sync_delay(100); | ||
| 56 | au_writel(0x4000, PB1000_MDR); /* enable pcmcia interrupt */ | ||
| 57 | au_sync(); | ||
| 58 | |||
| 59 | pcr |= SET_VCC_VPP(VCC_HIZ,VPP_HIZ,0); | ||
| 60 | pcr |= SET_VCC_VPP(VCC_HIZ,VPP_HIZ,1); | ||
| 61 | au_writel(pcr, PB1000_PCR); | ||
| 62 | au_sync_delay(20); | ||
| 63 | |||
| 64 | return PCMCIA_NUM_SOCKS; | ||
| 65 | } | ||
| 66 | |||
| 67 | static int pb1x00_pcmcia_shutdown(void) | ||
| 68 | { | ||
| 69 | u16 pcr; | ||
| 70 | pcr = PCR_SLOT_0_RST | PCR_SLOT_1_RST; | ||
| 71 | pcr |= SET_VCC_VPP(VCC_HIZ,VPP_HIZ,0); | ||
| 72 | pcr |= SET_VCC_VPP(VCC_HIZ,VPP_HIZ,1); | ||
| 73 | au_writel(pcr, PB1000_PCR); | ||
| 74 | au_sync_delay(20); | ||
| 75 | return 0; | ||
| 76 | } | ||
| 77 | |||
| 78 | static int | ||
| 79 | pb1x00_pcmcia_socket_state(unsigned sock, struct pcmcia_state *state) | ||
| 80 | { | ||
| 81 | u32 inserted0, inserted1; | ||
| 82 | u16 vs0, vs1; | ||
| 83 | |||
| 84 | vs0 = vs1 = (u16)au_readl(PB1000_ACR1); | ||
| 85 | inserted0 = !(vs0 & (ACR1_SLOT_0_CD1 | ACR1_SLOT_0_CD2)); | ||
| 86 | inserted1 = !(vs1 & (ACR1_SLOT_1_CD1 | ACR1_SLOT_1_CD2)); | ||
| 87 | vs0 = (vs0 >> 4) & 0x3; | ||
| 88 | vs1 = (vs1 >> 12) & 0x3; | ||
| 89 | |||
| 90 | state->ready = 0; | ||
| 91 | state->vs_Xv = 0; | ||
| 92 | state->vs_3v = 0; | ||
| 93 | state->detect = 0; | ||
| 94 | |||
| 95 | if (sock == 0) { | ||
| 96 | if (inserted0) { | ||
| 97 | switch (vs0) { | ||
| 98 | case 0: | ||
| 99 | case 2: | ||
| 100 | state->vs_3v=1; | ||
| 101 | break; | ||
| 102 | case 3: /* 5V */ | ||
| 103 | break; | ||
| 104 | default: | ||
| 105 | /* return without setting 'detect' */ | ||
| 106 | printk(KERN_ERR "pb1x00 bad VS (%d)\n", | ||
| 107 | vs0); | ||
| 108 | return 0; | ||
| 109 | } | ||
| 110 | state->detect = 1; | ||
| 111 | } | ||
| 112 | } | ||
| 113 | else { | ||
| 114 | if (inserted1) { | ||
| 115 | switch (vs1) { | ||
| 116 | case 0: | ||
| 117 | case 2: | ||
| 118 | state->vs_3v=1; | ||
| 119 | break; | ||
| 120 | case 3: /* 5V */ | ||
| 121 | break; | ||
| 122 | default: | ||
| 123 | /* return without setting 'detect' */ | ||
| 124 | printk(KERN_ERR "pb1x00 bad VS (%d)\n", | ||
| 125 | vs1); | ||
| 126 | return 0; | ||
| 127 | } | ||
| 128 | state->detect = 1; | ||
| 129 | } | ||
| 130 | } | ||
| 131 | |||
| 132 | if (state->detect) { | ||
| 133 | state->ready = 1; | ||
| 134 | } | ||
| 135 | |||
| 136 | state->bvd1=1; | ||
| 137 | state->bvd2=1; | ||
| 138 | state->wrprot=0; | ||
| 139 | return 1; | ||
| 140 | } | ||
| 141 | |||
| 142 | |||
| 143 | static int pb1x00_pcmcia_get_irq_info(struct pcmcia_irq_info *info) | ||
| 144 | { | ||
| 145 | |||
| 146 | if(info->sock > PCMCIA_MAX_SOCK) return -1; | ||
| 147 | |||
| 148 | /* | ||
| 149 | * Even in the case of the Pb1000, both sockets are connected | ||
| 150 | * to the same irq line. | ||
| 151 | */ | ||
| 152 | info->irq = PCMCIA_IRQ; | ||
| 153 | |||
| 154 | return 0; | ||
| 155 | } | ||
| 156 | |||
| 157 | |||
| 158 | static int | ||
| 159 | pb1x00_pcmcia_configure_socket(const struct pcmcia_configure *configure) | ||
| 160 | { | ||
| 161 | u16 pcr; | ||
| 162 | |||
| 163 | if(configure->sock > PCMCIA_MAX_SOCK) return -1; | ||
| 164 | |||
| 165 | pcr = au_readl(PB1000_PCR); | ||
| 166 | |||
| 167 | if (configure->sock == 0) { | ||
| 168 | pcr &= ~(PCR_SLOT_0_VCC0 | PCR_SLOT_0_VCC1 | | ||
| 169 | PCR_SLOT_0_VPP0 | PCR_SLOT_0_VPP1); | ||
| 170 | } | ||
| 171 | else { | ||
| 172 | pcr &= ~(PCR_SLOT_1_VCC0 | PCR_SLOT_1_VCC1 | | ||
| 173 | PCR_SLOT_1_VPP0 | PCR_SLOT_1_VPP1); | ||
| 174 | } | ||
| 175 | |||
| 176 | pcr &= ~PCR_SLOT_0_RST; | ||
| 177 | debug("Vcc %dV Vpp %dV, pcr %x\n", | ||
| 178 | configure->vcc, configure->vpp, pcr); | ||
| 179 | switch(configure->vcc){ | ||
| 180 | case 0: /* Vcc 0 */ | ||
| 181 | switch(configure->vpp) { | ||
| 182 | case 0: | ||
| 183 | pcr |= SET_VCC_VPP(VCC_HIZ,VPP_GND, | ||
| 184 | configure->sock); | ||
| 185 | break; | ||
| 186 | case 12: | ||
| 187 | pcr |= SET_VCC_VPP(VCC_HIZ,VPP_12V, | ||
| 188 | configure->sock); | ||
| 189 | break; | ||
| 190 | case 50: | ||
| 191 | pcr |= SET_VCC_VPP(VCC_HIZ,VPP_5V, | ||
| 192 | configure->sock); | ||
| 193 | break; | ||
| 194 | case 33: | ||
| 195 | pcr |= SET_VCC_VPP(VCC_HIZ,VPP_3V, | ||
| 196 | configure->sock); | ||
| 197 | break; | ||
| 198 | default: | ||
| 199 | pcr |= SET_VCC_VPP(VCC_HIZ,VPP_HIZ, | ||
| 200 | configure->sock); | ||
| 201 | printk("%s: bad Vcc/Vpp (%d:%d)\n", | ||
| 202 | __func__, | ||
| 203 | configure->vcc, | ||
| 204 | configure->vpp); | ||
| 205 | break; | ||
| 206 | } | ||
| 207 | break; | ||
| 208 | case 50: /* Vcc 5V */ | ||
| 209 | switch(configure->vpp) { | ||
| 210 | case 0: | ||
| 211 | pcr |= SET_VCC_VPP(VCC_5V,VPP_GND, | ||
| 212 | configure->sock); | ||
| 213 | break; | ||
| 214 | case 50: | ||
| 215 | pcr |= SET_VCC_VPP(VCC_5V,VPP_5V, | ||
| 216 | configure->sock); | ||
| 217 | break; | ||
| 218 | case 12: | ||
| 219 | pcr |= SET_VCC_VPP(VCC_5V,VPP_12V, | ||
| 220 | configure->sock); | ||
| 221 | break; | ||
| 222 | case 33: | ||
| 223 | pcr |= SET_VCC_VPP(VCC_5V,VPP_3V, | ||
| 224 | configure->sock); | ||
| 225 | break; | ||
| 226 | default: | ||
| 227 | pcr |= SET_VCC_VPP(VCC_HIZ,VPP_HIZ, | ||
| 228 | configure->sock); | ||
| 229 | printk("%s: bad Vcc/Vpp (%d:%d)\n", | ||
| 230 | __func__, | ||
| 231 | configure->vcc, | ||
| 232 | configure->vpp); | ||
| 233 | break; | ||
| 234 | } | ||
| 235 | break; | ||
| 236 | case 33: /* Vcc 3.3V */ | ||
| 237 | switch(configure->vpp) { | ||
| 238 | case 0: | ||
| 239 | pcr |= SET_VCC_VPP(VCC_3V,VPP_GND, | ||
| 240 | configure->sock); | ||
| 241 | break; | ||
| 242 | case 50: | ||
| 243 | pcr |= SET_VCC_VPP(VCC_3V,VPP_5V, | ||
| 244 | configure->sock); | ||
| 245 | break; | ||
| 246 | case 12: | ||
| 247 | pcr |= SET_VCC_VPP(VCC_3V,VPP_12V, | ||
| 248 | configure->sock); | ||
| 249 | break; | ||
| 250 | case 33: | ||
| 251 | pcr |= SET_VCC_VPP(VCC_3V,VPP_3V, | ||
| 252 | configure->sock); | ||
| 253 | break; | ||
| 254 | default: | ||
| 255 | pcr |= SET_VCC_VPP(VCC_HIZ,VPP_HIZ, | ||
| 256 | configure->sock); | ||
| 257 | printk("%s: bad Vcc/Vpp (%d:%d)\n", | ||
| 258 | __func__, | ||
| 259 | configure->vcc, | ||
| 260 | configure->vpp); | ||
| 261 | break; | ||
| 262 | } | ||
| 263 | break; | ||
| 264 | default: /* what's this ? */ | ||
| 265 | pcr |= SET_VCC_VPP(VCC_HIZ,VPP_HIZ,configure->sock); | ||
| 266 | printk(KERN_ERR "%s: bad Vcc %d\n", | ||
| 267 | __func__, configure->vcc); | ||
| 268 | break; | ||
| 269 | } | ||
| 270 | |||
| 271 | if (configure->sock == 0) { | ||
| 272 | pcr &= ~(PCR_SLOT_0_RST); | ||
| 273 | if (configure->reset) | ||
| 274 | pcr |= PCR_SLOT_0_RST; | ||
| 275 | } | ||
| 276 | else { | ||
| 277 | pcr &= ~(PCR_SLOT_1_RST); | ||
| 278 | if (configure->reset) | ||
| 279 | pcr |= PCR_SLOT_1_RST; | ||
| 280 | } | ||
| 281 | au_writel(pcr, PB1000_PCR); | ||
| 282 | au_sync_delay(300); | ||
| 283 | |||
| 284 | return 0; | ||
| 285 | } | ||
| 286 | |||
| 287 | |||
| 288 | struct pcmcia_low_level pb1x00_pcmcia_ops = { | ||
| 289 | pb1x00_pcmcia_init, | ||
| 290 | pb1x00_pcmcia_shutdown, | ||
| 291 | pb1x00_pcmcia_socket_state, | ||
| 292 | pb1x00_pcmcia_get_irq_info, | ||
| 293 | pb1x00_pcmcia_configure_socket | ||
| 294 | }; | ||
diff --git a/drivers/pcmcia/pxa2xx_lubbock.c b/drivers/pcmcia/pxa2xx_lubbock.c new file mode 100644 index 00000000000..c21888eebb5 --- /dev/null +++ b/drivers/pcmcia/pxa2xx_lubbock.c | |||
| @@ -0,0 +1,237 @@ | |||
| 1 | /* | ||
| 2 | * linux/drivers/pcmcia/pxa2xx_lubbock.c | ||
| 3 | * | ||
| 4 | * Author: George Davis | ||
| 5 | * Created: Jan 10, 2002 | ||
| 6 | * Copyright: MontaVista Software Inc. | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License version 2 as | ||
| 10 | * published by the Free Software Foundation. | ||
| 11 | * | ||
| 12 | * Originally based upon linux/drivers/pcmcia/sa1100_neponset.c | ||
| 13 | * | ||
| 14 | * Lubbock PCMCIA specific routines. | ||
| 15 | * | ||
| 16 | */ | ||
| 17 | #include <linux/module.h> | ||
| 18 | #include <linux/kernel.h> | ||
| 19 | #include <linux/device.h> | ||
| 20 | #include <linux/errno.h> | ||
| 21 | #include <linux/init.h> | ||
| 22 | #include <linux/delay.h> | ||
| 23 | |||
| 24 | #include <mach/hardware.h> | ||
| 25 | #include <asm/hardware/sa1111.h> | ||
| 26 | #include <asm/mach-types.h> | ||
| 27 | #include <mach/lubbock.h> | ||
| 28 | |||
| 29 | #include "sa1111_generic.h" | ||
| 30 | |||
| 31 | static int | ||
| 32 | lubbock_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, | ||
| 33 | const socket_state_t *state) | ||
| 34 | { | ||
| 35 | struct sa1111_pcmcia_socket *s = to_skt(skt); | ||
| 36 | unsigned int pa_dwr_mask, pa_dwr_set, misc_mask, misc_set; | ||
| 37 | int ret = 0; | ||
| 38 | |||
| 39 | pa_dwr_mask = pa_dwr_set = misc_mask = misc_set = 0; | ||
| 40 | |||
| 41 | /* Lubbock uses the Maxim MAX1602, with the following connections: | ||
| 42 | * | ||
| 43 | * Socket 0 (PCMCIA): | ||
| 44 | * MAX1602 Lubbock Register | ||
| 45 | * Pin Signal | ||
| 46 | * ----- ------- ---------------------- | ||
| 47 | * A0VPP S0_PWR0 SA-1111 GPIO A<0> | ||
| 48 | * A1VPP S0_PWR1 SA-1111 GPIO A<1> | ||
| 49 | * A0VCC S0_PWR2 SA-1111 GPIO A<2> | ||
| 50 | * A1VCC S0_PWR3 SA-1111 GPIO A<3> | ||
| 51 | * VX VCC | ||
| 52 | * VY +3.3V | ||
| 53 | * 12IN +12V | ||
| 54 | * CODE +3.3V Cirrus Code, CODE = High (VY) | ||
| 55 | * | ||
| 56 | * Socket 1 (CF): | ||
| 57 | * MAX1602 Lubbock Register | ||
| 58 | * Pin Signal | ||
| 59 | * ----- ------- ---------------------- | ||
| 60 | * A0VPP GND VPP is not connected | ||
| 61 | * A1VPP GND VPP is not connected | ||
| 62 | * A0VCC S1_PWR0 MISC_WR<14> | ||
| 63 | * A1VCC S1_PWR1 MISC_WR<15> | ||
| 64 | * VX VCC | ||
| 65 | * VY +3.3V | ||
| 66 | * 12IN GND VPP is not connected | ||
| 67 | * CODE +3.3V Cirrus Code, CODE = High (VY) | ||
| 68 | * | ||
| 69 | */ | ||
| 70 | |||
| 71 | again: | ||
| 72 | switch (skt->nr) { | ||
| 73 | case 0: | ||
| 74 | pa_dwr_mask = GPIO_A0 | GPIO_A1 | GPIO_A2 | GPIO_A3; | ||
| 75 | |||
| 76 | switch (state->Vcc) { | ||
| 77 | case 0: /* Hi-Z */ | ||
| 78 | break; | ||
| 79 | |||
| 80 | case 33: /* VY */ | ||
| 81 | pa_dwr_set |= GPIO_A3; | ||
| 82 | break; | ||
| 83 | |||
| 84 | case 50: /* VX */ | ||
| 85 | pa_dwr_set |= GPIO_A2; | ||
| 86 | break; | ||
| 87 | |||
| 88 | default: | ||
| 89 | printk(KERN_ERR "%s(): unrecognized Vcc %u\n", | ||
| 90 | __func__, state->Vcc); | ||
| 91 | ret = -1; | ||
| 92 | } | ||
| 93 | |||
| 94 | switch (state->Vpp) { | ||
| 95 | case 0: /* Hi-Z */ | ||
| 96 | break; | ||
| 97 | |||
| 98 | case 120: /* 12IN */ | ||
| 99 | pa_dwr_set |= GPIO_A1; | ||
| 100 | break; | ||
| 101 | |||
| 102 | default: /* VCC */ | ||
| 103 | if (state->Vpp == state->Vcc) | ||
| 104 | pa_dwr_set |= GPIO_A0; | ||
| 105 | else { | ||
| 106 | printk(KERN_ERR "%s(): unrecognized Vpp %u\n", | ||
| 107 | __func__, state->Vpp); | ||
| 108 | ret = -1; | ||
| 109 | break; | ||
| 110 | } | ||
| 111 | } | ||
| 112 | break; | ||
| 113 | |||
| 114 | case 1: | ||
| 115 | misc_mask = (1 << 15) | (1 << 14); | ||
| 116 | |||
| 117 | switch (state->Vcc) { | ||
| 118 | case 0: /* Hi-Z */ | ||
| 119 | break; | ||
| 120 | |||
| 121 | case 33: /* VY */ | ||
| 122 | misc_set |= 1 << 15; | ||
| 123 | break; | ||
| 124 | |||
| 125 | case 50: /* VX */ | ||
| 126 | misc_set |= 1 << 14; | ||
| 127 | break; | ||
| 128 | |||
| 129 | default: | ||
| 130 | printk(KERN_ERR "%s(): unrecognized Vcc %u\n", | ||
| 131 | __func__, state->Vcc); | ||
| 132 | ret = -1; | ||
| 133 | break; | ||
| 134 | } | ||
| 135 | |||
| 136 | if (state->Vpp != state->Vcc && state->Vpp != 0) { | ||
| 137 | printk(KERN_ERR "%s(): CF slot cannot support Vpp %u\n", | ||
| 138 | __func__, state->Vpp); | ||
| 139 | ret = -1; | ||
| 140 | break; | ||
| 141 | } | ||
| 142 | break; | ||
| 143 | |||
| 144 | default: | ||
| 145 | ret = -1; | ||
| 146 | } | ||
| 147 | |||
| 148 | if (ret == 0) | ||
| 149 | ret = sa1111_pcmcia_configure_socket(skt, state); | ||
| 150 | |||
| 151 | if (ret == 0) { | ||
| 152 | lubbock_set_misc_wr(misc_mask, misc_set); | ||
| 153 | sa1111_set_io(s->dev, pa_dwr_mask, pa_dwr_set); | ||
| 154 | } | ||
| 155 | |||
| 156 | #if 1 | ||
| 157 | if (ret == 0 && state->Vcc == 33) { | ||
| 158 | struct pcmcia_state new_state; | ||
| 159 | |||
| 160 | /* | ||
| 161 | * HACK ALERT: | ||
| 162 | * We can't sense the voltage properly on Lubbock before | ||
| 163 | * actually applying some power to the socket (catch 22). | ||
| 164 | * Resense the socket Voltage Sense pins after applying | ||
| 165 | * socket power. | ||
| 166 | * | ||
| 167 | * Note: It takes about 2.5ms for the MAX1602 VCC output | ||
| 168 | * to rise. | ||
| 169 | */ | ||
| 170 | mdelay(3); | ||
| 171 | |||
| 172 | sa1111_pcmcia_socket_state(skt, &new_state); | ||
| 173 | |||
| 174 | if (!new_state.vs_3v && !new_state.vs_Xv) { | ||
| 175 | /* | ||
| 176 | * Switch to 5V, Configure socket with 5V voltage | ||
| 177 | */ | ||
| 178 | lubbock_set_misc_wr(misc_mask, 0); | ||
| 179 | sa1111_set_io(s->dev, pa_dwr_mask, 0); | ||
| 180 | |||
| 181 | /* | ||
| 182 | * It takes about 100ms to turn off Vcc. | ||
| 183 | */ | ||
| 184 | mdelay(100); | ||
| 185 | |||
| 186 | /* | ||
| 187 | * We need to hack around the const qualifier as | ||
| 188 | * well to keep this ugly workaround localized and | ||
| 189 | * not force it to the rest of the code. Barf bags | ||
| 190 | * available in the seat pocket in front of you! | ||
| 191 | */ | ||
| 192 | ((socket_state_t *)state)->Vcc = 50; | ||
| 193 | ((socket_state_t *)state)->Vpp = 50; | ||
| 194 | goto again; | ||
| 195 | } | ||
| 196 | } | ||
| 197 | #endif | ||
| 198 | |||
| 199 | return ret; | ||
| 200 | } | ||
| 201 | |||
| 202 | static struct pcmcia_low_level lubbock_pcmcia_ops = { | ||
| 203 | .owner = THIS_MODULE, | ||
| 204 | .configure_socket = lubbock_pcmcia_configure_socket, | ||
| 205 | .socket_init = sa1111_pcmcia_socket_init, | ||
| 206 | .first = 0, | ||
| 207 | .nr = 2, | ||
| 208 | }; | ||
| 209 | |||
| 210 | #include "pxa2xx_base.h" | ||
| 211 | |||
| 212 | int pcmcia_lubbock_init(struct sa1111_dev *sadev) | ||
| 213 | { | ||
| 214 | int ret = -ENODEV; | ||
| 215 | |||
| 216 | if (machine_is_lubbock()) { | ||
| 217 | /* | ||
| 218 | * Set GPIO_A<3:0> to be outputs for the MAX1600, | ||
| 219 | * and switch to standby mode. | ||
| 220 | */ | ||
| 221 | sa1111_set_io_dir(sadev, GPIO_A0|GPIO_A1|GPIO_A2|GPIO_A3, 0, 0); | ||
| 222 | sa1111_set_io(sadev, GPIO_A0|GPIO_A1|GPIO_A2|GPIO_A3, 0); | ||
| 223 | sa1111_set_sleep_io(sadev, GPIO_A0|GPIO_A1|GPIO_A2|GPIO_A3, 0); | ||
| 224 | |||
| 225 | /* Set CF Socket 1 power to standby mode. */ | ||
| 226 | lubbock_set_misc_wr((1 << 15) | (1 << 14), 0); | ||
| 227 | |||
| 228 | pxa2xx_drv_pcmcia_ops(&lubbock_pcmcia_ops); | ||
| 229 | pxa2xx_configure_sockets(&sadev->dev); | ||
| 230 | ret = sa1111_pcmcia_add(sadev, &lubbock_pcmcia_ops, | ||
| 231 | pxa2xx_drv_pcmcia_add_one); | ||
| 232 | } | ||
| 233 | |||
| 234 | return ret; | ||
| 235 | } | ||
| 236 | |||
| 237 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/pcmcia/sa1100_badge4.c b/drivers/pcmcia/sa1100_badge4.c new file mode 100644 index 00000000000..1ce53f493be --- /dev/null +++ b/drivers/pcmcia/sa1100_badge4.c | |||
| @@ -0,0 +1,167 @@ | |||
| 1 | /* | ||
| 2 | * linux/drivers/pcmcia/sa1100_badge4.c | ||
| 3 | * | ||
| 4 | * BadgePAD 4 PCMCIA specific routines | ||
| 5 | * | ||
| 6 | * Christopher Hoover <ch@hpl.hp.com> | ||
| 7 | * | ||
| 8 | * Copyright (C) 2002 Hewlett-Packard Company | ||
| 9 | * | ||
| 10 | * This program is free software; you can redistribute it and/or modify | ||
| 11 | * it under the terms of the GNU General Public License version 2 as | ||
| 12 | * published by the Free Software Foundation. | ||
| 13 | * | ||
| 14 | */ | ||
| 15 | #include <linux/module.h> | ||
| 16 | #include <linux/kernel.h> | ||
| 17 | #include <linux/device.h> | ||
| 18 | #include <linux/errno.h> | ||
| 19 | #include <linux/init.h> | ||
| 20 | |||
| 21 | #include <mach/hardware.h> | ||
| 22 | #include <asm/mach-types.h> | ||
| 23 | #include <mach/badge4.h> | ||
| 24 | #include <asm/hardware/sa1111.h> | ||
| 25 | |||
| 26 | #include "sa1111_generic.h" | ||
| 27 | |||
| 28 | /* | ||
| 29 | * BadgePAD 4 Details | ||
| 30 | * | ||
| 31 | * PCM Vcc: | ||
| 32 | * | ||
| 33 | * PCM Vcc on BadgePAD 4 can be jumpered for 3v3 (short pins 1 and 3 | ||
| 34 | * on JP6) or 5v0 (short pins 3 and 5 on JP6). | ||
| 35 | * | ||
| 36 | * PCM Vpp: | ||
| 37 | * | ||
| 38 | * PCM Vpp on BadgePAD 4 can be jumpered for 12v0 (short pins 4 and 6 | ||
| 39 | * on JP6) or tied to PCM Vcc (short pins 2 and 4 on JP6). N.B., | ||
| 40 | * 12v0 operation requires that the power supply actually supply 12v0 | ||
| 41 | * via pin 7 of JP7. | ||
| 42 | * | ||
| 43 | * CF Vcc: | ||
| 44 | * | ||
| 45 | * CF Vcc on BadgePAD 4 can be jumpered either for 3v3 (short pins 1 | ||
| 46 | * and 2 on JP10) or 5v0 (short pins 2 and 3 on JP10). | ||
| 47 | * | ||
| 48 | * Unfortunately there's no way programmatically to determine how a | ||
| 49 | * given board is jumpered. This code assumes a default jumpering | ||
| 50 | * as described below. | ||
| 51 | * | ||
| 52 | * If the defaults aren't correct, you may override them with a pcmv | ||
| 53 | * setup argument: pcmv=<pcm vcc>,<pcm vpp>,<cf vcc>. The units are | ||
| 54 | * tenths of volts; e.g. pcmv=33,120,50 indicates 3v3 PCM Vcc, 12v0 | ||
| 55 | * PCM Vpp, and 5v0 CF Vcc. | ||
| 56 | * | ||
| 57 | */ | ||
| 58 | |||
| 59 | static int badge4_pcmvcc = 50; /* pins 3 and 5 jumpered on JP6 */ | ||
| 60 | static int badge4_pcmvpp = 50; /* pins 2 and 4 jumpered on JP6 */ | ||
| 61 | static int badge4_cfvcc = 33; /* pins 1 and 2 jumpered on JP10 */ | ||
| 62 | |||
| 63 | static void complain_about_jumpering(const char *whom, | ||
| 64 | const char *supply, | ||
| 65 | int given, int wanted) | ||
| 66 | { | ||
| 67 | printk(KERN_ERR | ||
| 68 | "%s: %s %d.%dV wanted but board is jumpered for %s %d.%dV operation" | ||
| 69 | "; re-jumper the board and/or use pcmv=xx,xx,xx\n", | ||
| 70 | whom, supply, | ||
| 71 | wanted / 10, wanted % 10, | ||
| 72 | supply, | ||
| 73 | given / 10, given % 10); | ||
| 74 | } | ||
| 75 | |||
| 76 | static int | ||
| 77 | badge4_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_t *state) | ||
| 78 | { | ||
| 79 | int ret; | ||
| 80 | |||
| 81 | switch (skt->nr) { | ||
| 82 | case 0: | ||
| 83 | if ((state->Vcc != 0) && | ||
| 84 | (state->Vcc != badge4_pcmvcc)) { | ||
| 85 | complain_about_jumpering(__func__, "pcmvcc", | ||
| 86 | badge4_pcmvcc, state->Vcc); | ||
| 87 | // Apply power regardless of the jumpering. | ||
| 88 | // return -1; | ||
| 89 | } | ||
| 90 | if ((state->Vpp != 0) && | ||
| 91 | (state->Vpp != badge4_pcmvpp)) { | ||
| 92 | complain_about_jumpering(__func__, "pcmvpp", | ||
| 93 | badge4_pcmvpp, state->Vpp); | ||
| 94 | return -1; | ||
| 95 | } | ||
| 96 | break; | ||
| 97 | |||
| 98 | case 1: | ||
| 99 | if ((state->Vcc != 0) && | ||
| 100 | (state->Vcc != badge4_cfvcc)) { | ||
| 101 | complain_about_jumpering(__func__, "cfvcc", | ||
| 102 | badge4_cfvcc, state->Vcc); | ||
| 103 | return -1; | ||
| 104 | } | ||
| 105 | break; | ||
| 106 | |||
| 107 | default: | ||
| 108 | return -1; | ||
| 109 | } | ||
| 110 | |||
| 111 | ret = sa1111_pcmcia_configure_socket(skt, state); | ||
| 112 | if (ret == 0) { | ||
| 113 | unsigned long flags; | ||
| 114 | int need5V; | ||
| 115 | |||
| 116 | local_irq_save(flags); | ||
| 117 | |||
| 118 | need5V = ((state->Vcc == 50) || (state->Vpp == 50)); | ||
| 119 | |||
| 120 | badge4_set_5V(BADGE4_5V_PCMCIA_SOCK(skt->nr), need5V); | ||
| 121 | |||
| 122 | local_irq_restore(flags); | ||
| 123 | } | ||
| 124 | |||
| 125 | return 0; | ||
| 126 | } | ||
| 127 | |||
| 128 | static struct pcmcia_low_level badge4_pcmcia_ops = { | ||
| 129 | .owner = THIS_MODULE, | ||
| 130 | .configure_socket = badge4_pcmcia_configure_socket, | ||
| 131 | .socket_init = sa1111_pcmcia_socket_init, | ||
| 132 | .first = 0, | ||
| 133 | .nr = 2, | ||
| 134 | }; | ||
| 135 | |||
| 136 | int pcmcia_badge4_init(struct device *dev) | ||
| 137 | { | ||
| 138 | int ret = -ENODEV; | ||
| 139 | |||
| 140 | if (machine_is_badge4()) { | ||
| 141 | printk(KERN_INFO | ||
| 142 | "%s: badge4_pcmvcc=%d, badge4_pcmvpp=%d, badge4_cfvcc=%d\n", | ||
| 143 | __func__, | ||
| 144 | badge4_pcmvcc, badge4_pcmvpp, badge4_cfvcc); | ||
| 145 | |||
| 146 | sa11xx_drv_pcmcia_ops(&badge4_pcmcia_ops); | ||
| 147 | ret = sa1111_pcmcia_add(dev, &badge4_pcmcia_ops, | ||
| 148 | sa11xx_drv_pcmcia_add_one); | ||
| 149 | } | ||
| 150 | |||
| 151 | return ret; | ||
| 152 | } | ||
| 153 | |||
| 154 | static int __init pcmv_setup(char *s) | ||
| 155 | { | ||
| 156 | int v[4]; | ||
| 157 | |||
| 158 | s = get_options(s, ARRAY_SIZE(v), v); | ||
| 159 | |||
| 160 | if (v[0] >= 1) badge4_pcmvcc = v[1]; | ||
| 161 | if (v[0] >= 2) badge4_pcmvpp = v[2]; | ||
| 162 | if (v[0] >= 3) badge4_cfvcc = v[3]; | ||
| 163 | |||
| 164 | return 1; | ||
| 165 | } | ||
| 166 | |||
| 167 | __setup("pcmv=", pcmv_setup); | ||
diff --git a/drivers/pcmcia/sa1100_jornada720.c b/drivers/pcmcia/sa1100_jornada720.c new file mode 100644 index 00000000000..6bcabee6bde --- /dev/null +++ b/drivers/pcmcia/sa1100_jornada720.c | |||
| @@ -0,0 +1,120 @@ | |||
| 1 | /* | ||
| 2 | * drivers/pcmcia/sa1100_jornada720.c | ||
| 3 | * | ||
| 4 | * Jornada720 PCMCIA specific routines | ||
| 5 | * | ||
| 6 | */ | ||
| 7 | #include <linux/module.h> | ||
| 8 | #include <linux/kernel.h> | ||
| 9 | #include <linux/device.h> | ||
| 10 | #include <linux/errno.h> | ||
| 11 | #include <linux/init.h> | ||
| 12 | |||
| 13 | #include <mach/hardware.h> | ||
| 14 | #include <asm/hardware/sa1111.h> | ||
| 15 | #include <asm/mach-types.h> | ||
| 16 | |||
| 17 | #include "sa1111_generic.h" | ||
| 18 | |||
| 19 | /* Does SOCKET1_3V actually do anything? */ | ||
| 20 | #define SOCKET0_POWER GPIO_GPIO0 | ||
| 21 | #define SOCKET0_3V GPIO_GPIO2 | ||
| 22 | #define SOCKET1_POWER (GPIO_GPIO1 | GPIO_GPIO3) | ||
| 23 | #define SOCKET1_3V GPIO_GPIO3 | ||
| 24 | |||
| 25 | static int | ||
| 26 | jornada720_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_t *state) | ||
| 27 | { | ||
| 28 | struct sa1111_pcmcia_socket *s = to_skt(skt); | ||
| 29 | unsigned int pa_dwr_mask, pa_dwr_set; | ||
| 30 | int ret; | ||
| 31 | |||
| 32 | printk(KERN_INFO "%s(): config socket %d vcc %d vpp %d\n", __func__, | ||
| 33 | skt->nr, state->Vcc, state->Vpp); | ||
| 34 | |||
| 35 | switch (skt->nr) { | ||
| 36 | case 0: | ||
| 37 | pa_dwr_mask = SOCKET0_POWER | SOCKET0_3V; | ||
| 38 | |||
| 39 | switch (state->Vcc) { | ||
| 40 | default: | ||
| 41 | case 0: | ||
| 42 | pa_dwr_set = 0; | ||
| 43 | break; | ||
| 44 | case 33: | ||
| 45 | pa_dwr_set = SOCKET0_POWER | SOCKET0_3V; | ||
| 46 | break; | ||
| 47 | case 50: | ||
| 48 | pa_dwr_set = SOCKET0_POWER; | ||
| 49 | break; | ||
| 50 | } | ||
| 51 | break; | ||
| 52 | |||
| 53 | case 1: | ||
| 54 | pa_dwr_mask = SOCKET1_POWER; | ||
| 55 | |||
| 56 | switch (state->Vcc) { | ||
| 57 | default: | ||
| 58 | case 0: | ||
| 59 | pa_dwr_set = 0; | ||
| 60 | break; | ||
| 61 | case 33: | ||
| 62 | pa_dwr_set = SOCKET1_POWER; | ||
| 63 | break; | ||
| 64 | case 50: | ||
| 65 | pa_dwr_set = SOCKET1_POWER; | ||
| 66 | break; | ||
| 67 | } | ||
| 68 | break; | ||
| 69 | |||
| 70 | default: | ||
| 71 | return -1; | ||
| 72 | } | ||
| 73 | |||
| 74 | if (state->Vpp != state->Vcc && state->Vpp != 0) { | ||
| 75 | printk(KERN_ERR "%s(): slot cannot support VPP %u\n", | ||
| 76 | __func__, state->Vpp); | ||
| 77 | return -EPERM; | ||
| 78 | } | ||
| 79 | |||
| 80 | ret = sa1111_pcmcia_configure_socket(skt, state); | ||
| 81 | if (ret == 0) { | ||
| 82 | unsigned long flags; | ||
| 83 | |||
| 84 | local_irq_save(flags); | ||
| 85 | sa1111_set_io(s->dev, pa_dwr_mask, pa_dwr_set); | ||
| 86 | local_irq_restore(flags); | ||
| 87 | } | ||
| 88 | |||
| 89 | return ret; | ||
| 90 | } | ||
| 91 | |||
| 92 | static struct pcmcia_low_level jornada720_pcmcia_ops = { | ||
| 93 | .owner = THIS_MODULE, | ||
| 94 | .configure_socket = jornada720_pcmcia_configure_socket, | ||
| 95 | .socket_init = sa1111_pcmcia_socket_init, | ||
| 96 | .first = 0, | ||
| 97 | .nr = 2, | ||
| 98 | }; | ||
| 99 | |||
| 100 | int __devinit pcmcia_jornada720_init(struct device *dev) | ||
| 101 | { | ||
| 102 | int ret = -ENODEV; | ||
| 103 | |||
| 104 | if (machine_is_jornada720()) { | ||
| 105 | unsigned int pin = GPIO_A0 | GPIO_A1 | GPIO_A2 | GPIO_A3; | ||
| 106 | |||
| 107 | GRER |= 0x00000002; | ||
| 108 | |||
| 109 | /* Set GPIO_A<3:1> to be outputs for PCMCIA/CF power controller: */ | ||
| 110 | sa1111_set_io_dir(dev, pin, 0, 0); | ||
| 111 | sa1111_set_io(dev, pin, 0); | ||
| 112 | sa1111_set_sleep_io(dev, pin, 0); | ||
| 113 | |||
| 114 | sa11xx_drv_pcmcia_ops(&jornada720_pcmcia_ops); | ||
| 115 | ret = sa1111_pcmcia_add(dev, &jornada720_pcmcia_ops, | ||
| 116 | sa11xx_drv_pcmcia_add_one); | ||
| 117 | } | ||
| 118 | |||
| 119 | return ret; | ||
| 120 | } | ||
diff --git a/drivers/pcmcia/sa1100_neponset.c b/drivers/pcmcia/sa1100_neponset.c new file mode 100644 index 00000000000..c95639b5f2a --- /dev/null +++ b/drivers/pcmcia/sa1100_neponset.c | |||
| @@ -0,0 +1,143 @@ | |||
| 1 | /* | ||
| 2 | * linux/drivers/pcmcia/sa1100_neponset.c | ||
| 3 | * | ||
| 4 | * Neponset PCMCIA specific routines | ||
| 5 | */ | ||
| 6 | #include <linux/module.h> | ||
| 7 | #include <linux/kernel.h> | ||
| 8 | #include <linux/device.h> | ||
| 9 | #include <linux/errno.h> | ||
| 10 | #include <linux/init.h> | ||
| 11 | |||
| 12 | #include <mach/hardware.h> | ||
| 13 | #include <asm/mach-types.h> | ||
| 14 | #include <mach/neponset.h> | ||
| 15 | #include <asm/hardware/sa1111.h> | ||
| 16 | |||
| 17 | #include "sa1111_generic.h" | ||
| 18 | |||
| 19 | /* | ||
| 20 | * Neponset uses the Maxim MAX1600, with the following connections: | ||
| 21 | * | ||
| 22 | * MAX1600 Neponset | ||
| 23 | * | ||
| 24 | * A0VCC SA-1111 GPIO A<1> | ||
| 25 | * A1VCC SA-1111 GPIO A<0> | ||
| 26 | * A0VPP CPLD NCR A0VPP | ||
| 27 | * A1VPP CPLD NCR A1VPP | ||
| 28 | * B0VCC SA-1111 GPIO A<2> | ||
| 29 | * B1VCC SA-1111 GPIO A<3> | ||
| 30 | * B0VPP ground (slot B is CF) | ||
| 31 | * B1VPP ground (slot B is CF) | ||
| 32 | * | ||
| 33 | * VX VCC (5V) | ||
| 34 | * VY VCC3_3 (3.3V) | ||
| 35 | * 12INA 12V | ||
| 36 | * 12INB ground (slot B is CF) | ||
| 37 | * | ||
| 38 | * The MAX1600 CODE pin is tied to ground, placing the device in | ||
| 39 | * "Standard Intel code" mode. Refer to the Maxim data sheet for | ||
| 40 | * the corresponding truth table. | ||
| 41 | */ | ||
| 42 | |||
| 43 | static int | ||
| 44 | neponset_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_t *state) | ||
| 45 | { | ||
| 46 | struct sa1111_pcmcia_socket *s = to_skt(skt); | ||
| 47 | unsigned int ncr_mask, ncr_set, pa_dwr_mask, pa_dwr_set; | ||
| 48 | int ret; | ||
| 49 | |||
| 50 | switch (skt->nr) { | ||
| 51 | case 0: | ||
| 52 | pa_dwr_mask = GPIO_A0 | GPIO_A1; | ||
| 53 | ncr_mask = NCR_A0VPP | NCR_A1VPP; | ||
| 54 | |||
| 55 | if (state->Vpp == 0) | ||
| 56 | ncr_set = 0; | ||
| 57 | else if (state->Vpp == 120) | ||
| 58 | ncr_set = NCR_A1VPP; | ||
| 59 | else if (state->Vpp == state->Vcc) | ||
| 60 | ncr_set = NCR_A0VPP; | ||
| 61 | else { | ||
| 62 | printk(KERN_ERR "%s(): unrecognized VPP %u\n", | ||
| 63 | __func__, state->Vpp); | ||
| 64 | return -1; | ||
| 65 | } | ||
| 66 | break; | ||
| 67 | |||
| 68 | case 1: | ||
| 69 | pa_dwr_mask = GPIO_A2 | GPIO_A3; | ||
| 70 | ncr_mask = 0; | ||
| 71 | ncr_set = 0; | ||
| 72 | |||
| 73 | if (state->Vpp != state->Vcc && state->Vpp != 0) { | ||
| 74 | printk(KERN_ERR "%s(): CF slot cannot support VPP %u\n", | ||
| 75 | __func__, state->Vpp); | ||
| 76 | return -1; | ||
| 77 | } | ||
| 78 | break; | ||
| 79 | |||
| 80 | default: | ||
| 81 | return -1; | ||
| 82 | } | ||
| 83 | |||
| 84 | /* | ||
| 85 | * pa_dwr_set is the mask for selecting Vcc on both sockets. | ||
| 86 | * pa_dwr_mask selects which bits (and therefore socket) we change. | ||
| 87 | */ | ||
| 88 | switch (state->Vcc) { | ||
| 89 | default: | ||
| 90 | case 0: pa_dwr_set = 0; break; | ||
| 91 | case 33: pa_dwr_set = GPIO_A1|GPIO_A2; break; | ||
| 92 | case 50: pa_dwr_set = GPIO_A0|GPIO_A3; break; | ||
| 93 | } | ||
| 94 | |||
| 95 | ret = sa1111_pcmcia_configure_socket(skt, state); | ||
| 96 | if (ret == 0) { | ||
| 97 | unsigned long flags; | ||
| 98 | |||
| 99 | local_irq_save(flags); | ||
| 100 | NCR_0 = (NCR_0 & ~ncr_mask) | ncr_set; | ||
| 101 | |||
| 102 | local_irq_restore(flags); | ||
| 103 | sa1111_set_io(s->dev, pa_dwr_mask, pa_dwr_set); | ||
| 104 | } | ||
| 105 | |||
| 106 | return 0; | ||
| 107 | } | ||
| 108 | |||
| 109 | static void neponset_pcmcia_socket_init(struct soc_pcmcia_socket *skt) | ||
| 110 | { | ||
| 111 | if (skt->nr == 0) | ||
| 112 | NCR_0 &= ~(NCR_A0VPP | NCR_A1VPP); | ||
| 113 | |||
| 114 | sa1111_pcmcia_socket_init(skt); | ||
| 115 | } | ||
| 116 | |||
| 117 | static struct pcmcia_low_level neponset_pcmcia_ops = { | ||
| 118 | .owner = THIS_MODULE, | ||
| 119 | .configure_socket = neponset_pcmcia_configure_socket, | ||
| 120 | .socket_init = neponset_pcmcia_socket_init, | ||
| 121 | .first = 0, | ||
| 122 | .nr = 2, | ||
| 123 | }; | ||
| 124 | |||
| 125 | int pcmcia_neponset_init(struct sa1111_dev *sadev) | ||
| 126 | { | ||
| 127 | int ret = -ENODEV; | ||
| 128 | |||
| 129 | if (machine_is_assabet()) { | ||
| 130 | /* | ||
| 131 | * Set GPIO_A<3:0> to be outputs for the MAX1600, | ||
| 132 | * and switch to standby mode. | ||
| 133 | */ | ||
| 134 | sa1111_set_io_dir(sadev, GPIO_A0|GPIO_A1|GPIO_A2|GPIO_A3, 0, 0); | ||
| 135 | sa1111_set_io(sadev, GPIO_A0|GPIO_A1|GPIO_A2|GPIO_A3, 0); | ||
| 136 | sa1111_set_sleep_io(sadev, GPIO_A0|GPIO_A1|GPIO_A2|GPIO_A3, 0); | ||
| 137 | sa11xx_drv_pcmcia_ops(&neponset_pcmcia_ops); | ||
| 138 | ret = sa1111_pcmcia_add(sadev, &neponset_pcmcia_ops, | ||
| 139 | sa11xx_drv_pcmcia_add_one); | ||
| 140 | } | ||
| 141 | |||
| 142 | return ret; | ||
| 143 | } | ||
