📄 pxa.c
字号:
conf.sock = sock; conf.vcc = 0; conf.vpp = 0; conf.output = 0; conf.speaker = 0; conf.reset = 1; ret = pcmcia_low_level->configure_socket(&conf); if (ret == 0) pxa_pcmcia_socket[sock].cs_state = dead_socket; return ret;}/* pxa_pcmcia_events() * ^^^^^^^^^^^^^^^^^^^^^^ * Helper routine to generate a Card Services event mask based on * state information obtained from the kernel low-level PCMCIA layer * in a recent (and previous) sampling. Updates `prev_state'. * * Returns: an event mask for the given socket state. */static inline unsigned pxa_pcmcia_events(struct pcmcia_state *state, struct pcmcia_state *prev_state, unsigned int mask, unsigned int flags){ unsigned int events=0; if(state->detect!=prev_state->detect){ DEBUG(2, "%s(): card detect value %u\n", __FUNCTION__, state->detect); events|=mask&SS_DETECT; } if(state->ready!=prev_state->ready){ DEBUG(2, "%s(): card ready value %u\n", __FUNCTION__, state->ready); events|=mask&((flags&SS_IOCARD)?0:SS_READY); } if(state->bvd1!=prev_state->bvd1){ DEBUG(2, "%s(): card BVD1 value %u\n", __FUNCTION__, state->bvd1); events|=mask&(flags&SS_IOCARD)?SS_STSCHG:SS_BATDEAD; } if(state->bvd2!=prev_state->bvd2){ DEBUG(2, "%s(): card BVD2 value %u\n", __FUNCTION__, state->bvd2); events|=mask&(flags&SS_IOCARD)?0:SS_BATWARN; } DEBUG(2, "events: %s%s%s%s%s%s\n", (events==0)?"<NONE>":"", (events&SS_DETECT)?"DETECT ":"", (events&SS_READY)?"READY ":"", (events&SS_BATDEAD)?"BATDEAD ":"", (events&SS_BATWARN)?"BATWARN ":"", (events&SS_STSCHG)?"STSCHG ":""); *prev_state=*state; return events;} /* pxa_pcmcia_events() *//* pxa_pcmcia_task_handler() * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * Processes serviceable socket events using the "eventd" thread context. * * Event processing (specifically, the invocation of the Card Services event * callback) occurs in this thread rather than in the actual interrupt * handler due to the use of scheduling operations in the PCMCIA core. */static void pxa_pcmcia_task_handler(void *data) { struct pcmcia_state state[PXA_PCMCIA_MAX_SOCK]; struct pcmcia_state_array state_array; int i, events, all_events, irq_status; DEBUG(2, "%s(): entering PCMCIA monitoring thread\n", __FUNCTION__); state_array.size=pxa_pcmcia_socket_count; state_array.state=state; do { DEBUG(3, "%s(): interrogating low-level PCMCIA service\n", __FUNCTION__); if((irq_status=pcmcia_low_level->socket_state(&state_array))<0) printk(KERN_ERR "Error in kernel low-level PCMCIA service.\n"); all_events=0; if(irq_status>0){ for(i=0; i<state_array.size; ++i, all_events|=events) if((events= pxa_pcmcia_events(&state[i], &pxa_pcmcia_socket[i].k_state, pxa_pcmcia_socket[i].cs_state.csc_mask, pxa_pcmcia_socket[i].cs_state.flags))) if(pxa_pcmcia_socket[i].handler!=NULL) pxa_pcmcia_socket[i].handler(pxa_pcmcia_socket[i].handler_info, events); } } while(all_events);} /* pxa_pcmcia_task_handler() */static struct tq_struct pxa_pcmcia_task = { routine: pxa_pcmcia_task_handler};/* pxa_pcmcia_poll_event() * ^^^^^^^^^^^^^^^^^^^^^^^^^^ * Let's poll for events in addition to IRQs since IRQ only is unreliable... */static void pxa_pcmcia_poll_event(unsigned long dummy){ DEBUG(3, "%s(): polling for events\n", __FUNCTION__); poll_timer.function = pxa_pcmcia_poll_event; poll_timer.expires = jiffies + PXA_PCMCIA_POLL_PERIOD; add_timer(&poll_timer); schedule_task(&pxa_pcmcia_task);}/* pxa_pcmcia_interrupt() * ^^^^^^^^^^^^^^^^^^^^^^^^^ * Service routine for socket driver interrupts (requested by the * low-level PCMCIA init() operation via pxa_pcmcia_thread()). * The actual interrupt-servicing work is performed by * pxa_pcmcia_thread(), largely because the Card Services event- * handling code performs scheduling operations which cannot be * executed from within an interrupt context. */static void pxa_pcmcia_interrupt(int irq, void *dev, struct pt_regs *regs){ DEBUG(3, "%s(): servicing IRQ %d\n", __FUNCTION__, irq); schedule_task(&pxa_pcmcia_task);}/* pxa_pcmcia_register_callback() * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * Implements the register_callback() operation for the in-kernel * PCMCIA service (formerly SS_RegisterCallback in Card Services). If * the function pointer `handler' is not NULL, remember the callback * location in the state for `sock', and increment the usage counter * for the driver module. (The callback is invoked from the interrupt * service routine, pxa_pcmcia_interrupt(), to notify Card Services * of interesting events.) Otherwise, clear the callback pointer in the * socket state and decrement the module usage count. * * Returns: 0 */static int pxa_pcmcia_register_callback(unsigned int sock, void (*handler)(void *, unsigned int), void *info){ if(handler==NULL){ pxa_pcmcia_socket[sock].handler=NULL; MOD_DEC_USE_COUNT; } else { MOD_INC_USE_COUNT; pxa_pcmcia_socket[sock].handler=handler; pxa_pcmcia_socket[sock].handler_info=info; } return 0;}/* pxa_pcmcia_inquire_socket() * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * Implements the inquire_socket() operation for the in-kernel PCMCIA * service (formerly SS_InquireSocket in Card Services). Of note is * the setting of the SS_CAP_PAGE_REGS bit in the `features' field of * `cap' to "trick" Card Services into tolerating large "I/O memory" * addresses. Also set is SS_CAP_STATIC_MAP, which disables the memory * resource database check. (Mapped memory is set up within the socket * driver itself.) * * In conjunction with the STATIC_MAP capability is a new field, * `io_offset', recommended by David Hinds. Rather than go through * the SetIOMap interface (which is not quite suited for communicating * window locations up from the socket driver), we just pass up * an offset which is applied to client-requested base I/O addresses * in alloc_io_space(). * * Returns: 0 on success, -1 if no pin has been configured for `sock' */static int pxa_pcmcia_inquire_socket(unsigned int sock, socket_cap_t *cap){ struct pcmcia_irq_info irq_info; DEBUG(3, "%s() for sock %u\n", __FUNCTION__, sock); if(sock>=pxa_pcmcia_socket_count){ printk(KERN_ERR "pxa_cs: socket %u not configured\n", sock); return -1; } /* SS_CAP_PAGE_REGS: used by setup_cis_mem() in cistpl.c to set the * force_low argument to validate_mem() in rsrc_mgr.c -- since in * general, the mapped * addresses of the PCMCIA memory regions * will not be within 0xffff, setting force_low would be * undesirable. * * SS_CAP_STATIC_MAP: don't bother with the (user-configured) memory * resource database; we instead pass up physical address ranges * and allow other parts of Card Services to deal with remapping. * * SS_CAP_PCCARD: we can deal with 16-bit PCMCIA & CF cards, but * not 32-bit CardBus devices. */ cap->features=(SS_CAP_PAGE_REGS | SS_CAP_STATIC_MAP | SS_CAP_PCCARD); irq_info.sock=sock; irq_info.irq=-1; if(pcmcia_low_level->get_irq_info(&irq_info)<0){ printk(KERN_ERR "Error obtaining IRQ info from kernel for socket %u\n", sock); return -1; } cap->irq_mask=0; cap->map_size=PAGE_SIZE; cap->pci_irq=irq_info.irq; cap->io_offset=pxa_pcmcia_socket[sock].virt_io; return 0;} /* pxa_pcmcia_inquire_socket() *//* pxa_pcmcia_get_status() * ^^^^^^^^^^^^^^^^^^^^^^^^^^ * Implements the get_status() operation for the in-kernel PCMCIA * service (formerly SS_GetStatus in Card Services). Essentially just * fills in bits in `status' according to internal driver state or * the value of the voltage detect chipselect register. * * As a debugging note, during card startup, the PCMCIA core issues * three set_socket() commands in a row the first with RESET deasserted, * the second with RESET asserted, and the last with RESET deasserted * again. Following the third set_socket(), a get_status() command will * be issued. The kernel is looking for the SS_READY flag (see * setup_socket(), reset_socket(), and unreset_socket() in cs.c). * * Returns: 0 */static int pxa_pcmcia_get_status(unsigned int sock, unsigned int *status){ struct pcmcia_state state[PXA_PCMCIA_MAX_SOCK]; struct pcmcia_state_array state_array; DEBUG(3, "%s() for sock %u\n", __FUNCTION__, sock); state_array.size=pxa_pcmcia_socket_count; state_array.state=state; if((pcmcia_low_level->socket_state(&state_array))<0){ printk(KERN_ERR "Unable to get PCMCIA status from kernel.\n"); return -1; } pxa_pcmcia_socket[sock].k_state=state[sock]; *status=state[sock].detect?SS_DETECT:0; *status|=state[sock].ready?SS_READY:0; /* The power status of individual sockets is not available * explicitly from the hardware, so we just remember the state * and regurgitate it upon request: */ *status|=pxa_pcmcia_socket[sock].cs_state.Vcc?SS_POWERON:0; if(pxa_pcmcia_socket[sock].cs_state.flags&SS_IOCARD) *status|=state[sock].bvd1?SS_STSCHG:0; else { if(state[sock].bvd1==0) *status|=SS_BATDEAD; else if(state[sock].bvd2==0) *status|=SS_BATWARN; } *status|=state[sock].vs_3v?SS_3VCARD:0; *status|=state[sock].vs_Xv?SS_XVCARD:0; DEBUG(3, "\tstatus: %s%s%s%s%s%s%s%s\n", (*status&SS_DETECT)?"DETECT ":"", (*status&SS_READY)?"READY ":"", (*status&SS_BATDEAD)?"BATDEAD ":"", (*status&SS_BATWARN)?"BATWARN ":"", (*status&SS_POWERON)?"POWERON ":"", (*status&SS_STSCHG)?"STSCHG ":"", (*status&SS_3VCARD)?"3VCARD ":"", (*status&SS_XVCARD)?"XVCARD ":""); return 0;} /* pxa_pcmcia_get_status() *//* pxa_pcmcia_get_socket() * ^^^^^^^^^^^^^^^^^^^^^^^^^^ * Implements the get_socket() operation for the in-kernel PCMCIA * service (formerly SS_GetSocket in Card Services). Not a very * exciting routine. * * Returns: 0 */static int pxa_pcmcia_get_socket(unsigned int sock, socket_state_t *state){ DEBUG(3, "%s() for sock %u\n", __FUNCTION__, sock); /* This information was given to us in an earlier call to set_socket(), * so we're just regurgitating it here: */ *state=pxa_pcmcia_socket[sock].cs_state; return 0;}/* pxa_pcmcia_set_socket() * ^^^^^^^^^^^^^^^^^^^^^^^^^^ * Implements the set_socket() operation for the in-kernel PCMCIA * service (formerly SS_SetSocket in Card Services). We more or * less punt all of this work and let the kernel handle the details * of power configuration, reset, &c. We also record the value of * `state' in order to regurgitate it to the PCMCIA core later. * * Returns: 0 */static int pxa_pcmcia_set_socket(unsigned int sock, socket_state_t *state){ struct pcmcia_configure configure; DEBUG(3, "%s() for sock %u\n", __FUNCTION__, sock); DEBUG(3, "\tmask: %s%s%s%s%s%s\n\tflags: %s%s%s%s%s%s\n" "\tVcc %d Vpp %d irq %d\n", (state->csc_mask==0)?"<NONE>":"", (state->csc_mask&SS_DETECT)?"DETECT ":"", (state->csc_mask&SS_READY)?"READY ":"", (state->csc_mask&SS_BATDEAD)?"BATDEAD ":"", (state->csc_mask&SS_BATWARN)?"BATWARN ":"", (state->csc_mask&SS_STSCHG)?"STSCHG ":"", (state->flags==0)?"<NONE>":"", (state->flags&SS_PWR_AUTO)?"PWR_AUTO ":"", (state->flags&SS_IOCARD)?"IOCARD ":"", (state->flags&SS_RESET)?"RESET ":"", (state->flags&SS_SPKR_ENA)?"SPKR_ENA ":"", (state->flags&SS_OUTPUT_ENA)?"OUTPUT_ENA ":"", state->Vcc, state->Vpp, state->io_irq); configure.sock=sock; configure.vcc=state->Vcc; configure.vpp=state->Vpp; configure.output=(state->flags&SS_OUTPUT_ENA)?1:0; configure.speaker=(state->flags&SS_SPKR_ENA)?1:0; configure.reset=(state->flags&SS_RESET)?1:0; if(pcmcia_low_level->configure_socket(&configure)<0){ printk(KERN_ERR "Unable to configure socket %u\n", sock); return -1; } pxa_pcmcia_socket[sock].cs_state=*state; return 0;} /* pxa_pcmcia_set_socket() *//* pxa_pcmcia_get_io_map() * ^^^^^^^^^^^^^^^^^^^^^^^^^^ * Implements the get_io_map() operation for the in-kernel PCMCIA * service (formerly SS_GetIOMap in Card Services). Just returns an * I/O map descriptor which was assigned earlier by a set_io_map(). * * Returns: 0 on success, -1 if the map index was out of range */static int pxa_pcmcia_get_io_map(unsigned int sock, struct pccard_io_map *map){ DEBUG(4, "%s() for sock %u\n", __FUNCTION__, sock); if(map->map>=MAX_IO_WIN){ printk(KERN_ERR "%s(): map (%d) out of range\n", __FUNCTION__, map->map); return -1; } *map=pxa_pcmcia_socket[sock].io_map[map->map]; return 0;}/* 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.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -