/* pxa_pcmcia_driver_init() * ^^^^^^^^^^^^^^^^^^^^^^^^^^^ * * This routine performs a basic sanity check to ensure that this * kernel has been built with the appropriate board-specific low-level * PCMCIA support, performs low-level PCMCIA initialization, registers * this socket driver with Card Services, and then spawns the daemon * thread which is the real workhorse of the socket driver. * * Please see linux/Documentation/arm/SA1100/PCMCIA for more information * on the low-level kernel interface. * * Returns: 0 on success, -1 on error */ static int __init pxa_pcmcia_driver_init(void){ servinfo_t info; struct pcmcia_init pcmcia_init; struct pcmcia_state state[PXA_PCMCIA_MAX_SOCK]; struct pcmcia_state_array state_array; unsigned int i, clock; unsigned long mecr; printk(KERN_INFO "Intel PXA250/210 PCMCIA (CS release %s)\n", CS_RELEASE); CardServices(GetCardServicesInfo, &info); if(info.Revision!=CS_RELEASE_CODE){ printk(KERN_ERR "Card Services release codes do not match\n"); return -1; } /* Setup GPIOs for PCMCIA/CF alternate function mode. * * It would be nice if set_GPIO_mode included support * for driving GPIO outputs to default high/low state * before programming GPIOs as outputs. Setting GPIO * outputs to default high/low state via GPSR/GPCR * before defining them as outputs should reduce * the possibility of glitching outputs during GPIO * setup. This of course assumes external terminators * are present to hold GPIOs in a defined state. * * In the meantime, setup default state of GPIO * outputs before we enable them as outputs. */ GPSR(GPIO48_nPOE) = GPIO_bit(GPIO48_nPOE) | GPIO_bit(GPIO49_nPWE) | GPIO_bit(GPIO50_nPIOR) | GPIO_bit(GPIO51_nPIOW) | GPIO_bit(GPIO52_nPCE_1) | GPIO_bit(GPIO53_nPCE_2); set_GPIO_mode(GPIO48_nPOE_MD); set_GPIO_mode(GPIO49_nPWE_MD); set_GPIO_mode(GPIO50_nPIOR_MD); set_GPIO_mode(GPIO51_nPIOW_MD); set_GPIO_mode(GPIO52_nPCE_1_MD); set_GPIO_mode(GPIO53_nPCE_2_MD); set_GPIO_mode(GPIO54_pSKTSEL_MD); /* REVISIT: s/b dependent on num sockets */ set_GPIO_mode(GPIO55_nPREG_MD); set_GPIO_mode(GPIO56_nPWAIT_MD); set_GPIO_mode(GPIO57_nIOIS16_MD); if(machine_is_lubbock()){ #ifdef CONFIG_ARCH_LUBBOCK pcmcia_low_level=&lubbock_pcmcia_ops; #endif } else if (machine_is_pxa_idp()) { pcmcia_low_level=&pxa_idp_pcmcia_ops; } else if( machine_is_pxa_cerf()){ pcmcia_low_level=&cerf_pcmcia_ops; } if (!pcmcia_low_level) { printk(KERN_ERR "This hardware is not supported by the PXA250/210 Card Service driver\n"); return -ENODEV; } pcmcia_init.handler=pxa_pcmcia_interrupt; if((pxa_pcmcia_socket_count=pcmcia_low_level->init(&pcmcia_init))<0){ printk(KERN_ERR "Unable to initialize kernel PCMCIA service.\n"); return -EIO; } state_array.size=pxa_pcmcia_socket_count; state_array.state=state; /* Configure MECR based on the number of sockets present. */ if (pxa_pcmcia_socket_count == 2) { MECR |= GPIO_bit(0); } else { MECR &= ~GPIO_bit(0); } if(pcmcia_low_level->socket_state(&state_array)<0){ printk(KERN_ERR "Unable to get PCMCIA status from kernel.\n"); return -EIO; } /* Well, it looks good to go. So we can now enable the PCMCIA * controller. */ MECR |= GPIO_bit(1); /* We need to initialize the MCXX registers to default values * here because we're not guaranteed to see a SetIOMap operation * at runtime. */ clock = get_lclk_frequency_10khz(); for(i=0; i<pxa_pcmcia_socket_count; ++i){ pxa_pcmcia_socket[i].k_state=state[i]; /* This is an interim fix. Apparently, SetSocket is no longer * called to initialize each socket (prior to the first detect * event). For now, we'll just manually set up the mask. */ pxa_pcmcia_socket[i].cs_state.csc_mask=SS_DETECT; pxa_pcmcia_socket[i].virt_io=(i==0)?PCMCIA_IO_0_BASE:PCMCIA_IO_1_BASE; pxa_pcmcia_socket[i].phys_attr=_PCMCIAAttr(i); pxa_pcmcia_socket[i].phys_mem=_PCMCIAMem(i); /* REVISIT: cleanup these macros */ //MCIO_SET(i, PXA_PCMCIA_IO_ACCESS, clock); //MCATTR_SET(i, PXA_PCMCIA_5V_MEM_ACCESS, clock); //MCMEM_SET(i, PXA_PCMCIA_5V_MEM_ACCESS, clock); pxa_pcmcia_socket[i].speed_io=PXA_PCMCIA_IO_ACCESS; pxa_pcmcia_socket[i].speed_attr=PXA_PCMCIA_5V_MEM_ACCESS; pxa_pcmcia_socket[i].speed_mem=PXA_PCMCIA_5V_MEM_ACCESS; } /* REVISIT: cleanup these macros */ MCMEM0 = ((pxa_mcxx_setup(PXA_PCMCIA_5V_MEM_ACCESS, clock) & MCXX_SETUP_MASK) << MCXX_SETUP_SHIFT) | ((pxa_mcxx_asst(PXA_PCMCIA_5V_MEM_ACCESS, clock) & MCXX_ASST_MASK) << MCXX_ASST_SHIFT) | ((pxa_mcxx_hold(PXA_PCMCIA_5V_MEM_ACCESS, clock) & MCXX_HOLD_MASK) << MCXX_HOLD_SHIFT); MCMEM1 = ((pxa_mcxx_setup(PXA_PCMCIA_5V_MEM_ACCESS, clock) & MCXX_SETUP_MASK) << MCXX_SETUP_SHIFT) | ((pxa_mcxx_asst(PXA_PCMCIA_5V_MEM_ACCESS, clock) & MCXX_ASST_MASK) << MCXX_ASST_SHIFT) | ((pxa_mcxx_hold(PXA_PCMCIA_5V_MEM_ACCESS, clock) & MCXX_HOLD_MASK) << MCXX_HOLD_SHIFT); MCATT0 = ((pxa_mcxx_setup(PXA_PCMCIA_5V_MEM_ACCESS, clock) & MCXX_SETUP_MASK) << MCXX_SETUP_SHIFT) | ((pxa_mcxx_asst(PXA_PCMCIA_5V_MEM_ACCESS, clock) & MCXX_ASST_MASK) << MCXX_ASST_SHIFT) | ((pxa_mcxx_hold(PXA_PCMCIA_5V_MEM_ACCESS, clock) & MCXX_HOLD_MASK) << MCXX_HOLD_SHIFT); MCATT1 = ((pxa_mcxx_setup(PXA_PCMCIA_5V_MEM_ACCESS, clock) & MCXX_SETUP_MASK) << MCXX_SETUP_SHIFT) | ((pxa_mcxx_asst(PXA_PCMCIA_5V_MEM_ACCESS, clock) & MCXX_ASST_MASK) << MCXX_ASST_SHIFT) | ((pxa_mcxx_hold(PXA_PCMCIA_5V_MEM_ACCESS, clock) & MCXX_HOLD_MASK) << MCXX_HOLD_SHIFT); MCIO0 = ((pxa_mcxx_setup(PXA_PCMCIA_IO_ACCESS, clock) & MCXX_SETUP_MASK) << MCXX_SETUP_SHIFT) | ((pxa_mcxx_asst(PXA_PCMCIA_IO_ACCESS, clock) & MCXX_ASST_MASK) << MCXX_ASST_SHIFT) | ((pxa_mcxx_hold(PXA_PCMCIA_IO_ACCESS, clock) & MCXX_HOLD_MASK) << MCXX_HOLD_SHIFT); MCIO1 = ((pxa_mcxx_setup(PXA_PCMCIA_IO_ACCESS, clock) & MCXX_SETUP_MASK) << MCXX_SETUP_SHIFT) | ((pxa_mcxx_asst(PXA_PCMCIA_IO_ACCESS, clock) & MCXX_ASST_MASK) << MCXX_ASST_SHIFT) | ((pxa_mcxx_hold(PXA_PCMCIA_IO_ACCESS, clock) & MCXX_HOLD_MASK) << MCXX_HOLD_SHIFT); #ifdef CONFIG_CPU_FREQ if(cpufreq_register_notifier(&pxa_pcmcia_notifier_block) < 0){ printk(KERN_ERR "Unable to register CPU frequency change notifier\n"); return -ENXIO; } #endif /* Only advertise as many sockets as we can detect: */ if(register_ss_entry(pxa_pcmcia_socket_count, &pxa_pcmcia_operations)<0){ printk(KERN_ERR "Unable to register socket service routine\n"); return -ENXIO; } /* Start the event poll timer. It will reschedule by itself afterwards. */ pxa_pcmcia_poll_event(0); DEBUG(1, "pxa_cs: initialization complete\n"); return 0; } /* pxa_pcmcia_driver_init() */
/* pxa_pcmcia_set_io_map() * ^^^^^^^^^^^^^^^^^^^^^^^^^^ * Implements the set_io_map() operation for the in-kernel PCMCIA * service (formerly SS_SetIOMap in Card Services). We configure * the map speed as requested, but override the address ranges * supplied by Card Services. * * Returns: 0 on success, -1 on error */ static int pxa_pcmcia_set_io_map(unsigned int sock, struct pccard_io_map *map){ unsigned int clock, speed; unsigned long mecr, start; DEBUG(4, "%s() for sock %u\n", __FUNCTION__, sock); DEBUG(4, "\tmap %u speed %u\n\tstart 0x%08lx stop 0x%08lx\n" "\tflags: %s%s%s%s%s%s%s%s\n", map->map, map->speed, map->start, map->stop, (map->flags==0)?"<NONE>":"", (map->flags&MAP_ACTIVE)?"ACTIVE ":"", (map->flags&MAP_16BIT)?"16BIT ":"", (map->flags&MAP_AUTOSZ)?"AUTOSZ ":"", (map->flags&MAP_0WS)?"0WS ":"", (map->flags&MAP_WRPROT)?"WRPROT ":"", (map->flags&MAP_USE_WAIT)?"USE_WAIT ":"", (map->flags&MAP_PREFETCH)?"PREFETCH ":""); if(map->map>=MAX_IO_WIN){ printk(KERN_ERR "%s(): map (%d) out of range\n", __FUNCTION__, map->map); return -1; } if(map->flags&MAP_ACTIVE){ speed=(map->speed>0)?map->speed:PXA_PCMCIA_IO_ACCESS; clock = get_lclk_frequency_10khz(); pxa_pcmcia_socket[sock].speed_io=speed; if (sock == 0) { MCIO0 = ((pxa_mcxx_setup(speed, clock) & MCXX_SETUP_MASK) << MCXX_SETUP_SHIFT) | ((pxa_mcxx_asst(speed, clock) & MCXX_ASST_MASK) << MCXX_ASST_SHIFT) | ((pxa_mcxx_hold(speed, clock) & MCXX_HOLD_MASK) << MCXX_HOLD_SHIFT); } else { MCIO1 = ((pxa_mcxx_setup(speed, clock) & MCXX_SETUP_MASK) << MCXX_SETUP_SHIFT) | ((pxa_mcxx_asst(speed, clock) & MCXX_ASST_MASK) << MCXX_ASST_SHIFT) | ((pxa_mcxx_hold(speed, clock) & MCXX_HOLD_MASK) << MCXX_HOLD_SHIFT); } DEBUG(4, "%s(): FAST%u %lx BSM%u %lx BSA%u %lx BSIO%u %lx\n", __FUNCTION__, sock, MECR_FAST_GET(mecr, sock), sock, MECR_BSM_GET(mecr, sock), sock, MECR_BSA_GET(mecr, sock), sock, MECR_BSIO_GET(mecr, sock)); } start=map->start; if(map->stop==1) map->stop=PAGE_SIZE-1; map->start=pxa_pcmcia_socket[sock].virt_io; map->stop=map->start+(map->stop-start); pxa_pcmcia_socket[sock].io_map[map->map]=*map; return 0; } /* pxa_pcmcia_set_io_map() */
/* pxa_pcmcia_set_mem_map() * ^^^^^^^^^^^^^^^^^^^^^^^^^^^ * Implements the set_mem_map() operation for the in-kernel PCMCIA * service (formerly SS_SetMemMap in Card Services). We configure * the map speed as requested, but override the address ranges * supplied by Card Services. * * Returns: 0 on success, -1 on error */ static int pxa_pcmcia_set_mem_map(unsigned int sock, struct pccard_mem_map *map){ unsigned int clock, speed; unsigned long mecr, start; DEBUG(4, "%s() for sock %u\n", __FUNCTION__, sock); DEBUG(4, "\tmap %u speed %u\n\tsys_start %#lx\n" "\tsys_stop %#lx\n\tcard_start %#x\n" "\tflags: %s%s%s%s%s%s%s%s\n", map->map, map->speed, map->sys_start, map->sys_stop, map->card_start, (map->flags==0)?"<NONE>":"", (map->flags&MAP_ACTIVE)?"ACTIVE ":"", (map->flags&MAP_16BIT)?"16BIT ":"", (map->flags&MAP_AUTOSZ)?"AUTOSZ ":"", (map->flags&MAP_0WS)?"0WS ":"", (map->flags&MAP_WRPROT)?"WRPROT ":"", (map->flags&MAP_ATTRIB)?"ATTRIB ":"", (map->flags&MAP_USE_WAIT)?"USE_WAIT ":""); if(map->map>=MAX_WIN){ printk(KERN_ERR "%s(): map (%d) out of range\n", __FUNCTION__, map->map); return -1; } if(map->flags&MAP_ACTIVE){ /* When clients issue RequestMap, the access speed is not always * properly configured: */ if(map->speed > 0) speed = map->speed; else switch(pxa_pcmcia_socket[sock].cs_state.Vcc){ case 33: speed = PXA_PCMCIA_3V_MEM_ACCESS; break; default: speed = PXA_PCMCIA_5V_MEM_ACCESS; } clock = get_lclk_frequency_10khz(); if(map->flags&MAP_ATTRIB){ if (sock == 0) { MCATT0 = ((pxa_mcxx_setup(speed, clock) & MCXX_SETUP_MASK) << MCXX_SETUP_SHIFT) | ((pxa_mcxx_asst(speed, clock) & MCXX_ASST_MASK) << MCXX_ASST_SHIFT) | ((pxa_mcxx_hold(speed, clock) & MCXX_HOLD_MASK) << MCXX_HOLD_SHIFT); } else { MCATT1 = ((pxa_mcxx_setup(speed, clock) & MCXX_SETUP_MASK) << MCXX_SETUP_SHIFT) | ((pxa_mcxx_asst(speed, clock) & MCXX_ASST_MASK) << MCXX_ASST_SHIFT) | ((pxa_mcxx_hold(speed, clock) & MCXX_HOLD_MASK) << MCXX_HOLD_SHIFT); } pxa_pcmcia_socket[sock].speed_attr=speed; } else { if (sock == 0) { MCMEM0 = ((pxa_mcxx_setup(speed, clock) & MCXX_SETUP_MASK) << MCXX_SETUP_SHIFT) | ((pxa_mcxx_asst(speed, clock) & MCXX_ASST_MASK) << MCXX_ASST_SHIFT) | ((pxa_mcxx_hold(speed, clock) & MCXX_HOLD_MASK) << MCXX_HOLD_SHIFT); } else { MCMEM1 = ((pxa_mcxx_setup(speed, clock) & MCXX_SETUP_MASK) << MCXX_SETUP_SHIFT) | ((pxa_mcxx_asst(speed, clock) & MCXX_ASST_MASK) << MCXX_ASST_SHIFT) | ((pxa_mcxx_hold(speed, clock) & MCXX_HOLD_MASK) << MCXX_HOLD_SHIFT); } pxa_pcmcia_socket[sock].speed_mem=speed; } DEBUG(4, "%s(): FAST%u %lx BSM%u %lx BSA%u %lx BSIO%u %lx\n", __FUNCTION__, sock, MECR_FAST_GET(mecr, sock), sock, MECR_BSM_GET(mecr, sock), sock, MECR_BSA_GET(mecr, sock), sock, MECR_BSIO_GET(mecr, sock)); } start=map->sys_start; if(map->sys_stop==0) map->sys_stop=PAGE_SIZE-1; map->sys_start=(map->flags & MAP_ATTRIB)?\ pxa_pcmcia_socket[sock].phys_attr:\ pxa_pcmcia_socket[sock].phys_mem; map->sys_stop=map->sys_start+(map->sys_stop-start); pxa_pcmcia_socket[sock].mem_map[map->map]=*map; return 0; } /* pxa_pcmcia_set_mem_map() */