📄 mmc.patch
字号:
+ + tasklet_schedule(&host->data_task);+ }+}++static void au1xmmc_receive_pio(struct au1xmmc_host *host) {++ struct mmc_data *data = 0;+ int sg_len = 0, max = 0, count = 0;+ unsigned char *sg_ptr = 0;+ u32 status = 0;++ data = host->mrq->data;++ if (!(host->flags & HOST_F_RECV)) + return;++ max = host->pio.len;++ if (host->pio.index < host->dma.len) {+ sg_ptr = phys_to_virt(sg_dma_address(&data->sg[host->pio.index]));+ sg_ptr += host->pio.offset;+ + /* This is the space left inside the buffer */+ sg_len = sg_dma_len(&data->sg[host->pio.index]) - host->pio.offset;++ /* Check to if we need less then the size of the sg_buffer */+ if (sg_len < max) max = sg_len;+ }++ if (max > AU1XMMC_MAX_TRANSFER) + max = AU1XMMC_MAX_TRANSFER;++ for(count = 0; count < max; count++ ) { + u32 val;+ status = au_readl(HOST_STATUS(host));++ if (!(status & SD_STATUS_NE))+ break;++ if (status & SD_STATUS_RC) {+ DEBUG("RX CRC Error [%d + %d].\n", host->id, + host->pio.len, count);+ break;+ }++ if (status & SD_STATUS_RO) {+ DEBUG("RX Overrun [%d + %d]\n", host->id, + host->pio.len, count);+ break;+ }+ else if (status & SD_STATUS_RU) {+ DEBUG("RX Underrun [%d + %d]\n", host->id, + host->pio.len, count);+ break;+ }++ val = au_readl(HOST_RXPORT(host)); ++ if (sg_ptr) + *sg_ptr++ = (unsigned char) (val & 0xFF);+ }++ host->pio.len -= count;+ host->pio.offset += count;++ if (sg_len && count == sg_len) {+ host->pio.index++;+ host->pio.offset = 0;+ }++ if (host->pio.len == 0) {+ //IRQ_OFF(host, SD_CONFIG_RA | SD_CONFIG_RF);+ IRQ_OFF(host, SD_CONFIG_NE);++ if (host->flags & HOST_F_STOP)+ SEND_STOP(host);++ tasklet_schedule(&host->data_task);+ }+}++/* static void au1xmmc_cmd_complete+ This is called when a command has been completed - grab the response+ and check for errors. Then start the data transfer if it is indicated.+*/++static void au1xmmc_cmd_complete(struct au1xmmc_host *host, u32 status) {++ struct mmc_request *mrq = host->mrq;+ struct mmc_command *cmd;+ int trans;++ if (!host->mrq) + return;++ cmd = mrq->cmd;+ cmd->error = MMC_ERR_NONE;++ if ((cmd->flags & MMC_RSP_MASK) == MMC_RSP_SHORT) {++ /* Techincally, we should be getting all 48 bits of the response+ * (SD_RESP1 + SD_RESP2), but because our response omits the CRC,+ * our data ends up being shifted 8 bits to the right. In this case,+ * that means that the OSR data starts at bit 31, so we can just+ * read RESP0 and return that + */++ cmd->resp[0] = au_readl(host->iobase + SD_RESP0);+ }+ else if ((cmd->flags & MMC_RSP_MASK) == MMC_RSP_LONG) {+ u32 r[4];+ int i;++ r[0] = au_readl(host->iobase + SD_RESP3);+ r[1] = au_readl(host->iobase + SD_RESP2);+ r[2] = au_readl(host->iobase + SD_RESP1);+ r[3] = au_readl(host->iobase + SD_RESP0);+ + /* The CRC is omitted from the response, so really we only got+ * 120 bytes, but the engine expects 128 bits, so we have to shift+ * things up + */+ + for(i = 0; i < 4; i++) {+ cmd->resp[i] = (r[i] & 0x00FFFFFF) << 8;+ if (i != 3) cmd->resp[i] |= (r[i + 1] & 0xFF000000) >> 24;+ }+ }++ /* Figure out errors */++ if (status & (SD_STATUS_SC | SD_STATUS_WC | SD_STATUS_RC))+ cmd->error = MMC_ERR_BADCRC;++ trans = host->flags & (HOST_F_XMIT | HOST_F_RECV);++ if (!trans || cmd->error != MMC_ERR_NONE) {++ IRQ_OFF(host, SD_CONFIG_TH | SD_CONFIG_RA|SD_CONFIG_RF);+ tasklet_schedule(&host->finish_task);+ return;+ }++ host->status = HOST_S_DATA;++ if (host->flags & HOST_F_DMA) {+ u32 channel = DMA_CHANNEL(host);++ /* Start the DMA as soon as the buffer gets something in it */++ if (host->flags & HOST_F_RECV) {+ u32 mask = SD_STATUS_DB | SD_STATUS_NE;++ while((status & mask) != mask) + status = au_readl(HOST_STATUS(host));+ }++ au1xxx_dbdma_start(channel);+ }+}++static void au1xmmc_set_clock(struct au1xmmc_host *host, int rate) {++ unsigned int pbus = get_au1x00_speed();+ unsigned int divisor;+ u32 config;++ /* From databook:+ divisor = ((((cpuclock / sbus_divisor) / 2) / mmcclock) / 2) - 1+ */++ pbus /= ((au_readl(SYS_POWERCTRL) & 0x3) + 2);+ pbus /= 2;++ divisor = ((pbus / rate) / 2) - 1;++ config = au_readl(HOST_CONFIG(host));++ config &= ~(SD_CONFIG_DIV);+ config |= (divisor & SD_CONFIG_DIV) | SD_CONFIG_DE;++ au_writel(config, HOST_CONFIG(host));+ au_sync();+}++static int au1xmmc_prepare_data(struct au1xmmc_host *host, + struct mmc_data *data) {++ int datalen = data->blocks * (1 << data->blksz_bits);++ if (dma != 0) + host->flags |= HOST_F_DMA;++ if (data->flags & MMC_DATA_READ)+ host->flags |= HOST_F_RECV;+ else+ host->flags |= HOST_F_XMIT;++ if (host->mrq->stop) + host->flags |= HOST_F_STOP;+ + host->dma.dir = DMA_BIDIRECTIONAL;++ host->dma.len = dma_map_sg(mmc_dev(host->mmc), data->sg,+ data->sg_len, host->dma.dir);++ if (host->dma.len == 0)+ return MMC_ERR_TIMEOUT;++ au_writel((1 << data->blksz_bits) - 1, HOST_BLKSIZE(host)); ++ if (host->flags & HOST_F_DMA) {+ int i;+ u32 channel = DMA_CHANNEL(host);++ au1xxx_dbdma_stop(channel);++ for(i = 0; i < host->dma.len; i++) {+ u32 ret = 0, flags = DDMA_FLAGS_NOIE;+ u32 sg_addr = sg_dma_address(&data->sg[i]);+ int sg_len = sg_dma_len(&data->sg[i]);+ int len = (datalen > sg_len) ? sg_len : datalen;++ if (i == host->dma.len - 1)+ flags = DDMA_FLAGS_IE;++ if (host->flags & HOST_F_XMIT)+ ret = au1xxx_dbdma_put_source_flags(channel, + (void *) phys_to_virt(sg_addr), + len, flags);+ else+ ret = au1xxx_dbdma_put_dest_flags(channel, + (void *) phys_to_virt(sg_addr),+ len, flags);++ if (!ret) + goto dataerr;++ datalen -= len;+ }+ }+ else {+ host->pio.index = 0;+ host->pio.offset = 0;+ host->pio.len = datalen;+ + if (host->flags & HOST_F_XMIT)+ IRQ_ON(host, SD_CONFIG_TH);+ else + IRQ_ON(host, SD_CONFIG_NE);+ //IRQ_ON(host, SD_CONFIG_RA|SD_CONFIG_RF);+ }++ return MMC_ERR_NONE;++ dataerr:+ dma_unmap_sg(mmc_dev(host->mmc),data->sg,data->sg_len,host->dma.dir); + return MMC_ERR_TIMEOUT;+} ++/* static void au1xmmc_request+ This actually starts a command or data transaction+*/++static void au1xmmc_request(struct mmc_host* mmc, struct mmc_request* mrq) {++ struct au1xmmc_host *host = mmc_priv(mmc);+ int ret = MMC_ERR_NONE;++ //WARN_ON(irqs_disabled());+ //WARN_ON(host->status != HOST_S_IDLE);++ host->mrq = mrq;+ host->status = HOST_S_CMD;++ bcsr->disk_leds &= ~(1 << 8);++ if (mrq->data) {+ FLUSH_FIFO(host);+ ret = au1xmmc_prepare_data(host, mrq->data);+ }++ if (ret == MMC_ERR_NONE)+ ret = au1xmmc_send_command(host, 0, mrq->cmd);++ if (ret != MMC_ERR_NONE) {+ mrq->cmd->error = ret;+ au1xmmc_finish_request(host);+ }+}++static void au1xmmc_reset_controller(struct au1xmmc_host *host) {++ /* Apply the clock */+ au_writel(SD_ENABLE_CE, HOST_ENABLE(host));+ au_sync_delay(1);++ au_writel(SD_ENABLE_R | SD_ENABLE_CE, HOST_ENABLE(host));+ au_sync_delay(5);++ au_writel(~0, HOST_STATUS(host));+ au_sync();++ au_writel(0, HOST_BLKSIZE(host));+ au_writel(0x001fffff, HOST_TIMEOUT(host));+ au_sync();++ au_writel(SD_CONFIG2_EN, HOST_CONFIG2(host));+ au_sync();++ au_writel(SD_CONFIG2_EN | SD_CONFIG2_FF, HOST_CONFIG2(host));+ au_sync_delay(1);++ au_writel(SD_CONFIG2_EN, HOST_CONFIG2(host));+ au_sync();++ /* Configure interrupts */+ au_writel(AU1XMMC_INTERRUPTS, HOST_CONFIG(host));+ au_sync();+}+ ++static void au1xmmc_set_ios(struct mmc_host* mmc, struct mmc_ios* ios) {+ struct au1xmmc_host *host = mmc_priv(mmc);++ DEBUG("set_ios (power=%u, clock=%uHz, vdd=%u, mode=%u)\n",+ host->id, ios->power_mode, ios->clock, ios->vdd, + ios->bus_mode);++ if (ios->power_mode == MMC_POWER_OFF) + au1xmmc_set_power(host, 0);+ else if (ios->power_mode == MMC_POWER_ON) {+ au1xmmc_set_power(host, 1);+ }++ if (ios->clock && ios->clock != host->clock) {+ au1xmmc_set_clock(host, ios->clock);+ host->clock = ios->clock;+ }+}++static void au1xmmc_dma_callback(int irq, void *dev_id, struct pt_regs *regs) {+ struct au1xmmc_host *host = (struct au1xmmc_host *) dev_id;+ u32 status;++ /* Avoid spurious interrupts */++ if (!host->mrq) + return;++ if (host->flags & HOST_F_STOP) + SEND_STOP(host);++ tasklet_schedule(&host->data_task);+}++#define STATUS_TIMEOUT (SD_STATUS_RAT | SD_STATUS_DT)+#define STATUS_DATA_IN (SD_STATUS_NE)+#define STATUS_DATA_OUT (SD_STATUS_TH)++static irqreturn_t au1xmmc_irq(int irq, void *dev_id, struct pt_regs *regs) {+ + u32 status;+ int i, ret = 0;++ disable_irq(AU1100_SD_IRQ);++ for(i = 0; i < AU1XMMC_CONTROLLER_COUNT; i++) { + struct au1xmmc_host * host = au1xmmc_hosts[i];+ u32 handled = 1;++ status = au_readl(HOST_STATUS(host));++ if (host->mrq && (status & STATUS_TIMEOUT)) {+ if (status & SD_STATUS_RAT) + host->mrq->cmd->error = MMC_ERR_TIMEOUT;+ + else if (status & SD_STATUS_DT) + host->mrq->data->error = MMC_ERR_TIMEOUT;+ + /* In PIO mode, interrupts might still be enabled */+ IRQ_OFF(host, SD_CONFIG_NE | SD_CONFIG_TH);++ //IRQ_OFF(host, SD_CONFIG_TH|SD_CONFIG_RA|SD_CONFIG_RF);+ tasklet_schedule(&host->finish_task);+ }+#if 0+ else if (status & SD_STATUS_DD) {++ /* Sometimes we get a DD before a NE in PIO mode */++ if (!(host->flags & HOST_F_DMA) && + (status & SD_STATUS_NE))+ au1xmmc_receive_pio(host);+ else {+ au1xmmc_data_complete(host, status);+ //tasklet_schedule(&host->data_task);+ }+ }+#endif+ else if (status & (SD_STATUS_CR)) {+ if (host->status == HOST_S_CMD)+ au1xmmc_cmd_complete(host,status);+ }+ else if (!(host->flags & HOST_F_DMA)) { + if ((host->flags & HOST_F_XMIT) && + (status & STATUS_DATA_OUT))+ au1xmmc_send_pio(host);+ else if ((host->flags & HOST_F_RECV) && + (status & STATUS_DATA_IN))+ au1xmmc_receive_pio(host);+ }+ else if (status & 0x203FBC70) {+ DEBUG("Unhandled status %8.8x\n", host->id, status);+ handled = 0;+ }+ + au_writel(status, HOST_STATUS(host));+ au_sync();++ ret |= handled;+ }++ enable_irq(AU1100_SD_IRQ);+ return ret;+}++static void au1xmmc_poll_event(unsigned long arg) {+ struct au1xmmc_host *host = (struct au1xmmc_host *) arg;++ int card = au1xmmc_card_inserted(host);+ int controller = (host->flags & HOST_F_ACTIVE) ? 1 : 0;++ if (card != controller) {+ host->flags &= ~HOST_F_ACTIVE;+ if (card) host->flags |= HOST_F_ACTIVE;+ mmc_detect_change(host->mmc);+ }++ if (host->mrq != NULL) {+ u32 status = au_readl(HOST_STATUS(host));+ DEBUG("PENDING - %8.8x\n", host->id, status);+ }++ mod_timer(&host->timer, jiffies + AU1XMMC_DETECT_TIMEOUT);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -