cregit-Linux how code gets into the kernel

Release 4.14 drivers/mmc/host/tifm_sd.c

Directory: drivers/mmc/host
/*
 *  tifm_sd.c - TI FlashMedia driver
 *
 *  Copyright (C) 2006 Alex Dubov <oakad@yahoo.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * Special thanks to Brad Campbell for extensive testing of this driver.
 *
 */


#include <linux/tifm.h>
#include <linux/mmc/host.h>
#include <linux/highmem.h>
#include <linux/scatterlist.h>
#include <linux/module.h>
#include <asm/io.h>


#define DRIVER_NAME "tifm_sd"

#define DRIVER_VERSION "0.8"


static bool no_dma = 0;

static bool fixed_timeout = 0;
module_param(no_dma, bool, 0644);
module_param(fixed_timeout, bool, 0644);

/* Constants here are mostly from OMAP5912 datasheet */

#define TIFM_MMCSD_RESET      0x0002

#define TIFM_MMCSD_CLKMASK    0x03ff

#define TIFM_MMCSD_POWER      0x0800

#define TIFM_MMCSD_4BBUS      0x8000

#define TIFM_MMCSD_RXDE       0x8000   
/* rx dma enable */

#define TIFM_MMCSD_TXDE       0x0080   
/* tx dma enable */

#define TIFM_MMCSD_BUFINT     0x0c00   
/* set bits: AE, AF */

#define TIFM_MMCSD_DPE        0x0020   
/* data timeout counted in kilocycles */

#define TIFM_MMCSD_INAB       0x0080   
/* abort / initialize command */

#define TIFM_MMCSD_READ       0x8000


#define TIFM_MMCSD_ERRMASK    0x01e0   
/* set bits: CCRC, CTO, DCRC, DTO */

#define TIFM_MMCSD_EOC        0x0001   
/* end of command phase  */

#define TIFM_MMCSD_CD         0x0002   
/* card detect           */

#define TIFM_MMCSD_CB         0x0004   
/* card enter busy state */

#define TIFM_MMCSD_BRS        0x0008   
/* block received/sent   */

#define TIFM_MMCSD_EOFB       0x0010   
/* card exit busy state  */

#define TIFM_MMCSD_DTO        0x0020   
/* data time-out         */

#define TIFM_MMCSD_DCRC       0x0040   
/* data crc error        */

#define TIFM_MMCSD_CTO        0x0080   
/* command time-out      */

#define TIFM_MMCSD_CCRC       0x0100   
/* command crc error     */

#define TIFM_MMCSD_AF         0x0400   
/* fifo almost full      */

#define TIFM_MMCSD_AE         0x0800   
/* fifo almost empty     */

#define TIFM_MMCSD_OCRB       0x1000   
/* OCR busy              */

#define TIFM_MMCSD_CIRQ       0x2000   
/* card irq (cmd40/sdio) */

#define TIFM_MMCSD_CERR       0x4000   
/* card status error     */


#define TIFM_MMCSD_ODTO       0x0040   
/* open drain / extended timeout */

#define TIFM_MMCSD_CARD_RO    0x0200   
/* card is read-only     */


#define TIFM_MMCSD_FIFO_SIZE  0x0020


#define TIFM_MMCSD_RSP_R0     0x0000

#define TIFM_MMCSD_RSP_R1     0x0100

#define TIFM_MMCSD_RSP_R2     0x0200

#define TIFM_MMCSD_RSP_R3     0x0300

#define TIFM_MMCSD_RSP_R4     0x0400

#define TIFM_MMCSD_RSP_R5     0x0500

#define TIFM_MMCSD_RSP_R6     0x0600


#define TIFM_MMCSD_RSP_BUSY   0x0800


#define TIFM_MMCSD_CMD_BC     0x0000

#define TIFM_MMCSD_CMD_BCR    0x1000

#define TIFM_MMCSD_CMD_AC     0x2000

#define TIFM_MMCSD_CMD_ADTC   0x3000


#define TIFM_MMCSD_MAX_BLOCK_SIZE  0x0800UL

enum {
	
CMD_READY    = 0x0001,
	
FIFO_READY   = 0x0002,
	
BRS_READY    = 0x0004,
	
SCMD_ACTIVE  = 0x0008,
	
SCMD_READY   = 0x0010,
	
CARD_BUSY    = 0x0020,
	
DATA_CARRY   = 0x0040
};


struct tifm_sd {
	
struct tifm_dev       *dev;

	
unsigned short        eject:1,
			      
open_drain:1,
			      
no_dma:1;
	
unsigned short        cmd_flags;

	
unsigned int          clk_freq;
	
unsigned int          clk_div;
	
unsigned long         timeout_jiffies;

	
struct tasklet_struct finish_tasklet;
	
struct timer_list     timer;
	
struct mmc_request    *req;

	
int                   sg_len;
	
int                   sg_pos;
	
unsigned int          block_pos;
	
struct scatterlist    bounce_buf;
	
unsigned char         bounce_buf_data[TIFM_MMCSD_MAX_BLOCK_SIZE];
};

/* for some reason, host won't respond correctly to readw/writew */

static void tifm_sd_read_fifo(struct tifm_sd *host, struct page *pg, unsigned int off, unsigned int cnt) { struct tifm_dev *sock = host->dev; unsigned char *buf; unsigned int pos = 0, val; buf = kmap_atomic(pg) + off; if (host->cmd_flags & DATA_CARRY) { buf[pos++] = host->bounce_buf_data[0]; host->cmd_flags &= ~DATA_CARRY; } while (pos < cnt) { val = readl(sock->addr + SOCK_MMCSD_DATA); buf[pos++] = val & 0xff; if (pos == cnt) { host->bounce_buf_data[0] = (val >> 8) & 0xff; host->cmd_flags |= DATA_CARRY; break; } buf[pos++] = (val >> 8) & 0xff; } kunmap_atomic(buf - off); }

Contributors

PersonTokensPropCommitsCommitProp
Alex Dubov165100.00%4100.00%
Total165100.00%4100.00%


static void tifm_sd_write_fifo(struct tifm_sd *host, struct page *pg, unsigned int off, unsigned int cnt) { struct tifm_dev *sock = host->dev; unsigned char *buf; unsigned int pos = 0, val; buf = kmap_atomic(pg) + off; if (host->cmd_flags & DATA_CARRY) { val = host->bounce_buf_data[0] | ((buf[pos++] << 8) & 0xff00); writel(val, sock->addr + SOCK_MMCSD_DATA); host->cmd_flags &= ~DATA_CARRY; } while (pos < cnt) { val = buf[pos++]; if (pos == cnt) { host->bounce_buf_data[0] = val & 0xff; host->cmd_flags |= DATA_CARRY; break; } val |= (buf[pos++] << 8) & 0xff00; writel(val, sock->addr + SOCK_MMCSD_DATA); } kunmap_atomic(buf - off); }

Contributors

PersonTokensPropCommitsCommitProp
Alex Dubov180100.00%4100.00%
Total180100.00%4100.00%


static void tifm_sd_transfer_data(struct tifm_sd *host) { struct mmc_data *r_data = host->req->cmd->data; struct scatterlist *sg = r_data->sg; unsigned int off, cnt, t_size = TIFM_MMCSD_FIFO_SIZE * 2; unsigned int p_off, p_cnt; struct page *pg; if (host->sg_pos == host->sg_len) return; while (t_size) { cnt = sg[host->sg_pos].length - host->block_pos; if (!cnt) { host->block_pos = 0; host->sg_pos++; if (host->sg_pos == host->sg_len) { if ((r_data->flags & MMC_DATA_WRITE) && (host->cmd_flags & DATA_CARRY)) writel(host->bounce_buf_data[0], host->dev->addr + SOCK_MMCSD_DATA); return; } cnt = sg[host->sg_pos].length; } off = sg[host->sg_pos].offset + host->block_pos; pg = nth_page(sg_page(&sg[host->sg_pos]), off >> PAGE_SHIFT); p_off = offset_in_page(off); p_cnt = PAGE_SIZE - p_off; p_cnt = min(p_cnt, cnt); p_cnt = min(p_cnt, t_size); if (r_data->flags & MMC_DATA_READ) tifm_sd_read_fifo(host, pg, p_off, p_cnt); else if (r_data->flags & MMC_DATA_WRITE) tifm_sd_write_fifo(host, pg, p_off, p_cnt); t_size -= p_cnt; host->block_pos += p_cnt; } }

Contributors

PersonTokensPropCommitsCommitProp
Alex Dubov27196.44%250.00%
Roel Kluin62.14%125.00%
Jens Axboe41.42%125.00%
Total281100.00%4100.00%


static void tifm_sd_copy_page(struct page *dst, unsigned int dst_off, struct page *src, unsigned int src_off, unsigned int count) { unsigned char *src_buf = kmap_atomic(src) + src_off; unsigned char *dst_buf = kmap_atomic(dst) + dst_off; memcpy(dst_buf, src_buf, count); kunmap_atomic(dst_buf - dst_off); kunmap_atomic(src_buf - src_off); }

Contributors

PersonTokensPropCommitsCommitProp
Alex Dubov75100.00%1100.00%
Total75100.00%1100.00%


static void tifm_sd_bounce_block(struct tifm_sd *host, struct mmc_data *r_data) { struct scatterlist *sg = r_data->sg; unsigned int t_size = r_data->blksz; unsigned int off, cnt; unsigned int p_off, p_cnt; struct page *pg; dev_dbg(&host->dev->dev, "bouncing block\n"); while (t_size) { cnt = sg[host->sg_pos].length - host->block_pos; if (!cnt) { host->block_pos = 0; host->sg_pos++; if (host->sg_pos == host->sg_len) return; cnt = sg[host->sg_pos].length; } off = sg[host->sg_pos].offset + host->block_pos; pg = nth_page(sg_page(&sg[host->sg_pos]), off >> PAGE_SHIFT); p_off = offset_in_page(off); p_cnt = PAGE_SIZE - p_off; p_cnt = min(p_cnt, cnt); p_cnt = min(p_cnt, t_size); if (r_data->flags & MMC_DATA_WRITE) tifm_sd_copy_page(sg_page(&host->bounce_buf), r_data->blksz - t_size, pg, p_off, p_cnt); else if (r_data->flags & MMC_DATA_READ) tifm_sd_copy_page(pg, p_off, sg_page(&host->bounce_buf), r_data->blksz - t_size, p_cnt); t_size -= p_cnt; host->block_pos += p_cnt; } }

Contributors

PersonTokensPropCommitsCommitProp
Alex Dubov25095.42%150.00%
Jens Axboe124.58%150.00%
Total262100.00%2100.00%


static int tifm_sd_set_dma_data(struct tifm_sd *host, struct mmc_data *r_data) { struct tifm_dev *sock = host->dev; unsigned int t_size = TIFM_DMA_TSIZE * r_data->blksz; unsigned int dma_len, dma_blk_cnt, dma_off; struct scatterlist *sg = NULL; unsigned long flags; if (host->sg_pos == host->sg_len) return 1; if (host->cmd_flags & DATA_CARRY) { host->cmd_flags &= ~DATA_CARRY; local_irq_save(flags); tifm_sd_bounce_block(host, r_data); local_irq_restore(flags); if (host->sg_pos == host->sg_len) return 1; } dma_len = sg_dma_len(&r_data->sg[host->sg_pos]) - host->block_pos; if (!dma_len) { host->block_pos = 0; host->sg_pos++; if (host->sg_pos == host->sg_len) return 1; dma_len = sg_dma_len(&r_data->sg[host->sg_pos]); } if (dma_len < t_size) { dma_blk_cnt = dma_len / r_data->blksz; dma_off = host->block_pos; host->block_pos += dma_blk_cnt * r_data->blksz; } else { dma_blk_cnt = TIFM_DMA_TSIZE; dma_off = host->block_pos; host->block_pos += t_size; } if (dma_blk_cnt) sg = &r_data->sg[host->sg_pos]; else if (dma_len) { if (r_data->flags & MMC_DATA_WRITE) { local_irq_save(flags); tifm_sd_bounce_block(host, r_data); local_irq_restore(flags); } else host->cmd_flags |= DATA_CARRY; sg = &host->bounce_buf; dma_off = 0; dma_blk_cnt = 1; } else return 1; dev_dbg(&sock->dev, "setting dma for %d blocks\n", dma_blk_cnt); writel(sg_dma_address(sg) + dma_off, sock->addr + SOCK_DMA_ADDRESS); if (r_data->flags & MMC_DATA_WRITE) writel((dma_blk_cnt << 8) | TIFM_DMA_TX | TIFM_DMA_EN, sock->addr + SOCK_DMA_CONTROL); else writel((dma_blk_cnt << 8) | TIFM_DMA_EN, sock->addr + SOCK_DMA_CONTROL); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Alex Dubov38199.74%150.00%
Adrian Bunk10.26%150.00%
Total382100.00%2100.00%


static unsigned int tifm_sd_op_flags(struct mmc_command *cmd) { unsigned int rc = 0; switch (mmc_resp_type(cmd)) { case MMC_RSP_NONE: rc |= TIFM_MMCSD_RSP_R0; break; case MMC_RSP_R1B: rc |= TIFM_MMCSD_RSP_BUSY; // deliberate fall-through case MMC_RSP_R1: rc |= TIFM_MMCSD_RSP_R1; break; case MMC_RSP_R2: rc |= TIFM_MMCSD_RSP_R2; break; case MMC_RSP_R3: rc |= TIFM_MMCSD_RSP_R3; break; default: BUG(); } switch (mmc_cmd_type(cmd)) { case MMC_CMD_BC: rc |= TIFM_MMCSD_CMD_BC; break; case MMC_CMD_BCR: rc |= TIFM_MMCSD_CMD_BCR; break; case MMC_CMD_AC: rc |= TIFM_MMCSD_CMD_AC; break; case MMC_CMD_ADTC: rc |= TIFM_MMCSD_CMD_ADTC; break; default: BUG(); } return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Alex Dubov119100.00%1100.00%
Total119100.00%1100.00%


static void tifm_sd_exec(struct tifm_sd *host, struct mmc_command *cmd) { struct tifm_dev *sock = host->dev; unsigned int cmd_mask = tifm_sd_op_flags(cmd); if (host->open_drain) cmd_mask |= TIFM_MMCSD_ODTO; if (cmd->data && (cmd->data->flags & MMC_DATA_READ)) cmd_mask |= TIFM_MMCSD_READ; dev_dbg(&sock->dev, "executing opcode 0x%x, arg: 0x%x, mask: 0x%x\n", cmd->opcode, cmd->arg, cmd_mask); writel((cmd->arg >> 16) & 0xffff, sock->addr + SOCK_MMCSD_ARG_HIGH); writel(cmd->arg & 0xffff, sock->addr + SOCK_MMCSD_ARG_LOW); writel(cmd->opcode | cmd_mask, sock->addr + SOCK_MMCSD_COMMAND); }

Contributors

PersonTokensPropCommitsCommitProp
Alex Dubov133100.00%3100.00%
Total133100.00%3100.00%


static void tifm_sd_fetch_resp(struct mmc_command *cmd, struct tifm_dev *sock) { cmd->resp[0] = (readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x1c) << 16) | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x18); cmd->resp[1] = (readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x14) << 16) | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x10); cmd->resp[2] = (readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x0c) << 16) | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x08); cmd->resp[3] = (readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x04) << 16) | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x00); }

Contributors

PersonTokensPropCommitsCommitProp
Alex Dubov148100.00%1100.00%
Total148100.00%1100.00%


static void tifm_sd_check_status(struct tifm_sd *host) { struct tifm_dev *sock = host->dev; struct mmc_command *cmd = host->req->cmd; if (cmd->error) goto finish_request; if (!(host->cmd_flags & CMD_READY)) return; if (cmd->data) { if (cmd->data->error) { if ((host->cmd_flags & SCMD_ACTIVE) && !(host->cmd_flags & SCMD_READY)) return; goto finish_request; } if (!(host->cmd_flags & BRS_READY)) return; if (!(host->no_dma || (host->cmd_flags & FIFO_READY))) return; if (cmd->data->flags & MMC_DATA_WRITE) { if (host->req->stop) { if (!(host->cmd_flags & SCMD_ACTIVE)) { host->cmd_flags |= SCMD_ACTIVE; writel(TIFM_MMCSD_EOFB | readl(sock->addr + SOCK_MMCSD_INT_ENABLE), sock->addr + SOCK_MMCSD_INT_ENABLE); tifm_sd_exec(host, host->req->stop); return; } else { if (!(host->cmd_flags & SCMD_READY) || (host->cmd_flags & CARD_BUSY)) return; writel((~TIFM_MMCSD_EOFB) & readl(sock->addr + SOCK_MMCSD_INT_ENABLE), sock->addr + SOCK_MMCSD_INT_ENABLE); } } else { if (host->cmd_flags & CARD_BUSY) return; writel((~TIFM_MMCSD_EOFB) & readl(sock->addr + SOCK_MMCSD_INT_ENABLE), sock->addr + SOCK_MMCSD_INT_ENABLE); } } else { if (host->req->stop) { if (!(host->cmd_flags & SCMD_ACTIVE)) { host->cmd_flags |= SCMD_ACTIVE; tifm_sd_exec(host, host->req->stop); return; } else { if (!(host->cmd_flags & SCMD_READY)) return; } } } } finish_request: tasklet_schedule(&host->finish_tasklet); }

Contributors

PersonTokensPropCommitsCommitProp
Alex Dubov346100.00%7100.00%
Total346100.00%7100.00%

/* Called from interrupt handler */
static void tifm_sd_data_event(struct tifm_dev *sock) { struct tifm_sd *host; unsigned int fifo_status = 0; struct mmc_data *r_data = NULL; spin_lock(&sock->lock); host = mmc_priv((struct mmc_host*)tifm_get_drvdata(sock)); fifo_status = readl(sock->addr + SOCK_DMA_FIFO_STATUS); dev_dbg(&sock->dev, "data event: fifo_status %x, flags %x\n", fifo_status, host->cmd_flags); if (host->req) { r_data = host->req->cmd->data; if (r_data && (fifo_status & TIFM_FIFO_READY)) { if (tifm_sd_set_dma_data(host, r_data)) { host->cmd_flags |= FIFO_READY; tifm_sd_check_status(host); } } } writel(fifo_status, sock->addr + SOCK_DMA_FIFO_STATUS); spin_unlock(&sock->lock); }

Contributors

PersonTokensPropCommitsCommitProp
Alex Dubov150100.00%4100.00%
Total150100.00%4100.00%

/* Called from interrupt handler */
static void tifm_sd_card_event(struct tifm_dev *sock) { struct tifm_sd *host; unsigned int host_status = 0; int cmd_error = 0; struct mmc_command *cmd = NULL; unsigned long flags; spin_lock(&sock->lock); host = mmc_priv((struct mmc_host*)tifm_get_drvdata(sock)); host_status = readl(sock->addr + SOCK_MMCSD_STATUS); dev_dbg(&sock->dev, "host event: host_status %x, flags %x\n", host_status, host->cmd_flags); if (host->req) { cmd = host->req->cmd; if (host_status & TIFM_MMCSD_ERRMASK) { writel(host_status & TIFM_MMCSD_ERRMASK, sock->addr + SOCK_MMCSD_STATUS); if (host_status & TIFM_MMCSD_CTO) cmd_error = -ETIMEDOUT; else if (host_status & TIFM_MMCSD_CCRC) cmd_error = -EILSEQ; if (cmd->data) { if (host_status & TIFM_MMCSD_DTO) cmd->data->error = -ETIMEDOUT; else if (host_status & TIFM_MMCSD_DCRC) cmd->data->error = -EILSEQ; } writel(TIFM_FIFO_INT_SETALL, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); writel(TIFM_DMA_RESET, sock->addr + SOCK_DMA_CONTROL); if (host->req->stop) { if (host->cmd_flags & SCMD_ACTIVE) { host->req->stop->error = cmd_error; host->cmd_flags |= SCMD_READY; } else { cmd->error = cmd_error; host->cmd_flags |= SCMD_ACTIVE; tifm_sd_exec(host, host->req->stop); goto done; } } else cmd->error = cmd_error; } else { if (host_status & (TIFM_MMCSD_EOC | TIFM_MMCSD_CERR)) { if (!(host->cmd_flags & CMD_READY)) { host->cmd_flags |= CMD_READY; tifm_sd_fetch_resp(cmd, sock); } else if (host->cmd_flags & SCMD_ACTIVE) { host->cmd_flags |= SCMD_READY; tifm_sd_fetch_resp(host->req->stop, sock); } } if (host_status & TIFM_MMCSD_BRS) host->cmd_flags |= BRS_READY; } if (host->no_dma && cmd->data) { if (host_status & TIFM_MMCSD_AE) writel(host_status & TIFM_MMCSD_AE, sock->addr + SOCK_MMCSD_STATUS); if (host_status & (TIFM_MMCSD_AE | TIFM_MMCSD_AF | TIFM_MMCSD_BRS)) { local_irq_save(flags); tifm_sd_transfer_data(host); local_irq_restore(flags); host_status &= ~TIFM_MMCSD_AE; } } if (host_status & TIFM_MMCSD_EOFB) host->cmd_flags &= ~CARD_BUSY; else if (host_status & TIFM_MMCSD_CB) host->cmd_flags |= CARD_BUSY; tifm_sd_check_status(host); } done: writel(host_status, sock->addr + SOCK_MMCSD_STATUS); spin_unlock(&sock->lock); }

Contributors

PersonTokensPropCommitsCommitProp
Alex Dubov47098.12%583.33%
Pierre Ossman91.88%116.67%
Total479100.00%6100.00%


static void tifm_sd_set_data_timeout(struct tifm_sd *host, struct mmc_data *data) { struct tifm_dev *sock = host->dev; unsigned int data_timeout = data->timeout_clks; if (fixed_timeout) return; data_timeout += data->timeout_ns / ((1000000000UL / host->clk_freq) * host->clk_div); if (data_timeout < 0xffff) { writel(data_timeout, sock->addr + SOCK_MMCSD_DATA_TO); writel((~TIFM_MMCSD_DPE) & readl(sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG), sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG); } else { data_timeout = (data_timeout >> 10) + 1; if (data_timeout > 0xffff) data_timeout = 0; /* set to unlimited */ writel(data_timeout, sock->addr + SOCK_MMCSD_DATA_TO); writel(TIFM_MMCSD_DPE | readl(sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG), sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG); } }

Contributors

PersonTokensPropCommitsCommitProp
Alex Dubov155100.00%2100.00%
Total155100.00%2100.00%


static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq) { struct tifm_sd *host = mmc_priv(mmc); struct tifm_dev *sock = host->dev; unsigned long flags; struct mmc_data *r_data = mrq->cmd->data; spin_lock_irqsave(&sock->lock, flags); if (host->eject) { mrq->cmd->error = -ENOMEDIUM; goto err_out; } if (host->req) { pr_err("%s : unfinished request detected\n", dev_name(&sock->dev)); mrq->cmd->error = -ETIMEDOUT; goto err_out; } host->cmd_flags = 0; host->block_pos = 0; host->sg_pos = 0; if (mrq->data && !is_power_of_2(mrq->data->blksz)) host->no_dma = 1; else host->no_dma = no_dma ? 1 : 0; if (r_data) { tifm_sd_set_data_timeout(host, r_data); if ((r_data->flags & MMC_DATA_WRITE) && !mrq->stop) writel(TIFM_MMCSD_EOFB | readl(sock->addr + SOCK_MMCSD_INT_ENABLE), sock->addr + SOCK_MMCSD_INT_ENABLE); if (host->no_dma) { writel(TIFM_MMCSD_BUFINT | readl(sock->addr + SOCK_MMCSD_INT_ENABLE), sock->addr + SOCK_MMCSD_INT_ENABLE); writel(((TIFM_MMCSD_FIFO_SIZE - 1) << 8) | (TIFM_MMCSD_FIFO_SIZE - 1), sock->addr + SOCK_MMCSD_BUFFER_CONFIG); host->sg_len = r_data->sg_len; } else { sg_init_one(&host->bounce_buf, host->bounce_buf_data, r_data->blksz); if(1 != tifm_map_sg(sock, &host->bounce_buf, 1, r_data->flags & MMC_DATA_WRITE ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE)) { pr_err("%s : scatterlist map failed\n", dev_name(&sock->dev)); mrq->cmd->error = -ENOMEM; goto err_out; } host->sg_len = tifm_map_sg(sock, r_data->sg, r_data->sg_len, r_data->flags & MMC_DATA_WRITE ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); if (host->sg_len < 1) { pr_err("%s : scatterlist map failed\n", dev_name(&sock->dev)); tifm_unmap_sg(sock, &host->bounce_buf, 1, r_data->flags & MMC_DATA_WRITE ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); mrq->cmd->error = -ENOMEM; goto err_out; } writel(TIFM_FIFO_INT_SETALL, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); writel(ilog2(r_data->blksz) - 2, sock->addr + SOCK_FIFO_PAGE_SIZE); writel(TIFM_FIFO_ENABLE, sock->addr + SOCK_FIFO_CONTROL); writel(TIFM_FIFO_INTMASK, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET); if (r_data->flags & MMC_DATA_WRITE) writel(TIFM_MMCSD_TXDE, sock->addr + SOCK_MMCSD_BUFFER_CONFIG); else writel(TIFM_MMCSD_RXDE, sock->addr + SOCK_MMCSD_BUFFER_CONFIG); tifm_sd_set_dma_data(host, r_data); } writel(r_data->blocks - 1, sock->addr + SOCK_MMCSD_NUM_BLOCKS); writel(r_data->blksz - 1, sock->addr + SOCK_MMCSD_BLOCK_LEN); } host->req = mrq; mod_timer(&host->timer, jiffies + host->timeout_jiffies); writel(TIFM_CTRL_LED | readl(sock->addr + SOCK_CONTROL), sock->addr + SOCK_CONTROL); tifm_sd_exec(host, mrq->cmd); spin_unlock_irqrestore(&sock->lock, flags); return; err_out: spin_unlock_irqrestore(&sock->lock, flags); mmc_request_done(mmc, mrq); }

Contributors

PersonTokensPropCommitsCommitProp
Alex Dubov56990.75%763.64%
Pierre Ossman416.54%19.09%
Kay Sievers121.91%19.09%
Girish K.S30.48%19.09%
Nico Pitre20.32%19.09%
Total627100.00%11100.00%


static void tifm_sd_end_cmd(unsigned long data) { struct tifm_sd *host = (struct tifm_sd*)data; struct tifm_dev *sock = host->dev; struct mmc_host *mmc = tifm_get_drvdata(sock); struct mmc_request *mrq; struct mmc_data *r_data = NULL; unsigned long flags; spin_lock_irqsave(&sock->lock, flags); del_timer(&host->timer); mrq = host->req; host->req = NULL; if (!mrq) { pr_err(" %s : no request to complete?\n", dev_name(&sock->dev)); spin_unlock_irqrestore(&sock->lock, flags); return; } r_data = mrq->cmd->data; if (r_data) { if (host->no_dma) { writel((~TIFM_MMCSD_BUFINT) & readl(sock->addr + SOCK_MMCSD_INT_ENABLE), sock->addr + SOCK_MMCSD_INT_ENABLE); } else { tifm_unmap_sg(sock, &host->bounce_buf, 1, (r_data->flags & MMC_DATA_WRITE) ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); tifm_unmap_sg(sock, r_data->sg, r_data->sg_len, (r_data->flags & MMC_DATA_WRITE) ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); } r_data->bytes_xfered = r_data->blocks - readl(sock->addr + SOCK_MMCSD_NUM_BLOCKS) - 1; r_data->bytes_xfered *= r_data->blksz; r_data->bytes_xfered += r_data->blksz - readl(sock->addr + SOCK_MMCSD_BLOCK_LEN) + 1; } writel((~TIFM_CTRL_LED) & readl(sock->addr + SOCK_CONTROL), sock->addr + SOCK_CONTROL); spin_unlock_irqrestore(&sock->lock, flags); mmc_request_done(mmc, mrq); }

Contributors

PersonTokensPropCommitsCommitProp
Alex Dubov29497.67%666.67%
Kay Sievers41.33%111.11%
Al Viro20.66%111.11%
Girish K.S10.33%111.11%
Total301100.00%9100.00%


static void tifm_sd_abort(unsigned long data) { struct tifm_sd *host = (struct tifm_sd*)data; pr_err("%s : card failed to respond for a long period of time " "(%x, %x)\n", dev_name(&host->dev->dev), host->req->cmd->opcode, host->cmd_flags); tifm_eject(host->dev); }

Contributors

PersonTokensPropCommitsCommitProp
Alex Dubov5291.23%466.67%
Kay Sievers47.02%116.67%
Girish K.S11.75%116.67%
Total57100.00%6100.00%


static void tifm_sd_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct tifm_sd *host = mmc_priv(mmc); struct tifm_dev *sock = host->dev; unsigned int clk_div1, clk_div2; unsigned long flags; spin_lock_irqsave(&sock->lock, flags); dev_dbg(&sock->dev, "ios: clock = %u, vdd = %x, bus_mode = %x, " "chip_select = %x, power_mode = %x, bus_width = %x\n", ios->clock, ios->vdd, ios->bus_mode, ios->chip_select, ios->power_mode, ios->bus_width); if (ios->bus_width == MMC_BUS_WIDTH_4) { writel(TIFM_MMCSD_4BBUS | readl(sock->addr + SOCK_MMCSD_CONFIG), sock->addr + SOCK_MMCSD_CONFIG); } else { writel((~TIFM_MMCSD_4BBUS) & readl(sock->addr + SOCK_MMCSD_CONFIG), sock->addr + SOCK_MMCSD_CONFIG); } if (ios->clock) { clk_div1 = 20000000 / ios->clock; if (!clk_div1) clk_div1 = 1; clk_div2 = 24000000 / ios->clock; if (!clk_div2) clk_div2 = 1; if ((20000000 / clk_div1) > ios->clock) clk_div1++; if ((24000000 / clk_div2) > ios->clock) clk_div2++; if ((20000000 / clk_div1) > (24000000 / clk_div2)) { host->clk_freq = 20000000; host->clk_div = clk_div1; writel((~TIFM_CTRL_FAST_CLK) & readl(sock->addr + SOCK_CONTROL), sock->addr + SOCK_CONTROL); } else { host->clk_freq = 24000000; host->clk_div = clk_div2; writel(TIFM_CTRL_FAST_CLK | readl(sock->addr + SOCK_CONTROL), sock->addr + SOCK_CONTROL); } } else { host->clk_div = 0; } host->clk_div &= TIFM_MMCSD_CLKMASK; writel(host->clk_div | ((~TIFM_MMCSD_CLKMASK) & readl(sock->addr + SOCK_MMCSD_CONFIG)), sock->addr + SOCK_MMCSD_CONFIG); host->open_drain = (ios->bus_mode == MMC_BUSMODE_OPENDRAIN); /* chip_select : maybe later */ //vdd //power is set before probe / after remove spin_unlock_irqrestore(&sock->lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Alex Dubov373100.00%3100.00%
Total373100.00%3100.00%


static int tifm_sd_ro(struct mmc_host *mmc) { int rc = 0; struct tifm_sd *host = mmc_priv(mmc); struct tifm_dev *sock = host->dev; unsigned long flags; spin_lock_irqsave(&sock->lock, flags); if (TIFM_MMCSD_CARD_RO & readl(sock->addr + SOCK_PRESENT_STATE)) rc = 1; spin_unlock_irqrestore(&sock->lock, flags); return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Alex Dubov79100.00%2100.00%
Total79100.00%2100.00%

static const struct mmc_host_ops tifm_sd_ops = { .request = tifm_sd_request, .set_ios = tifm_sd_ios, .get_ro = tifm_sd_ro };
static int tifm_sd_initialize_host(struct tifm_sd *host) { int rc; unsigned int host_status = 0; struct tifm_dev *sock = host->dev; writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE); mmiowb(); host->clk_div = 61; host->clk_freq = 20000000; writel(TIFM_MMCSD_RESET, sock->addr + SOCK_MMCSD_SYSTEM_CONTROL); writel(host->clk_div | TIFM_MMCSD_POWER, sock->addr + SOCK_MMCSD_CONFIG); /* wait up to 0.51 sec for reset */ for (rc = 32; rc <= 256; rc <<= 1) { if (1 & readl(sock->addr + SOCK_MMCSD_SYSTEM_STATUS)) { rc = 0; break; } msleep(rc); } if (rc) { pr_err("%s : controller failed to reset\n", dev_name(&sock->dev)); return -ENODEV; } writel(0, sock->addr + SOCK_MMCSD_NUM_BLOCKS); writel(host->clk_div | TIFM_MMCSD_POWER, sock->addr + SOCK_MMCSD_CONFIG); writel(TIFM_MMCSD_RXDE, sock->addr + SOCK_MMCSD_BUFFER_CONFIG); // command timeout fixed to 64 clocks for now writel(64, sock->addr + SOCK_MMCSD_COMMAND_TO); writel(TIFM_MMCSD_INAB, sock->addr + SOCK_MMCSD_COMMAND); for (rc = 16; rc <= 64; rc <<= 1) { host_status = readl(sock->addr + SOCK_MMCSD_STATUS); writel(host_status, sock->addr + SOCK_MMCSD_STATUS); if (!(host_status & TIFM_MMCSD_ERRMASK) && (host_status & TIFM_MMCSD_EOC)) { rc = 0; break; } msleep(rc); } if (rc) { pr_err("%s : card not ready - probe failed on initialization\n", dev_name(&sock->dev)); return -ENODEV; } writel(TIFM_MMCSD_CERR | TIFM_MMCSD_BRS | TIFM_MMCSD_EOC | TIFM_MMCSD_ERRMASK, sock->addr + SOCK_MMCSD_INT_ENABLE); mmiowb(); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Alex Dubov30696.53%457.14%
Kay Sievers82.52%114.29%
Girish K.S20.63%114.29%
David Howells10.32%114.29%
Total317100.00%7100.00%


static int tifm_sd_probe(struct tifm_dev *sock) { struct mmc_host *mmc; struct tifm_sd *host; int rc = -EIO; if (!(TIFM_SOCK_STATE_OCCUPIED & readl(sock->addr + SOCK_PRESENT_STATE))) { pr_warn("%s : card gone, unexpectedly\n", dev_name(&sock->dev)); return rc; } mmc = mmc_alloc_host(sizeof(struct tifm_sd), &sock->dev); if (!mmc) return -ENOMEM; host = mmc_priv(mmc); tifm_set_drvdata(sock, mmc); host->dev = sock; host->timeout_jiffies = msecs_to_jiffies(1000); tasklet_init(&host->finish_tasklet, tifm_sd_end_cmd, (unsigned long)host); setup_timer(&host->timer, tifm_sd_abort, (unsigned long)host); mmc->ops = &tifm_sd_ops; mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; mmc->caps = MMC_CAP_4_BIT_DATA; mmc->f_min = 20000000 / 60; mmc->f_max = 24000000; mmc->max_blk_count = 2048; mmc->max_segs = mmc->max_blk_count; mmc->max_blk_size = min(TIFM_MMCSD_MAX_BLOCK_SIZE, PAGE_SIZE); mmc->max_seg_size = mmc->max_blk_count * mmc->max_blk_size; mmc->max_req_size = mmc->max_seg_size; sock->card_event = tifm_sd_card_event; sock->data_event = tifm_sd_data_event; rc = tifm_sd_initialize_host(host); if (!rc) rc = mmc_add_host(mmc); if (!rc) return 0; mmc_free_host(mmc); return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Alex Dubov25593.07%654.55%
Pierre Ossman134.74%218.18%
Kay Sievers41.46%19.09%
Martin K. Petersen10.36%19.09%
Joe Perches10.36%19.09%
Total274100.00%11100.00%


static void tifm_sd_remove(struct tifm_dev *sock) { struct mmc_host *mmc = tifm_get_drvdata(sock); struct tifm_sd *host = mmc_priv(mmc); unsigned long flags; spin_lock_irqsave(&sock->lock, flags); host->eject = 1; writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE); mmiowb(); spin_unlock_irqrestore(&sock->lock, flags); tasklet_kill(&host->finish_tasklet); spin_lock_irqsave(&sock->lock, flags); if (host->req) { writel(TIFM_FIFO_INT_SETALL, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); writel(0, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET); host->req->cmd->error = -ENOMEDIUM; if (host->req->stop) host->req->stop->error = -ENOMEDIUM; tasklet_schedule(&host->finish_tasklet); } spin_unlock_irqrestore(&sock->lock, flags); mmc_remove_host(mmc); dev_dbg(&sock->dev, "after remove\n"); mmc_free_host(mmc); }

Contributors

PersonTokensPropCommitsCommitProp
Alex Dubov18797.91%685.71%
Pierre Ossman42.09%114.29%
Total191100.00%7100.00%

#ifdef CONFIG_PM
static int tifm_sd_suspend(struct tifm_dev *sock, pm_message_t state) { return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Alex Dubov1694.12%266.67%
Ulf Hansson15.88%133.33%
Total17100.00%3100.00%


static int tifm_sd_resume(struct tifm_dev *sock) { struct mmc_host *mmc = tifm_get_drvdata(sock); struct tifm_sd *host = mmc_priv(mmc); int rc; rc = tifm_sd_initialize_host(host); dev_dbg(&sock->dev, "resume initialize %d\n", rc); if (rc) host->eject = 1; return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Alex Dubov66100.00%2100.00%
Total66100.00%2100.00%

#else #define tifm_sd_suspend NULL #define tifm_sd_resume NULL #endif /* CONFIG_PM */ static struct tifm_device_id tifm_sd_id_tbl[] = { { TIFM_TYPE_SD }, { } }; static struct tifm_driver tifm_sd_driver = { .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE }, .id_table = tifm_sd_id_tbl, .probe = tifm_sd_probe, .remove = tifm_sd_remove, .suspend = tifm_sd_suspend, .resume = tifm_sd_resume };
static int __init tifm_sd_init(void) { return tifm_register_driver(&tifm_sd_driver); }

Contributors

PersonTokensPropCommitsCommitProp
Alex Dubov16100.00%1100.00%
Total16100.00%1100.00%


static void __exit tifm_sd_exit(void) { tifm_unregister_driver(&tifm_sd_driver); }

Contributors

PersonTokensPropCommitsCommitProp
Alex Dubov15100.00%1100.00%
Total15100.00%1100.00%

MODULE_AUTHOR("Alex Dubov"); MODULE_DESCRIPTION("TI FlashMedia SD driver"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(tifm, tifm_sd_id_tbl); MODULE_VERSION(DRIVER_VERSION); module_init(tifm_sd_init); module_exit(tifm_sd_exit);

Overall Contributors

PersonTokensPropCommitsCommitProp
Alex Dubov555297.45%2153.85%
Pierre Ossman671.18%410.26%
Kay Sievers320.56%12.56%
Jens Axboe160.28%12.56%
Girish K.S70.12%12.56%
Roel Kluin60.11%12.56%
Al Viro50.09%25.13%
Paul Gortmaker30.05%12.56%
Nico Pitre20.04%12.56%
Rusty Russell20.04%12.56%
Martin K. Petersen10.02%12.56%
David Howells10.02%12.56%
Joe Perches10.02%12.56%
Ulf Hansson10.02%12.56%
Adrian Bunk10.02%12.56%
Total5697100.00%39100.00%
Directory: drivers/mmc/host
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.