diff options
Diffstat (limited to 'drivers/mmc/host/rtsx_pci_sdmmc.c')
| -rw-r--r-- | drivers/mmc/host/rtsx_pci_sdmmc.c | 418 | 
1 files changed, 68 insertions, 350 deletions
| diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index 5fb994f9a653..0b9ded13a3ae 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -31,28 +31,14 @@  #include <linux/mfd/rtsx_pci.h>  #include <asm/unaligned.h> -struct realtek_next { -	unsigned int	sg_count; -	s32		cookie; -}; -  struct realtek_pci_sdmmc {  	struct platform_device	*pdev;  	struct rtsx_pcr		*pcr;  	struct mmc_host		*mmc;  	struct mmc_request	*mrq; -	struct mmc_command	*cmd; -	struct mmc_data		*data; - -	spinlock_t		lock; -	struct timer_list	timer; -	struct tasklet_struct	cmd_tasklet; -	struct tasklet_struct	data_tasklet; -	struct tasklet_struct	finish_tasklet; - -	u8			rsp_type; -	u8			rsp_len; -	int			sg_count; + +	struct mutex		host_mutex; +  	u8			ssc_depth;  	unsigned int		clock;  	bool			vpclk; @@ -62,13 +48,8 @@ struct realtek_pci_sdmmc {  	int			power_state;  #define SDMMC_POWER_ON		1  #define SDMMC_POWER_OFF		0 - -	struct realtek_next	next_data;  }; -static int sd_start_multi_rw(struct realtek_pci_sdmmc *host, -		struct mmc_request *mrq); -  static inline struct device *sdmmc_dev(struct realtek_pci_sdmmc *host)  {  	return &(host->pdev->dev); @@ -105,95 +86,6 @@ static void sd_print_debug_regs(struct realtek_pci_sdmmc *host)  #define sd_print_debug_regs(host)  #endif /* DEBUG */ -static void sd_isr_done_transfer(struct platform_device *pdev) -{ -	struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev); - -	spin_lock(&host->lock); -	if (host->cmd) -		tasklet_schedule(&host->cmd_tasklet); -	if (host->data) -		tasklet_schedule(&host->data_tasklet); -	spin_unlock(&host->lock); -} - -static void sd_request_timeout(unsigned long host_addr) -{ -	struct realtek_pci_sdmmc *host = (struct realtek_pci_sdmmc *)host_addr; -	unsigned long flags; - -	spin_lock_irqsave(&host->lock, flags); - -	if (!host->mrq) { -		dev_err(sdmmc_dev(host), "error: no request exist\n"); -		goto out; -	} - -	if (host->cmd) -		host->cmd->error = -ETIMEDOUT; -	if (host->data) -		host->data->error = -ETIMEDOUT; - -	dev_dbg(sdmmc_dev(host), "timeout for request\n"); - -out: -	tasklet_schedule(&host->finish_tasklet); -	spin_unlock_irqrestore(&host->lock, flags); -} - -static void sd_finish_request(unsigned long host_addr) -{ -	struct realtek_pci_sdmmc *host = (struct realtek_pci_sdmmc *)host_addr; -	struct rtsx_pcr *pcr = host->pcr; -	struct mmc_request *mrq; -	struct mmc_command *cmd; -	struct mmc_data *data; -	unsigned long flags; -	bool any_error; - -	spin_lock_irqsave(&host->lock, flags); - -	del_timer(&host->timer); -	mrq = host->mrq; -	if (!mrq) { -		dev_err(sdmmc_dev(host), "error: no request need finish\n"); -		goto out; -	} - -	cmd = mrq->cmd; -	data = mrq->data; - -	any_error = (mrq->sbc && mrq->sbc->error) || -		(mrq->stop && mrq->stop->error) || -		(cmd && cmd->error) || (data && data->error); - -	if (any_error) { -		rtsx_pci_stop_cmd(pcr); -		sd_clear_error(host); -	} - -	if (data) { -		if (any_error) -			data->bytes_xfered = 0; -		else -			data->bytes_xfered = data->blocks * data->blksz; - -		if (!data->host_cookie) -			rtsx_pci_dma_unmap_sg(pcr, data->sg, data->sg_len, -					data->flags & MMC_DATA_READ); - -	} - -	host->mrq = NULL; -	host->cmd = NULL; -	host->data = NULL; - -out: -	spin_unlock_irqrestore(&host->lock, flags); -	mutex_unlock(&pcr->pcr_mutex); -	mmc_request_done(host->mmc, mrq); -} -  static int sd_read_data(struct realtek_pci_sdmmc *host, u8 *cmd, u16 byte_cnt,  		u8 *buf, int buf_len, int timeout)  { @@ -311,7 +203,8 @@ static int sd_write_data(struct realtek_pci_sdmmc *host, u8 *cmd, u16 byte_cnt,  	return 0;  } -static void sd_send_cmd(struct realtek_pci_sdmmc *host, struct mmc_command *cmd) +static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host, +		struct mmc_command *cmd)  {  	struct rtsx_pcr *pcr = host->pcr;  	u8 cmd_idx = (u8)cmd->opcode; @@ -319,14 +212,11 @@ static void sd_send_cmd(struct realtek_pci_sdmmc *host, struct mmc_command *cmd)  	int err = 0;  	int timeout = 100;  	int i; +	u8 *ptr; +	int stat_idx = 0;  	u8 rsp_type;  	int rsp_len = 5; -	unsigned long flags; - -	if (host->cmd) -		dev_err(sdmmc_dev(host), "error: cmd already exist\n"); - -	host->cmd = cmd; +	bool clock_toggled = false;  	dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n",  			__func__, cmd_idx, arg); @@ -361,8 +251,6 @@ static void sd_send_cmd(struct realtek_pci_sdmmc *host, struct mmc_command *cmd)  		err = -EINVAL;  		goto out;  	} -	host->rsp_type = rsp_type; -	host->rsp_len = rsp_len;  	if (rsp_type == SD_RSP_TYPE_R1b)  		timeout = 3000; @@ -372,6 +260,8 @@ static void sd_send_cmd(struct realtek_pci_sdmmc *host, struct mmc_command *cmd)  				0xFF, SD_CLK_TOGGLE_EN);  		if (err < 0)  			goto out; + +		clock_toggled = true;  	}  	rtsx_pci_init_cmd(pcr); @@ -395,60 +285,25 @@ static void sd_send_cmd(struct realtek_pci_sdmmc *host, struct mmc_command *cmd)  		/* Read data from ping-pong buffer */  		for (i = PPBUF_BASE2; i < PPBUF_BASE2 + 16; i++)  			rtsx_pci_add_cmd(pcr, READ_REG_CMD, (u16)i, 0, 0); +		stat_idx = 16;  	} else if (rsp_type != SD_RSP_TYPE_R0) {  		/* Read data from SD_CMDx registers */  		for (i = SD_CMD0; i <= SD_CMD4; i++)  			rtsx_pci_add_cmd(pcr, READ_REG_CMD, (u16)i, 0, 0); +		stat_idx = 5;  	}  	rtsx_pci_add_cmd(pcr, READ_REG_CMD, SD_STAT1, 0, 0); -	mod_timer(&host->timer, jiffies + msecs_to_jiffies(timeout)); - -	spin_lock_irqsave(&pcr->lock, flags); -	pcr->trans_result = TRANS_NOT_READY; -	rtsx_pci_send_cmd_no_wait(pcr); -	spin_unlock_irqrestore(&pcr->lock, flags); - -	return; - -out: -	cmd->error = err; -	tasklet_schedule(&host->finish_tasklet); -} - -static void sd_get_rsp(unsigned long host_addr) -{ -	struct realtek_pci_sdmmc *host = (struct realtek_pci_sdmmc *)host_addr; -	struct rtsx_pcr *pcr = host->pcr; -	struct mmc_command *cmd; -	int i, err = 0, stat_idx; -	u8 *ptr, rsp_type; -	unsigned long flags; - -	spin_lock_irqsave(&host->lock, flags); - -	cmd = host->cmd; -	host->cmd = NULL; - -	if (!cmd) { -		dev_err(sdmmc_dev(host), "error: cmd not exist\n"); +	err = rtsx_pci_send_cmd(pcr, timeout); +	if (err < 0) { +		sd_print_debug_regs(host); +		sd_clear_error(host); +		dev_dbg(sdmmc_dev(host), +			"rtsx_pci_send_cmd error (err = %d)\n", err);  		goto out;  	} -	spin_lock(&pcr->lock); -	if (pcr->trans_result == TRANS_NO_DEVICE) -		err = -ENODEV; -	else if (pcr->trans_result != TRANS_RESULT_OK) -		err = -EINVAL; -	spin_unlock(&pcr->lock); - -	if (err < 0) -		goto out; - -	rsp_type = host->rsp_type; -	stat_idx = host->rsp_len; -  	if (rsp_type == SD_RSP_TYPE_R0) {  		err = 0;  		goto out; @@ -485,106 +340,26 @@ static void sd_get_rsp(unsigned long host_addr)  				cmd->resp[0]);  	} -	if (cmd == host->mrq->sbc) { -		sd_send_cmd(host, host->mrq->cmd); -		spin_unlock_irqrestore(&host->lock, flags); -		return; -	} - -	if (cmd == host->mrq->stop) -		goto out; - -	if (cmd->data) { -		sd_start_multi_rw(host, host->mrq); -		spin_unlock_irqrestore(&host->lock, flags); -		return; -	} -  out:  	cmd->error = err; -	tasklet_schedule(&host->finish_tasklet); -	spin_unlock_irqrestore(&host->lock, flags); -} - -static int sd_pre_dma_transfer(struct realtek_pci_sdmmc *host, -			struct mmc_data *data, struct realtek_next *next) -{ -	struct rtsx_pcr *pcr = host->pcr; -	int read = data->flags & MMC_DATA_READ; -	int sg_count = 0; - -	if (!next && data->host_cookie && -		data->host_cookie != host->next_data.cookie) { -		dev_err(sdmmc_dev(host), -			"error: invalid cookie data[%d] host[%d]\n", -			data->host_cookie, host->next_data.cookie); -		data->host_cookie = 0; -	} - -	if (next || (!next && data->host_cookie != host->next_data.cookie)) -		sg_count = rtsx_pci_dma_map_sg(pcr, -				data->sg, data->sg_len, read); -	else -		sg_count = host->next_data.sg_count; - -	if (next) { -		next->sg_count = sg_count; -		if (++next->cookie < 0) -			next->cookie = 1; -		data->host_cookie = next->cookie; -	} - -	return sg_count; -} - -static void sdmmc_pre_req(struct mmc_host *mmc, struct mmc_request *mrq, -		bool is_first_req) -{ -	struct realtek_pci_sdmmc *host = mmc_priv(mmc); -	struct mmc_data *data = mrq->data; - -	if (data->host_cookie) { -		dev_err(sdmmc_dev(host), -			"error: descard already cookie data[%d]\n", -			data->host_cookie); -		data->host_cookie = 0; -	} - -	dev_dbg(sdmmc_dev(host), "dma sg prepared: %d\n", -		sd_pre_dma_transfer(host, data, &host->next_data)); -} - -static void sdmmc_post_req(struct mmc_host *mmc, struct mmc_request *mrq, -		int err) -{ -	struct realtek_pci_sdmmc *host = mmc_priv(mmc); -	struct rtsx_pcr *pcr = host->pcr; -	struct mmc_data *data = mrq->data; -	int read = data->flags & MMC_DATA_READ; - -	rtsx_pci_dma_unmap_sg(pcr, data->sg, data->sg_len, read); -	data->host_cookie = 0; +	if (err && clock_toggled) +		rtsx_pci_write_register(pcr, SD_BUS_STAT, +				SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0);  } -static int sd_start_multi_rw(struct realtek_pci_sdmmc *host, -		struct mmc_request *mrq) +static int sd_rw_multi(struct realtek_pci_sdmmc *host, struct mmc_request *mrq)  {  	struct rtsx_pcr *pcr = host->pcr;  	struct mmc_host *mmc = host->mmc;  	struct mmc_card *card = mmc->card;  	struct mmc_data *data = mrq->data;  	int uhs = mmc_card_uhs(card); -	int read = data->flags & MMC_DATA_READ; +	int read = (data->flags & MMC_DATA_READ) ? 1 : 0;  	u8 cfg2, trans_mode;  	int err;  	size_t data_len = data->blksz * data->blocks; -	if (host->data) -		dev_err(sdmmc_dev(host), "error: data already exist\n"); - -	host->data = data; -  	if (read) {  		cfg2 = SD_CALCULATE_CRC7 | SD_CHECK_CRC16 |  			SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_0; @@ -635,54 +410,15 @@ static int sd_start_multi_rw(struct realtek_pci_sdmmc *host,  	rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER,  			SD_TRANSFER_END, SD_TRANSFER_END); -	mod_timer(&host->timer, jiffies + 10 * HZ);  	rtsx_pci_send_cmd_no_wait(pcr); -	err = rtsx_pci_dma_transfer(pcr, data->sg, host->sg_count, read); -	if (err < 0) { -		data->error = err; -		tasklet_schedule(&host->finish_tasklet); -	} -	return 0; -} - -static void sd_finish_multi_rw(unsigned long host_addr) -{ -	struct realtek_pci_sdmmc *host = (struct realtek_pci_sdmmc *)host_addr; -	struct rtsx_pcr *pcr = host->pcr; -	struct mmc_data *data; -	int err = 0; -	unsigned long flags; - -	spin_lock_irqsave(&host->lock, flags); - -	if (!host->data) { -		dev_err(sdmmc_dev(host), "error: no data exist\n"); -		goto out; -	} - -	data = host->data; -	host->data = NULL; - -	if (pcr->trans_result == TRANS_NO_DEVICE) -		err = -ENODEV; -	else if (pcr->trans_result != TRANS_RESULT_OK) -		err = -EINVAL; - +	err = rtsx_pci_transfer_data(pcr, data->sg, data->sg_len, read, 10000);  	if (err < 0) { -		data->error = err; -		goto out; -	} - -	if (!host->mrq->sbc && data->stop) { -		sd_send_cmd(host, data->stop); -		spin_unlock_irqrestore(&host->lock, flags); -		return; +		sd_clear_error(host); +		return err;  	} -out: -	tasklet_schedule(&host->finish_tasklet); -	spin_unlock_irqrestore(&host->lock, flags); +	return 0;  }  static inline void sd_enable_initial_mode(struct realtek_pci_sdmmc *host) @@ -901,13 +637,6 @@ static int sd_tuning_rx(struct realtek_pci_sdmmc *host, u8 opcode)  	return 0;  } -static inline bool sd_use_muti_rw(struct mmc_command *cmd) -{ -	return mmc_op_multi(cmd->opcode) || -		(cmd->opcode == MMC_READ_SINGLE_BLOCK) || -		(cmd->opcode == MMC_WRITE_BLOCK); -} -  static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)  {  	struct realtek_pci_sdmmc *host = mmc_priv(mmc); @@ -916,14 +645,6 @@ static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)  	struct mmc_data *data = mrq->data;  	unsigned int data_size = 0;  	int err; -	unsigned long flags; - -	mutex_lock(&pcr->pcr_mutex); -	spin_lock_irqsave(&host->lock, flags); - -	if (host->mrq) -		dev_err(sdmmc_dev(host), "error: request already exist\n"); -	host->mrq = mrq;  	if (host->eject) {  		cmd->error = -ENOMEDIUM; @@ -936,6 +657,8 @@ static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)  		goto finish;  	} +	mutex_lock(&pcr->pcr_mutex); +  	rtsx_pci_start_run(pcr);  	rtsx_pci_switch_clock(pcr, host->clock, host->ssc_depth, @@ -944,28 +667,46 @@ static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)  	rtsx_pci_write_register(pcr, CARD_SHARE_MODE,  			CARD_SHARE_MASK, CARD_SHARE_48_SD); +	mutex_lock(&host->host_mutex); +	host->mrq = mrq; +	mutex_unlock(&host->host_mutex); +  	if (mrq->data)  		data_size = data->blocks * data->blksz; -	if (sd_use_muti_rw(cmd)) -		host->sg_count = sd_pre_dma_transfer(host, data, NULL); +	if (!data_size || mmc_op_multi(cmd->opcode) || +			(cmd->opcode == MMC_READ_SINGLE_BLOCK) || +			(cmd->opcode == MMC_WRITE_BLOCK)) { +		sd_send_cmd_get_rsp(host, cmd); -	if (!data_size || sd_use_muti_rw(cmd)) { -		if (mrq->sbc) -			sd_send_cmd(host, mrq->sbc); -		else -			sd_send_cmd(host, cmd); -		spin_unlock_irqrestore(&host->lock, flags); +		if (!cmd->error && data_size) { +			sd_rw_multi(host, mrq); + +			if (mmc_op_multi(cmd->opcode) && mrq->stop) +				sd_send_cmd_get_rsp(host, mrq->stop); +		}  	} else { -		spin_unlock_irqrestore(&host->lock, flags);  		sd_normal_rw(host, mrq); -		tasklet_schedule(&host->finish_tasklet);  	} -	return; + +	if (mrq->data) { +		if (cmd->error || data->error) +			data->bytes_xfered = 0; +		else +			data->bytes_xfered = data->blocks * data->blksz; +	} + +	mutex_unlock(&pcr->pcr_mutex);  finish: -	tasklet_schedule(&host->finish_tasklet); -	spin_unlock_irqrestore(&host->lock, flags); +	if (cmd->error) +		dev_dbg(sdmmc_dev(host), "cmd->error = %d\n", cmd->error); + +	mutex_lock(&host->host_mutex); +	host->mrq = NULL; +	mutex_unlock(&host->host_mutex); + +	mmc_request_done(mmc, mrq);  }  static int sd_set_bus_width(struct realtek_pci_sdmmc *host, @@ -1400,8 +1141,6 @@ out:  }  static const struct mmc_host_ops realtek_pci_sdmmc_ops = { -	.pre_req = sdmmc_pre_req, -	.post_req = sdmmc_post_req,  	.request = sdmmc_request,  	.set_ios = sdmmc_set_ios,  	.get_ro = sdmmc_get_ro, @@ -1465,7 +1204,6 @@ static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev)  	struct realtek_pci_sdmmc *host;  	struct rtsx_pcr *pcr;  	struct pcr_handle *handle = pdev->dev.platform_data; -	unsigned long host_addr;  	if (!handle)  		return -ENXIO; @@ -1489,15 +1227,8 @@ static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev)  	pcr->slots[RTSX_SD_CARD].p_dev = pdev;  	pcr->slots[RTSX_SD_CARD].card_event = rtsx_pci_sdmmc_card_event; -	host_addr = (unsigned long)host; -	host->next_data.cookie = 1; -	setup_timer(&host->timer, sd_request_timeout, host_addr); -	tasklet_init(&host->cmd_tasklet, sd_get_rsp, host_addr); -	tasklet_init(&host->data_tasklet, sd_finish_multi_rw, host_addr); -	tasklet_init(&host->finish_tasklet, sd_finish_request, host_addr); -	spin_lock_init(&host->lock); +	mutex_init(&host->host_mutex); -	pcr->slots[RTSX_SD_CARD].done_transfer = sd_isr_done_transfer;  	realtek_init_host(host);  	mmc_add_host(mmc); @@ -1510,8 +1241,6 @@ static int rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev)  	struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev);  	struct rtsx_pcr *pcr;  	struct mmc_host *mmc; -	struct mmc_request *mrq; -	unsigned long flags;  	if (!host)  		return 0; @@ -1519,33 +1248,22 @@ static int rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev)  	pcr = host->pcr;  	pcr->slots[RTSX_SD_CARD].p_dev = NULL;  	pcr->slots[RTSX_SD_CARD].card_event = NULL; -	pcr->slots[RTSX_SD_CARD].done_transfer = NULL;  	mmc = host->mmc; -	mrq = host->mrq; -	spin_lock_irqsave(&host->lock, flags); +	mutex_lock(&host->host_mutex);  	if (host->mrq) {  		dev_dbg(&(pdev->dev),  			"%s: Controller removed during transfer\n",  			mmc_hostname(mmc)); -		if (mrq->sbc) -			mrq->sbc->error = -ENOMEDIUM; -		if (mrq->cmd) -			mrq->cmd->error = -ENOMEDIUM; -		if (mrq->stop) -			mrq->stop->error = -ENOMEDIUM; -		if (mrq->data) -			mrq->data->error = -ENOMEDIUM; +		rtsx_pci_complete_unfinished_transfer(pcr); -		tasklet_schedule(&host->finish_tasklet); +		host->mrq->cmd->error = -ENOMEDIUM; +		if (host->mrq->stop) +			host->mrq->stop->error = -ENOMEDIUM; +		mmc_request_done(mmc, host->mrq);  	} -	spin_unlock_irqrestore(&host->lock, flags); - -	del_timer_sync(&host->timer); -	tasklet_kill(&host->cmd_tasklet); -	tasklet_kill(&host->data_tasklet); -	tasklet_kill(&host->finish_tasklet); +	mutex_unlock(&host->host_mutex);  	mmc_remove_host(mmc);  	host->eject = true; | 

