// SPDX-License-Identifier: GPL-2.0-or-later
/* Realtek PCI-Express SD/MMC Card Interface driver
*
* Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
*
* Author:
* Wei WANG <wei_wang@realsil.com.cn>
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/highmem.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/sd.h>
#include <linux/mmc/sdio.h>
#include <linux/mmc/card.h>
#include <linux/rtsx_pci.h>
#include <asm/unaligned.h>
#include <linux/pm_runtime.h>
struct realtek_pci_sdmmc {
struct platform_device *pdev;
struct rtsx_pcr *pcr;
struct mmc_host *mmc;
struct mmc_request *mrq;
#define SDMMC_WORKQ_NAME "rtsx_pci_sdmmc_workq"
struct work_struct work;
struct mutex host_mutex;
u8 ssc_depth;
unsigned int clock;
bool vpclk;
bool double_clk;
bool eject;
bool initial_mode;
int prev_power_state;
int sg_count;
s32 cookie;
int cookie_sg_count;
bool using_cookie;
};
static int sdmmc_init_sd_express(struct mmc_host *mmc, struct mmc_ios *ios);
static inline struct device *sdmmc_dev(struct realtek_pci_sdmmc *host)
{
return &(host->pdev->dev);
}
static inline void sd_clear_error(struct realtek_pci_sdmmc *host)
{
rtsx_pci_write_register(host->pcr, CARD_STOP,
SD_STOP | SD_CLR_ERR, SD_STOP | SD_CLR_ERR);
}
#ifdef DEBUG
static void dump_reg_range(struct realtek_pci_sdmmc *host, u16 start, u16 end)
{
u16 len = end - start + 1;
int i;
u8 data[8];
for (i = 0; i < len; i += 8) {
int j;
int n = min(8, len - i);
memset(&data, 0, sizeof(data));
for (j = 0; j < n; j++)
rtsx_pci_read_register(host->pcr, start + i + j,
data + j);
dev_dbg(sdmmc_dev(host), "0x%04X(%d): %8ph\n",
start + i, n, data);
}
}
static void sd_print_debug_regs(struct realtek_pci_sdmmc *host)
{
dump_reg_range(host, 0xFDA0, 0xFDB3);
dump_reg_range(host, 0xFD52, 0xFD69);
}
#else
#define sd_print_debug_regs(host)
#endif /* DEBUG */
static inline int sd_get_cd_int(struct realtek_pci_sdmmc *host)
{
return rtsx_pci_readl(host->pcr, RTSX_BIPR) & SD_EXIST;
}
static void sd_cmd_set_sd_cmd(struct rtsx_pcr *pcr, struct mmc_command *cmd)
{
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD0, 0xFF,
SD_CMD_START | cmd->opcode);
rtsx_pci_write_be32(pcr, SD_CMD1, cmd->arg);
}
static void sd_cmd_set_data_len(struct rtsx_pcr *pcr, u16 blocks, u16 blksz)
{
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_L, 0xFF, blocks);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_H, 0xFF, blocks >> 8);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, blksz);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_H, 0xFF, blksz >> 8);
}
static int sd_response_type(struct mmc_command *cmd)
{
switch (mmc_resp_type(cmd)) {
case MMC_RSP_NONE:
return SD_RSP_TYPE_R0;
case MMC_RSP_R1:
return SD_RSP_TYPE_R1;
case MMC_RSP_R1_NO_CRC:
return SD_RSP_TYPE_R1 | SD_NO_CHECK_CRC7;
case MMC_RSP_R1B:
return SD_RSP_TYPE_R1b;
case MMC_RSP_R2:
return SD_RSP_TYPE_R2;
case MMC_RSP_R3:
return SD_RSP_TYPE_R3;
default:
return -EINVAL;
}
}
static int sd_status_index(int resp_type)
{
if (resp_type == SD_RSP_TYPE_R0)
return 0;
else if (resp_type == SD_RSP_TYPE_R2)
return 16;
return 5;
}
/*
* sd_pre_dma_transfer - do dma_map_sg() or using cookie
*
* @pre: if called in pre_req()
* return:
* 0 - do dma_map_sg()
* 1 - using cookie
*/
static int sd_pre_dma_transfer(struct realtek_pci_sdmmc *host,
struct mmc_data *data, bool pre)
{
struct rtsx_pcr *pcr = host->pcr;
int read = data->flags & MMC_DATA_READ;
int count = 0;
int using_cookie = 0;
if (!pre && data->host_cookie && data->host_cookie != host->cookie) {
dev_err(sdmmc_dev(host),
"error: data->host_cookie = %d, host->cookie = %d\n",
data->host_cookie, host->cookie);
data->host_cookie = 0;
}
if (pre || data->host_cookie != host->cookie) {
count = rtsx_pci_dma_map_sg(pcr, data->sg, data->sg_len, read);
} else {
count = host->cookie_sg_count;
using_cookie = 1;
}
if (pre) {
host->cookie_sg_count = count;
if (++host->cookie < 0)
host->cookie = 1;
data->host_cookie = host->cookie;
} else {
host->sg_count = count;
}
return using_cookie;
}
static void sdmmc_pre_req(struct mmc_host *mmc, struct mmc_request *mrq)
{
struct realtek_pci_sdmmc *host = mmc_priv(mmc);
struct mmc_data *data = mrq->data;
if (data->host_cookie) {
dev_err(sdmmc_dev(host),
"error: reset data->host_cookie = %d\n",
data->host_cookie);
data->host_cookie = 0;
}
sd_pre_dma_transfer(host, data, true);
dev_dbg(sdmmc_dev(host), "pre dma sg: %d\n", host->cookie_sg_count);
}
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;
}
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;
u32 arg = cmd->arg
|