📄 pxa3xx_controller.c
字号:
host->ios.bus_width = host->bus_width;}#ifdef CONFIG_DVFMstatic int pxa3xx_mmc_dvfm_notifier(unsigned cmd, void *client_data, void *info){ struct pxa_mss_host *pxa_host = (struct pxa_mss_host *)client_data; switch (cmd) { case FV_NOTIFIER_QUERY_SET : if (pxa_host->dma_run) return -1; break; case FV_NOTIFIER_PRE_SET : break; case FV_NOTIFIER_POST_SET : break; } return 0;}#endif/* * send command by setting CMDAT reg of MMC/SD/SDIO controller. * Note: to hide info about SD/SDIO. */static void pxa_mss_handle_request(struct mss_host *host, struct mss_ll_request *llreq){ struct pxa_mss_host *pxa_host = host->private; struct mss_cmd *cmd; struct mss_card *card = host->active_card; u32 cmd_dat = 0; dbg("pxammc %d acitive slot %x\n", host->id + 1, (pxa_host->active_slot) ? pxa_host->active_slot->id : -1); if (pxa_host->active_slot != card->slot) { dbg("pxammc %d change acitive slot to %d\n", host->id + 1, card->slot->id); pxa_mss_slot_select(card->slot); pxa_host->active_slot = card->slot; } pxa_host->cmd = llreq->cmd; pxa_host->data = llreq->data; pxa_host->llreq = llreq; cmd = pxa_host->cmd;// printk("====>%s:Read to send CMD :%d, cmd flag 0x%x\n", __FUNCTION__, cmd->opcode, cmd->flags); dbg("Read to send CMD :0x%x, cmd flag 0x%x", cmd->opcode, cmd->flags); if (cmd->flags & MSS_CMD_INIT) cmd_dat |= MMC_CMDAT_INIT; if (cmd->flags & MSS_CMD_STOP) cmd_dat |= MMC_CMDAT_STOP; if (cmd->flags & MSS_CMD_SDIO_EN || host->sdio_int) cmd_dat |= MMC_CMDAT_SDIO_INT; if (llreq->data) { dbg("data flags 0x%x", llreq->data->flags); if (llreq->data->flags & MSS_DATA_WRITE) cmd_dat |= MMC_CMDAT_WRITE; pxa_host_setup_data(pxa_host, llreq->data); cmd_dat |= (MMC_CMDAT_DATA | MMC_CMDAT_DMA); cmd_dat &= ~MMC_CMDAT_BUSY; dbg("data nob %d, block_len %d", llreq->data->blocks, llreq->data->blksz); if (llreq->data->flags & MSS_DATA_STREAM) cmd_dat |= MMC_CMDAT_STREAM; } else { writel(0, pxa_host->base + MMC_NUMBLK); writel(0, pxa_host->base + MMC_BLKLEN); } dbg("cmd rtype %d", cmd->rtype); switch (cmd->rtype) { case MSS_RESPONSE_NONE: cmd_dat |= MMC_CMDAT_RESP_FMT_NONE; break; case MSS_RESPONSE_R1B: cmd_dat |= MMC_CMDAT_BUSY; /* Fall through */ case MSS_RESPONSE_R1: case MSS_RESPONSE_R4: case MSS_RESPONSE_R5: case MSS_RESPONSE_R6: case MSS_RESPONSE_R7: cmd_dat |= MMC_CMDAT_RESP_FMT_R1; break; case MSS_RESPONSE_R2_CID: case MSS_RESPONSE_R2_CSD: cmd_dat |= MMC_CMDAT_RESP_FMT_R2; break; case MSS_RESPONSE_R3: cmd_dat |= MMC_CMDAT_RESP_FMT_R3; break; } dbg("card bus width %d", card->bus_width); if (card->bus_width == MSS_BUSWIDTH_4BIT) cmd_dat |= MMC_CMDAT_4DAT; writel(cmd->opcode, pxa_host->base + MMC_CMD); writel(cmd->arg >> 16, pxa_host->base + MMC_ARGH); writel(cmd->arg & 0xffff, pxa_host->base + MMC_ARGL); writel(cmd_dat, pxa_host->base + MMC_CMDAT); dbg("Read out MMC_CMD 0x%x",readl(pxa_host->base+MMC_CMD)); dbg("Read out MMC_ARGH 0x%x",readl(pxa_host->base+MMC_ARGH)); dbg("Read out MMC_ARGL 0x%x",readl(pxa_host->base+MMC_ARGL)); dbg("Read out MMC_CMDAT 0x%x",readl(pxa_host->base+MMC_CMDAT)); dbg("Read out MMC_CLKRT 0x%x",readl(pxa_host->base+MMC_CLKRT)); dbg("Read out MMC_NUMBLK 0x%x",readl(pxa_host->base+MMC_NUMBLK)); dbg("Read out MMC_BLKSZ 0x%x",readl(pxa_host->base+MMC_BLKLEN)); if (llreq->data && llreq->data->sg_len) { pxa_host->dma_run = 1; DDADR(pxa_host->dma) = pxa_host->sg_dma; /* start DMA */ DCSR(pxa_host->dma) |= DCSR_RUN; dbg("enable dma %d, DDADR: 0x%x, DCSR: 0x%x", pxa_host->dma, DDADR(pxa_host->dma), DCSR(pxa_host->dma)); } pxa_host_enable_int(pxa_host, MMC_I_MASK_END_CMD_RES | MMC_I_MASK_RES_ERR); }static void pxa_mss_enable_sdio_int(struct mss_host *host, int enable){ unsigned long flags; struct pxa_mss_host *pxa_host = host->private; local_irq_save(flags); if (enable) { host->sdio_int = MSS_SDIO_INT_EN; pxa_host_enable_int(pxa_host, MMC_I_MASK_SDIO_INT); } else { host->sdio_int = MSS_SDIO_INT_DIS; pxa_host_disable_int(pxa_host, MMC_I_MASK_SDIO_INT); } local_irq_restore(flags);}/* * DMA irq handler. devid represent pxa_mss_controller_device for dma interrupt * is to pxa_controller */static void pxa_host_dma_irq(int dma, void *devid){ unsigned int dcsr; struct pxa_mss_host *pxa_host; struct mss_card *card; struct mss_cmd *cmd; struct mss_data *data; dbg("DMA IRQ"); pxa_host = (struct pxa_mss_host *)devid; card = pxa_host->host->active_card; data = pxa_host->data; if (!card) { printk(KERN_ERR "Can not find card\n"); BUG(); } cmd = pxa_host->cmd; dcsr = DCSR(dma); DCSR(dma) &= ~DCSR_STOPIRQEN; dbg("dma int dcsr:0x%x, DSADR:0x%x, DCMD:0x%x", dcsr, DSADR(dma), DCMD(dma)); if (dcsr & DCSR_BUSERR) { /* dbg("DCSR_BUSEER"); */ cmd->error = MSS_ERROR_DMA; data->bytes_xfered = 0; pxa_host_disable_int(pxa_host, MMC_I_MASK_DATA_TRAN_DONE | MMC_I_MASK_DAT_ERR | MMC_I_MASK_TINT); pxa_host->llreq->done(pxa_host->llreq); } else if (dcsr & DCSR_STOPSTATE) { if (data && (data->flags & MSS_DATA_WRITE)) writel(BUF_PART_FULL, pxa_host->base + MMC_PRTBUF); if (data && (data->flags & MSS_DATA_WRITE) && cmd->rtype == MSS_RESPONSE_R1B) pxa_host_enable_int(pxa_host, MMC_I_REG_PRG_DONE); /* dbg("sg index:%d, dma_len:%d", pxa_host->sg_idx, pxa_host->dma_len); */ else if (data && (data->flags & (MSS_DATA_WRITE | MSS_DATA_READ))) { pxa_host_enable_int(pxa_host, MMC_I_MASK_DATA_TRAN_DONE | MMC_I_MASK_TINT); } else { printk(KERN_ERR "ERROR in SLOT_DMA_IRQ, neither WRITE" " nor READ or sg_idx exceed dma_len"); dump_stack(); } } pxa_host->dma_run = 0; return; }/* * slot INT routine for commands with R1B response type */static int pxa_host_r1b_irq(struct pxa_mss_host *pxa_host, u32 ireg, u32 stat, u32 mask){ struct mss_host *host; struct mss_cmd *cmd; /* dbg("host%d, I_REG: 0x%x, STAT: 0x%x, MASK: 0x%x\n", pxa_host->host->id, (u32)ireg, (u32)stat, (u32)mask); */ host = pxa_host->host; cmd = pxa_host->cmd; if ((ireg & MMC_I_REG_END_CMD_RES) && (!(mask & MMC_I_MASK_END_CMD_RES)) ) { pxa_host_disable_int(pxa_host, MMC_I_MASK_END_CMD_RES | MMC_I_MASK_RES_ERR); pxa_host_get_response(pxa_host, cmd); if (ireg & MMC_I_REG_RES_ERR) { pxa_host_set_error(cmd, stat); /* dbg("in RES_ERROR, prepare to cmd_complete");*/ pxa_host->llreq->done(pxa_host->llreq); return 1; } pxa_host_enable_int(pxa_host, MMC_I_MASK_PRG_DONE); return 1; } if ((ireg & MMC_I_REG_PRG_DONE) && (!(mask & MMC_I_MASK_PRG_DONE)) ) { pxa_host_disable_int(pxa_host, MMC_I_MASK_PRG_DONE); /* dbg("in PRG_DONE, prepare to cmd_complete\n"); */ pxa_host->llreq->done(pxa_host->llreq); } return 1;}/* * slot INT routine for write commands with data buffer */static int pxa_host_write_irq(struct pxa_mss_host *pxa_host, u32 ireg, u32 stat, u32 mask){ struct mss_host *host; struct mss_cmd *cmd; host = pxa_host->host; cmd = pxa_host->cmd; if ((ireg & MMC_I_REG_END_CMD_RES) && \ (!(mask & MMC_I_MASK_END_CMD_RES)) ) { pxa_host_disable_int(pxa_host, MMC_I_MASK_END_CMD_RES | MMC_I_MASK_RES_ERR); if (ireg & MMC_I_REG_RES_ERR) { pxa_host_set_error(cmd, stat); return 1; } pxa_host_get_response(pxa_host, cmd); return 1; } if ((ireg & MMC_I_REG_TINT) && (!(mask & MMC_I_MASK_TINT))) { stat = readl(pxa_host->base + MMC_STAT); pxa_host_disable_int(pxa_host, \ MMC_I_MASK_DATA_TRAN_DONE | \ MMC_I_MASK_DAT_ERR | \ MMC_I_MASK_TINT); pxa_host_set_error(cmd, stat); /* dbg("DATA/TIMEOUT error. I_REG: 0x%x, STAT: 0x%x, I_MASK: 0x%x. prepare to cmd_complete", MMC_I_REG, MMC_STAT, MMC_I_MASK); */ pxa_host->llreq->done(pxa_host->llreq); return 1; } /* wait for DMA interrupt, DATA WRITE done once DATA_TRAN_DONE * interrupt occurred */ if ((ireg & MMC_I_REG_DATA_TRAN_DONE) && (!(mask & MMC_I_MASK_DATA_TRAN_DONE)) ) { /* Note: must not disable DATA_ERROR interrupt, * for it may be occured again!!! */ pxa_host_disable_int(pxa_host, MMC_I_MASK_DATA_TRAN_DONE | MMC_I_MASK_DAT_ERR | MMC_I_MASK_TINT); pxa_host_enable_int(pxa_host, MMC_I_MASK_PRG_DONE); return 1; } if ((ireg & MMC_I_REG_PRG_DONE) && (!(mask & MMC_I_MASK_PRG_DONE)) ) { /* pxa_host_get_response(pxa_host, cmd); */ pxa_host_disable_int(pxa_host, MMC_I_MASK_PRG_DONE); /* dbg("in PRG_DONE, prepare to cmd_complete"); */ pxa_host->llreq->done(pxa_host->llreq); } return 1;}/** * pxa_slot_read_irq * @card: mss_card_device * @ireg: value of MMCx_I_REG * @stat: value of MMCx_STAT * @mask: value of MMCx_I_MASK * * slot INT routine for read commands with data buffer */static int pxa_host_read_irq(struct pxa_mss_host *pxa_host, u32 ireg, u32 stat, u32 mask){ struct mss_host *host; struct mss_cmd *cmd; host = pxa_host->host; cmd = pxa_host->cmd; if ((ireg & MMC_I_REG_END_CMD_RES) && (!(mask & MMC_I_MASK_END_CMD_RES))) { pxa_host_disable_int(pxa_host, MMC_I_MASK_END_CMD_RES | MMC_I_MASK_RES_ERR); if (ireg & MMC_I_REG_RES_ERR) { pxa_host_set_error(cmd, stat); pxa_host_enable_int(pxa_host, MMC_I_MASK_TINT); return 1; } pxa_host_get_response(pxa_host, cmd); /* DATA_TRAN_DONE is not enabled now */ pxa_host_enable_int(pxa_host, MMC_I_MASK_TINT); return 1; } if ((ireg & MMC_I_REG_TINT) && (!(mask & MMC_I_MASK_TINT))) { stat = readl(pxa_host->base + MMC_STAT); pxa_host_disable_int(pxa_host, \ MMC_I_MASK_DATA_TRAN_DONE | \ MMC_I_MASK_DAT_ERR | \ MMC_I_MASK_TINT); pxa_host_set_error(cmd, stat); pxa_host->llreq->done(pxa_host->llreq); return 1; } /* wait for DMA interrupt, DATA READ done * once DATA_TRAN_DONE interrupt occurred */ if ((ireg & MMC_I_REG_DATA_TRAN_DONE) && \ (!(mask & MMC_I_MASK_DATA_TRAN_DONE))) { pxa_host_disable_int(pxa_host, \ MMC_I_MASK_DATA_TRAN_DONE | \ MMC_I_MASK_DAT_ERR | \ MMC_I_MASK_TINT); /* check data error */ if (ireg & MMC_I_MASK_DAT_ERR) { stat = readl(pxa_host->base + MMC_STAT); pxa_host_set_error(cmd, stat); } /* dbg("in DATA_TRAN_DONE, prepare to cmd_complete");*/ pxa_host->llreq->done(pxa_host->llreq); return 1; } return 1;}/** * pxa_slot_other_irq * @card: mss_card_device * @ireg: value of MMCx_I_REG * @stat: value of MMCx_STAT * @mask: value of MMCx_I_MASK * * slot INT routine for commands other than R1B, write and read commands */static int pxa_host_other_irq(struct pxa_mss_host *pxa_host, u32 ireg, u32 stat, u32 mask){ struct mss_cmd *cmd = pxa_host->cmd; if ((ireg & MMC_I_REG_RES_ERR) && (!(mask & MMC_I_MASK_RES_ERR))){ pxa_host_disable_int(pxa_host, MMC_I_MASK_END_CMD_RES | \ MMC_I_MASK_RES_ERR); pxa_host_get_response(pxa_host, cmd); pxa_host_set_error(cmd, stat); /* dbg("in RES_ERROR, prepare to cmd_complete\n"); */ pxa_host->llreq->done(pxa_host->llreq); return 1; } else if((ireg & MMC_I_REG_END_CMD_RES) && \ (!(mask & MMC_I_MASK_END_CMD_RES))) { pxa_host_disable_int(pxa_host, \ MMC_I_MASK_END_CMD_RES | \ MMC_I_MASK_RES_ERR); pxa_host_get_response(pxa_host, cmd); pxa_host->llreq->done(pxa_host->llreq); } return 1;}static void sdio_interrupt_handler(struct work_struct *work){ struct pxa_slot * pxa_slot; struct pxa_mss_host * pxa_host; struct mss_card * card; struct mss_driver *drv; pxa_slot = container_of(work, struct pxa_slot, sdio_int); if (pxa_slot && pxa_slot->slot && pxa_slot->slot->host) pxa_host = pxa_slot->slot->host->private; else goto out; if (pxa_host && pxa_host->active_slot) card = pxa_host->active_slot->card; else goto out; if(pxa_host && pxa_host->active_slot && pxa_host->active_slot->card) { drv = container_of(pxa_host->active_slot->card->dev.driver, struct mss_driver, driver); } if (drv->sdio_int_handler) drv->sdio_int_handler(card); else return; out: dbg("null pointer occurred\n"); return;}/** * pxa_slot_irq * @irq: IRQ number * @devid: pointer to pxa_mss_controller_device * @regs: interrupt context * * response interrupt and sdio interrupt handler. * devid represent pxa_mss_controller_device because * interrupt is to pxa_controller */static irqreturn_t pxa_host_irq(int irq, void *devid){ struct pxa_mss_host *pxa_host; struct mss_cmd *cmd; struct mss_data *data; struct mss_driver *drv; struct mss_card *card; u32 ireg, stat, mask; pxa_host = (struct pxa_mss_host *)devid; cmd = pxa_host->cmd; data = pxa_host->data; card = pxa_host->host->active_card; ireg = readl(pxa_host->base + MMC_I_REG); stat = readl(pxa_host->base + MMC_STAT); mask = readl(pxa_host->base + MMC_I_MASK);// printk("%s:host%d irq, I_REG:0x%x, STAT:0x%x, I_MASK:0x%x, CLKRT:0x%x," // "RESTO:0x%x, RDTO:0x%x\n", __FUNCTION__,// pxa_host->host->id, ireg, stat, mask, // readl(pxa_host->base + MMC_CLKRT), // readl(pxa_host->base + MMC_RESTO),// readl(pxa_host->base + MMC_RDTO)); dbg("host%d irq, I_REG:0x%x, STAT:0x%x, I_MASK:0x%x, CLKRT:0x%x," "RESTO:0x%x, RDTO:0x%x\n", pxa_host->host->id, ireg, stat, mask, readl(pxa_host->base + MMC_CLKRT), readl(pxa_host->base + MMC_RESTO), readl(pxa_host->base + MMC_RDTO)); if (data) { if (data->flags & MSS_DATA_WRITE) { pxa_host_write_irq(pxa_host, ireg, stat, mask); } if (data->flags & MSS_DATA_READ) { pxa_host_read_irq(pxa_host, ireg, stat, mask); } } else if ((cmd->rtype == MSS_RESPONSE_R1B) && (!data || !data->sg_len)) pxa_host_r1b_irq(pxa_host, ireg, stat, mask); else pxa_host_other_irq(pxa_host, ireg, stat, mask); /* handle sdio interrupt, wakeup sdio int IRQ handler thread */ if ((ireg & MMC_I_REG_SDIO_INT) && (!(mask & MMC_I_MASK_SDIO_INT))) { struct pxa_slot *pxa_slot = pxa_host->active_slot->private; dbg("SDIO interrupt ocurred"); pxa_host_disable_int(pxa_host, MMC_I_MASK_SDIO_INT); pxa_host->host->sdio_int = MSS_SDIO_INT_DIS; if (!pxa_host->active_slot->card || !pxa_host->active_slot->card->dev.driver) return IRQ_HANDLED; drv = container_of(pxa_host->active_slot->card->dev.driver, struct mss_driver, driver); /* application driver sdio_int_handler should * get card from work_struct. e.g, * sdio_int_handler(struct work_struct *work) * { * struct pxa_slot * pxa_slot = * contrainer_of(work, struct pxa_slot, sdio_int.work); * struct pxa_mss_host * pxa_host = * pxa_slot->slot->host->private; * struct mss_card * card = pxa_host->active_slot->card; * ... * } */ INIT_WORK(&pxa_slot->sdio_int, (void (*)(void *))sdio_interrupt_handler); queue_work(pxa_host->sdio_work_queue, &pxa_slot->sdio_int); } return IRQ_HANDLED;}/** * pxa_slot_gpio_irq * @irq: IRQ number * @devid: pointer to mss_slot * @regs: interrupt context * * hot-plug interrupt handler. devid represent slot * because hot-plug is slot-specific. */static irqreturn_t pxa_slot_gpio_irq(int irq, void *devid){ struct mss_slot *slot = (struct mss_slot *)devid; struct pxa_slot *pxa_slot = slot->private; struct pxa_mss_host *pxa_host = slot->host->private; dbg("Slot :%d, inttrupt", slot->id);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -