162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* Realtek USB Memstick Card Interface driver
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Author:
762306a36Sopenharmony_ci *   Roger Tseng <rogerable@realtek.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/highmem.h>
1262306a36Sopenharmony_ci#include <linux/delay.h>
1362306a36Sopenharmony_ci#include <linux/platform_device.h>
1462306a36Sopenharmony_ci#include <linux/workqueue.h>
1562306a36Sopenharmony_ci#include <linux/memstick.h>
1662306a36Sopenharmony_ci#include <linux/kthread.h>
1762306a36Sopenharmony_ci#include <linux/rtsx_usb.h>
1862306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1962306a36Sopenharmony_ci#include <linux/mutex.h>
2062306a36Sopenharmony_ci#include <linux/sched.h>
2162306a36Sopenharmony_ci#include <linux/completion.h>
2262306a36Sopenharmony_ci#include <asm/unaligned.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistruct rtsx_usb_ms {
2562306a36Sopenharmony_ci	struct platform_device	*pdev;
2662306a36Sopenharmony_ci	struct rtsx_ucr	*ucr;
2762306a36Sopenharmony_ci	struct memstick_host	*msh;
2862306a36Sopenharmony_ci	struct memstick_request	*req;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	struct mutex		host_mutex;
3162306a36Sopenharmony_ci	struct work_struct	handle_req;
3262306a36Sopenharmony_ci	struct delayed_work	poll_card;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	u8			ssc_depth;
3562306a36Sopenharmony_ci	unsigned int		clock;
3662306a36Sopenharmony_ci	int			power_mode;
3762306a36Sopenharmony_ci	unsigned char           ifmode;
3862306a36Sopenharmony_ci	bool			eject;
3962306a36Sopenharmony_ci	bool			system_suspending;
4062306a36Sopenharmony_ci};
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic inline struct device *ms_dev(struct rtsx_usb_ms *host)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	return &(host->pdev->dev);
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic inline void ms_clear_error(struct rtsx_usb_ms *host)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	struct rtsx_ucr *ucr = host->ucr;
5062306a36Sopenharmony_ci	rtsx_usb_ep0_write_register(ucr, CARD_STOP,
5162306a36Sopenharmony_ci				  MS_STOP | MS_CLR_ERR,
5262306a36Sopenharmony_ci				  MS_STOP | MS_CLR_ERR);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	rtsx_usb_clear_dma_err(ucr);
5562306a36Sopenharmony_ci	rtsx_usb_clear_fsm_err(ucr);
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci#ifdef DEBUG
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic void ms_print_debug_regs(struct rtsx_usb_ms *host)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	struct rtsx_ucr *ucr = host->ucr;
6362306a36Sopenharmony_ci	u16 i;
6462306a36Sopenharmony_ci	u8 *ptr;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	/* Print MS host internal registers */
6762306a36Sopenharmony_ci	rtsx_usb_init_cmd(ucr);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	/* MS_CFG to MS_INT_REG */
7062306a36Sopenharmony_ci	for (i = 0xFD40; i <= 0xFD44; i++)
7162306a36Sopenharmony_ci		rtsx_usb_add_cmd(ucr, READ_REG_CMD, i, 0, 0);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	/* CARD_SHARE_MODE to CARD_GPIO */
7462306a36Sopenharmony_ci	for (i = 0xFD51; i <= 0xFD56; i++)
7562306a36Sopenharmony_ci		rtsx_usb_add_cmd(ucr, READ_REG_CMD, i, 0, 0);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	/* CARD_PULL_CTLx */
7862306a36Sopenharmony_ci	for (i = 0xFD60; i <= 0xFD65; i++)
7962306a36Sopenharmony_ci		rtsx_usb_add_cmd(ucr, READ_REG_CMD, i, 0, 0);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	/* CARD_DATA_SOURCE, CARD_SELECT, CARD_CLK_EN, CARD_PWR_CTL */
8262306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, READ_REG_CMD, CARD_DATA_SOURCE, 0, 0);
8362306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, READ_REG_CMD, CARD_SELECT, 0, 0);
8462306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, READ_REG_CMD, CARD_CLK_EN, 0, 0);
8562306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, READ_REG_CMD, CARD_PWR_CTL, 0, 0);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	rtsx_usb_send_cmd(ucr, MODE_CR, 100);
8862306a36Sopenharmony_ci	rtsx_usb_get_rsp(ucr, 21, 100);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	ptr = ucr->rsp_buf;
9162306a36Sopenharmony_ci	for (i = 0xFD40; i <= 0xFD44; i++)
9262306a36Sopenharmony_ci		dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++));
9362306a36Sopenharmony_ci	for (i = 0xFD51; i <= 0xFD56; i++)
9462306a36Sopenharmony_ci		dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++));
9562306a36Sopenharmony_ci	for (i = 0xFD60; i <= 0xFD65; i++)
9662306a36Sopenharmony_ci		dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++));
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", CARD_DATA_SOURCE, *(ptr++));
9962306a36Sopenharmony_ci	dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", CARD_SELECT, *(ptr++));
10062306a36Sopenharmony_ci	dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", CARD_CLK_EN, *(ptr++));
10162306a36Sopenharmony_ci	dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", CARD_PWR_CTL, *(ptr++));
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci#else
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic void ms_print_debug_regs(struct rtsx_usb_ms *host)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci#endif
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic int ms_pull_ctl_disable_lqfp48(struct rtsx_ucr *ucr)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	rtsx_usb_init_cmd(ucr);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF, 0x55);
11762306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, 0x55);
11862306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, 0x95);
11962306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, 0x55);
12062306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF, 0x55);
12162306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF, 0xA5);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	return rtsx_usb_send_cmd(ucr, MODE_C, 100);
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic int ms_pull_ctl_disable_qfn24(struct rtsx_ucr *ucr)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	rtsx_usb_init_cmd(ucr);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF, 0x65);
13162306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, 0x55);
13262306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, 0x95);
13362306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, 0x55);
13462306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF, 0x56);
13562306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF, 0x59);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	return rtsx_usb_send_cmd(ucr, MODE_C, 100);
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_cistatic int ms_pull_ctl_enable_lqfp48(struct rtsx_ucr *ucr)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	rtsx_usb_init_cmd(ucr);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF, 0x55);
14562306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, 0x55);
14662306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, 0x95);
14762306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, 0x55);
14862306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF, 0x55);
14962306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF, 0xA5);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	return rtsx_usb_send_cmd(ucr, MODE_C, 100);
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic int ms_pull_ctl_enable_qfn24(struct rtsx_ucr *ucr)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	rtsx_usb_init_cmd(ucr);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF, 0x65);
15962306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, 0x55);
16062306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, 0x95);
16162306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, 0x55);
16262306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF, 0x55);
16362306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF, 0x59);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	return rtsx_usb_send_cmd(ucr, MODE_C, 100);
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic int ms_power_on(struct rtsx_usb_ms *host)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	struct rtsx_ucr *ucr = host->ucr;
17162306a36Sopenharmony_ci	int err;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	dev_dbg(ms_dev(host), "%s\n", __func__);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	rtsx_usb_init_cmd(ucr);
17662306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_SELECT, 0x07, MS_MOD_SEL);
17762306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_SHARE_MODE,
17862306a36Sopenharmony_ci			CARD_SHARE_MASK, CARD_SHARE_MS);
17962306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_CLK_EN,
18062306a36Sopenharmony_ci			MS_CLK_EN, MS_CLK_EN);
18162306a36Sopenharmony_ci	err = rtsx_usb_send_cmd(ucr, MODE_C, 100);
18262306a36Sopenharmony_ci	if (err < 0)
18362306a36Sopenharmony_ci		return err;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	if (CHECK_PKG(ucr, LQFP48))
18662306a36Sopenharmony_ci		err = ms_pull_ctl_enable_lqfp48(ucr);
18762306a36Sopenharmony_ci	else
18862306a36Sopenharmony_ci		err = ms_pull_ctl_enable_qfn24(ucr);
18962306a36Sopenharmony_ci	if (err < 0)
19062306a36Sopenharmony_ci		return err;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	err = rtsx_usb_write_register(ucr, CARD_PWR_CTL,
19362306a36Sopenharmony_ci			POWER_MASK, PARTIAL_POWER_ON);
19462306a36Sopenharmony_ci	if (err)
19562306a36Sopenharmony_ci		return err;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	usleep_range(800, 1000);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	rtsx_usb_init_cmd(ucr);
20062306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL,
20162306a36Sopenharmony_ci			POWER_MASK, POWER_ON);
20262306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_OE,
20362306a36Sopenharmony_ci			MS_OUTPUT_EN, MS_OUTPUT_EN);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	return rtsx_usb_send_cmd(ucr, MODE_C, 100);
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistatic int ms_power_off(struct rtsx_usb_ms *host)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	struct rtsx_ucr *ucr = host->ucr;
21162306a36Sopenharmony_ci	int err;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	dev_dbg(ms_dev(host), "%s\n", __func__);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	rtsx_usb_init_cmd(ucr);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_CLK_EN, MS_CLK_EN, 0);
21862306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_OE, MS_OUTPUT_EN, 0);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	err = rtsx_usb_send_cmd(ucr, MODE_C, 100);
22162306a36Sopenharmony_ci	if (err < 0)
22262306a36Sopenharmony_ci		return err;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	if (CHECK_PKG(ucr, LQFP48))
22562306a36Sopenharmony_ci		return ms_pull_ctl_disable_lqfp48(ucr);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	return ms_pull_ctl_disable_qfn24(ucr);
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic int ms_transfer_data(struct rtsx_usb_ms *host, unsigned char data_dir,
23162306a36Sopenharmony_ci		u8 tpc, u8 cfg, struct scatterlist *sg)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	struct rtsx_ucr *ucr = host->ucr;
23462306a36Sopenharmony_ci	int err;
23562306a36Sopenharmony_ci	unsigned int length = sg->length;
23662306a36Sopenharmony_ci	u16 sec_cnt = (u16)(length / 512);
23762306a36Sopenharmony_ci	u8 trans_mode, dma_dir, flag;
23862306a36Sopenharmony_ci	unsigned int pipe;
23962306a36Sopenharmony_ci	struct memstick_dev *card = host->msh->card;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	dev_dbg(ms_dev(host), "%s: tpc = 0x%02x, data_dir = %s, length = %d\n",
24262306a36Sopenharmony_ci			__func__, tpc, (data_dir == READ) ? "READ" : "WRITE",
24362306a36Sopenharmony_ci			length);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	if (data_dir == READ) {
24662306a36Sopenharmony_ci		flag = MODE_CDIR;
24762306a36Sopenharmony_ci		dma_dir = DMA_DIR_FROM_CARD;
24862306a36Sopenharmony_ci		if (card->id.type != MEMSTICK_TYPE_PRO)
24962306a36Sopenharmony_ci			trans_mode = MS_TM_NORMAL_READ;
25062306a36Sopenharmony_ci		else
25162306a36Sopenharmony_ci			trans_mode = MS_TM_AUTO_READ;
25262306a36Sopenharmony_ci		pipe = usb_rcvbulkpipe(ucr->pusb_dev, EP_BULK_IN);
25362306a36Sopenharmony_ci	} else {
25462306a36Sopenharmony_ci		flag = MODE_CDOR;
25562306a36Sopenharmony_ci		dma_dir = DMA_DIR_TO_CARD;
25662306a36Sopenharmony_ci		if (card->id.type != MEMSTICK_TYPE_PRO)
25762306a36Sopenharmony_ci			trans_mode = MS_TM_NORMAL_WRITE;
25862306a36Sopenharmony_ci		else
25962306a36Sopenharmony_ci			trans_mode = MS_TM_AUTO_WRITE;
26062306a36Sopenharmony_ci		pipe = usb_sndbulkpipe(ucr->pusb_dev, EP_BULK_OUT);
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	rtsx_usb_init_cmd(ucr);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TPC, 0xFF, tpc);
26662306a36Sopenharmony_ci	if (card->id.type == MEMSTICK_TYPE_PRO) {
26762306a36Sopenharmony_ci		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_SECTOR_CNT_H,
26862306a36Sopenharmony_ci				0xFF, (u8)(sec_cnt >> 8));
26962306a36Sopenharmony_ci		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_SECTOR_CNT_L,
27062306a36Sopenharmony_ci				0xFF, (u8)sec_cnt);
27162306a36Sopenharmony_ci	}
27262306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_TC3,
27562306a36Sopenharmony_ci			0xFF, (u8)(length >> 24));
27662306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_TC2,
27762306a36Sopenharmony_ci			0xFF, (u8)(length >> 16));
27862306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_TC1,
27962306a36Sopenharmony_ci			0xFF, (u8)(length >> 8));
28062306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_TC0, 0xFF,
28162306a36Sopenharmony_ci			(u8)length);
28262306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_CTL,
28362306a36Sopenharmony_ci			0x03 | DMA_PACK_SIZE_MASK, dma_dir | DMA_EN | DMA_512);
28462306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_DATA_SOURCE,
28562306a36Sopenharmony_ci			0x01, RING_BUFFER);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TRANSFER,
28862306a36Sopenharmony_ci			0xFF, MS_TRANSFER_START | trans_mode);
28962306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, CHECK_REG_CMD, MS_TRANSFER,
29062306a36Sopenharmony_ci			MS_TRANSFER_END, MS_TRANSFER_END);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	err = rtsx_usb_send_cmd(ucr, flag | STAGE_MS_STATUS, 100);
29362306a36Sopenharmony_ci	if (err)
29462306a36Sopenharmony_ci		return err;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	err = rtsx_usb_transfer_data(ucr, pipe, sg, length,
29762306a36Sopenharmony_ci			1, NULL, 10000);
29862306a36Sopenharmony_ci	if (err)
29962306a36Sopenharmony_ci		goto err_out;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	err = rtsx_usb_get_rsp(ucr, 3, 15000);
30262306a36Sopenharmony_ci	if (err)
30362306a36Sopenharmony_ci		goto err_out;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	if (ucr->rsp_buf[0] & MS_TRANSFER_ERR ||
30662306a36Sopenharmony_ci	    ucr->rsp_buf[1] & (MS_CRC16_ERR | MS_RDY_TIMEOUT)) {
30762306a36Sopenharmony_ci		err = -EIO;
30862306a36Sopenharmony_ci		goto err_out;
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci	return 0;
31162306a36Sopenharmony_cierr_out:
31262306a36Sopenharmony_ci	ms_clear_error(host);
31362306a36Sopenharmony_ci	return err;
31462306a36Sopenharmony_ci}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_cistatic int ms_write_bytes(struct rtsx_usb_ms *host, u8 tpc,
31762306a36Sopenharmony_ci		u8 cfg, u8 cnt, u8 *data, u8 *int_reg)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	struct rtsx_ucr *ucr = host->ucr;
32062306a36Sopenharmony_ci	int err, i;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	dev_dbg(ms_dev(host), "%s: tpc = 0x%02x\n", __func__, tpc);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	rtsx_usb_init_cmd(ucr);
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	for (i = 0; i < cnt; i++)
32762306a36Sopenharmony_ci		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD,
32862306a36Sopenharmony_ci				PPBUF_BASE2 + i, 0xFF, data[i]);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	if (cnt % 2)
33162306a36Sopenharmony_ci		rtsx_usb_add_cmd(ucr, WRITE_REG_CMD,
33262306a36Sopenharmony_ci				PPBUF_BASE2 + i, 0xFF, 0xFF);
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TPC, 0xFF, tpc);
33562306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_BYTE_CNT, 0xFF, cnt);
33662306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg);
33762306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_DATA_SOURCE,
33862306a36Sopenharmony_ci			0x01, PINGPONG_BUFFER);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TRANSFER,
34162306a36Sopenharmony_ci			0xFF, MS_TRANSFER_START | MS_TM_WRITE_BYTES);
34262306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, CHECK_REG_CMD, MS_TRANSFER,
34362306a36Sopenharmony_ci			MS_TRANSFER_END, MS_TRANSFER_END);
34462306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, READ_REG_CMD, MS_TRANS_CFG, 0, 0);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	err = rtsx_usb_send_cmd(ucr, MODE_CR, 100);
34762306a36Sopenharmony_ci	if (err)
34862306a36Sopenharmony_ci		return err;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	err = rtsx_usb_get_rsp(ucr, 2, 5000);
35162306a36Sopenharmony_ci	if (err || (ucr->rsp_buf[0] & MS_TRANSFER_ERR)) {
35262306a36Sopenharmony_ci		u8 val;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci		rtsx_usb_ep0_read_register(ucr, MS_TRANS_CFG, &val);
35562306a36Sopenharmony_ci		dev_dbg(ms_dev(host), "MS_TRANS_CFG: 0x%02x\n", val);
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci		if (int_reg)
35862306a36Sopenharmony_ci			*int_reg = val & 0x0F;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci		ms_print_debug_regs(host);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci		ms_clear_error(host);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci		if (!(tpc & 0x08)) {
36562306a36Sopenharmony_ci			if (val & MS_CRC16_ERR)
36662306a36Sopenharmony_ci				return -EIO;
36762306a36Sopenharmony_ci		} else {
36862306a36Sopenharmony_ci			if (!(val & 0x80)) {
36962306a36Sopenharmony_ci				if (val & (MS_INT_ERR | MS_INT_CMDNK))
37062306a36Sopenharmony_ci					return -EIO;
37162306a36Sopenharmony_ci			}
37262306a36Sopenharmony_ci		}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci		return -ETIMEDOUT;
37562306a36Sopenharmony_ci	}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	if (int_reg)
37862306a36Sopenharmony_ci		*int_reg = ucr->rsp_buf[1] & 0x0F;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	return 0;
38162306a36Sopenharmony_ci}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_cistatic int ms_read_bytes(struct rtsx_usb_ms *host, u8 tpc,
38462306a36Sopenharmony_ci		u8 cfg, u8 cnt, u8 *data, u8 *int_reg)
38562306a36Sopenharmony_ci{
38662306a36Sopenharmony_ci	struct rtsx_ucr *ucr = host->ucr;
38762306a36Sopenharmony_ci	int err, i;
38862306a36Sopenharmony_ci	u8 *ptr;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	dev_dbg(ms_dev(host), "%s: tpc = 0x%02x\n", __func__, tpc);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	rtsx_usb_init_cmd(ucr);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TPC, 0xFF, tpc);
39562306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_BYTE_CNT, 0xFF, cnt);
39662306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg);
39762306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_DATA_SOURCE,
39862306a36Sopenharmony_ci			0x01, PINGPONG_BUFFER);
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TRANSFER,
40162306a36Sopenharmony_ci			0xFF, MS_TRANSFER_START | MS_TM_READ_BYTES);
40262306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, CHECK_REG_CMD, MS_TRANSFER,
40362306a36Sopenharmony_ci			MS_TRANSFER_END, MS_TRANSFER_END);
40462306a36Sopenharmony_ci	for (i = 0; i < cnt - 1; i++)
40562306a36Sopenharmony_ci		rtsx_usb_add_cmd(ucr, READ_REG_CMD, PPBUF_BASE2 + i, 0, 0);
40662306a36Sopenharmony_ci	if (cnt % 2)
40762306a36Sopenharmony_ci		rtsx_usb_add_cmd(ucr, READ_REG_CMD, PPBUF_BASE2 + cnt, 0, 0);
40862306a36Sopenharmony_ci	else
40962306a36Sopenharmony_ci		rtsx_usb_add_cmd(ucr, READ_REG_CMD,
41062306a36Sopenharmony_ci				PPBUF_BASE2 + cnt - 1, 0, 0);
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	rtsx_usb_add_cmd(ucr, READ_REG_CMD, MS_TRANS_CFG, 0, 0);
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	err = rtsx_usb_send_cmd(ucr, MODE_CR, 100);
41562306a36Sopenharmony_ci	if (err)
41662306a36Sopenharmony_ci		return err;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	err = rtsx_usb_get_rsp(ucr, cnt + 2, 5000);
41962306a36Sopenharmony_ci	if (err || (ucr->rsp_buf[0] & MS_TRANSFER_ERR)) {
42062306a36Sopenharmony_ci		u8 val;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci		rtsx_usb_ep0_read_register(ucr, MS_TRANS_CFG, &val);
42362306a36Sopenharmony_ci		dev_dbg(ms_dev(host), "MS_TRANS_CFG: 0x%02x\n", val);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci		if (int_reg && (host->ifmode != MEMSTICK_SERIAL))
42662306a36Sopenharmony_ci			*int_reg = val & 0x0F;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci		ms_print_debug_regs(host);
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci		ms_clear_error(host);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci		if (!(tpc & 0x08)) {
43362306a36Sopenharmony_ci			if (val & MS_CRC16_ERR)
43462306a36Sopenharmony_ci				return -EIO;
43562306a36Sopenharmony_ci		} else {
43662306a36Sopenharmony_ci			if (!(val & 0x80)) {
43762306a36Sopenharmony_ci				if (val & (MS_INT_ERR | MS_INT_CMDNK))
43862306a36Sopenharmony_ci					return -EIO;
43962306a36Sopenharmony_ci			}
44062306a36Sopenharmony_ci		}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci		return -ETIMEDOUT;
44362306a36Sopenharmony_ci	}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	ptr = ucr->rsp_buf + 1;
44662306a36Sopenharmony_ci	for (i = 0; i < cnt; i++)
44762306a36Sopenharmony_ci		data[i] = *ptr++;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	if (int_reg && (host->ifmode != MEMSTICK_SERIAL))
45162306a36Sopenharmony_ci		*int_reg = *ptr & 0x0F;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	return 0;
45462306a36Sopenharmony_ci}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_cistatic int rtsx_usb_ms_issue_cmd(struct rtsx_usb_ms *host)
45762306a36Sopenharmony_ci{
45862306a36Sopenharmony_ci	struct memstick_request *req = host->req;
45962306a36Sopenharmony_ci	int err = 0;
46062306a36Sopenharmony_ci	u8 cfg = 0, int_reg;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	dev_dbg(ms_dev(host), "%s\n", __func__);
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	if (req->need_card_int) {
46562306a36Sopenharmony_ci		if (host->ifmode != MEMSTICK_SERIAL)
46662306a36Sopenharmony_ci			cfg = WAIT_INT;
46762306a36Sopenharmony_ci	}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	if (req->long_data) {
47062306a36Sopenharmony_ci		err = ms_transfer_data(host, req->data_dir,
47162306a36Sopenharmony_ci				req->tpc, cfg, &(req->sg));
47262306a36Sopenharmony_ci	} else {
47362306a36Sopenharmony_ci		if (req->data_dir == READ)
47462306a36Sopenharmony_ci			err = ms_read_bytes(host, req->tpc, cfg,
47562306a36Sopenharmony_ci					req->data_len, req->data, &int_reg);
47662306a36Sopenharmony_ci		else
47762306a36Sopenharmony_ci			err = ms_write_bytes(host, req->tpc, cfg,
47862306a36Sopenharmony_ci					req->data_len, req->data, &int_reg);
47962306a36Sopenharmony_ci	}
48062306a36Sopenharmony_ci	if (err < 0)
48162306a36Sopenharmony_ci		return err;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	if (req->need_card_int) {
48462306a36Sopenharmony_ci		if (host->ifmode == MEMSTICK_SERIAL) {
48562306a36Sopenharmony_ci			err = ms_read_bytes(host, MS_TPC_GET_INT,
48662306a36Sopenharmony_ci					NO_WAIT_INT, 1, &req->int_reg, NULL);
48762306a36Sopenharmony_ci			if (err < 0)
48862306a36Sopenharmony_ci				return err;
48962306a36Sopenharmony_ci		} else {
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci			if (int_reg & MS_INT_CMDNK)
49262306a36Sopenharmony_ci				req->int_reg |= MEMSTICK_INT_CMDNAK;
49362306a36Sopenharmony_ci			if (int_reg & MS_INT_BREQ)
49462306a36Sopenharmony_ci				req->int_reg |= MEMSTICK_INT_BREQ;
49562306a36Sopenharmony_ci			if (int_reg & MS_INT_ERR)
49662306a36Sopenharmony_ci				req->int_reg |= MEMSTICK_INT_ERR;
49762306a36Sopenharmony_ci			if (int_reg & MS_INT_CED)
49862306a36Sopenharmony_ci				req->int_reg |= MEMSTICK_INT_CED;
49962306a36Sopenharmony_ci		}
50062306a36Sopenharmony_ci		dev_dbg(ms_dev(host), "int_reg: 0x%02x\n", req->int_reg);
50162306a36Sopenharmony_ci	}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	return 0;
50462306a36Sopenharmony_ci}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_cistatic void rtsx_usb_ms_handle_req(struct work_struct *work)
50762306a36Sopenharmony_ci{
50862306a36Sopenharmony_ci	struct rtsx_usb_ms *host = container_of(work,
50962306a36Sopenharmony_ci			struct rtsx_usb_ms, handle_req);
51062306a36Sopenharmony_ci	struct rtsx_ucr *ucr = host->ucr;
51162306a36Sopenharmony_ci	struct memstick_host *msh = host->msh;
51262306a36Sopenharmony_ci	int rc;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	if (!host->req) {
51562306a36Sopenharmony_ci		pm_runtime_get_sync(ms_dev(host));
51662306a36Sopenharmony_ci		do {
51762306a36Sopenharmony_ci			rc = memstick_next_req(msh, &host->req);
51862306a36Sopenharmony_ci			dev_dbg(ms_dev(host), "next req %d\n", rc);
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci			if (!rc) {
52162306a36Sopenharmony_ci				mutex_lock(&ucr->dev_mutex);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci				if (rtsx_usb_card_exclusive_check(ucr,
52462306a36Sopenharmony_ci							RTSX_USB_MS_CARD))
52562306a36Sopenharmony_ci					host->req->error = -EIO;
52662306a36Sopenharmony_ci				else
52762306a36Sopenharmony_ci					host->req->error =
52862306a36Sopenharmony_ci						rtsx_usb_ms_issue_cmd(host);
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci				mutex_unlock(&ucr->dev_mutex);
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci				dev_dbg(ms_dev(host), "req result %d\n",
53362306a36Sopenharmony_ci						host->req->error);
53462306a36Sopenharmony_ci			}
53562306a36Sopenharmony_ci		} while (!rc);
53662306a36Sopenharmony_ci		pm_runtime_put_sync(ms_dev(host));
53762306a36Sopenharmony_ci	}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_cistatic void rtsx_usb_ms_request(struct memstick_host *msh)
54262306a36Sopenharmony_ci{
54362306a36Sopenharmony_ci	struct rtsx_usb_ms *host = memstick_priv(msh);
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	dev_dbg(ms_dev(host), "--> %s\n", __func__);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	if (!host->eject)
54862306a36Sopenharmony_ci		schedule_work(&host->handle_req);
54962306a36Sopenharmony_ci}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_cistatic int rtsx_usb_ms_set_param(struct memstick_host *msh,
55262306a36Sopenharmony_ci		enum memstick_param param, int value)
55362306a36Sopenharmony_ci{
55462306a36Sopenharmony_ci	struct rtsx_usb_ms *host = memstick_priv(msh);
55562306a36Sopenharmony_ci	struct rtsx_ucr *ucr = host->ucr;
55662306a36Sopenharmony_ci	unsigned int clock = 0;
55762306a36Sopenharmony_ci	u8 ssc_depth = 0;
55862306a36Sopenharmony_ci	int err;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	dev_dbg(ms_dev(host), "%s: param = %d, value = %d\n",
56162306a36Sopenharmony_ci			__func__, param, value);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	pm_runtime_get_sync(ms_dev(host));
56462306a36Sopenharmony_ci	mutex_lock(&ucr->dev_mutex);
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	err = rtsx_usb_card_exclusive_check(ucr, RTSX_USB_MS_CARD);
56762306a36Sopenharmony_ci	if (err)
56862306a36Sopenharmony_ci		goto out;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	switch (param) {
57162306a36Sopenharmony_ci	case MEMSTICK_POWER:
57262306a36Sopenharmony_ci		if (value == host->power_mode)
57362306a36Sopenharmony_ci			break;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci		if (value == MEMSTICK_POWER_ON) {
57662306a36Sopenharmony_ci			pm_runtime_get_noresume(ms_dev(host));
57762306a36Sopenharmony_ci			err = ms_power_on(host);
57862306a36Sopenharmony_ci			if (err)
57962306a36Sopenharmony_ci				pm_runtime_put_noidle(ms_dev(host));
58062306a36Sopenharmony_ci		} else if (value == MEMSTICK_POWER_OFF) {
58162306a36Sopenharmony_ci			err = ms_power_off(host);
58262306a36Sopenharmony_ci			if (!err)
58362306a36Sopenharmony_ci				pm_runtime_put_noidle(ms_dev(host));
58462306a36Sopenharmony_ci		} else
58562306a36Sopenharmony_ci			err = -EINVAL;
58662306a36Sopenharmony_ci		if (!err)
58762306a36Sopenharmony_ci			host->power_mode = value;
58862306a36Sopenharmony_ci		break;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	case MEMSTICK_INTERFACE:
59162306a36Sopenharmony_ci		if (value == MEMSTICK_SERIAL) {
59262306a36Sopenharmony_ci			clock = 19000000;
59362306a36Sopenharmony_ci			ssc_depth = SSC_DEPTH_512K;
59462306a36Sopenharmony_ci			err = rtsx_usb_write_register(ucr, MS_CFG, 0x5A,
59562306a36Sopenharmony_ci				       MS_BUS_WIDTH_1 | PUSH_TIME_DEFAULT);
59662306a36Sopenharmony_ci			if (err < 0)
59762306a36Sopenharmony_ci				break;
59862306a36Sopenharmony_ci		} else if (value == MEMSTICK_PAR4) {
59962306a36Sopenharmony_ci			clock = 39000000;
60062306a36Sopenharmony_ci			ssc_depth = SSC_DEPTH_1M;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci			err = rtsx_usb_write_register(ucr, MS_CFG, 0x5A,
60362306a36Sopenharmony_ci					MS_BUS_WIDTH_4 | PUSH_TIME_ODD |
60462306a36Sopenharmony_ci					MS_NO_CHECK_INT);
60562306a36Sopenharmony_ci			if (err < 0)
60662306a36Sopenharmony_ci				break;
60762306a36Sopenharmony_ci		} else {
60862306a36Sopenharmony_ci			err = -EINVAL;
60962306a36Sopenharmony_ci			break;
61062306a36Sopenharmony_ci		}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci		err = rtsx_usb_switch_clock(ucr, clock,
61362306a36Sopenharmony_ci				ssc_depth, false, true, false);
61462306a36Sopenharmony_ci		if (err < 0) {
61562306a36Sopenharmony_ci			dev_dbg(ms_dev(host), "switch clock failed\n");
61662306a36Sopenharmony_ci			break;
61762306a36Sopenharmony_ci		}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci		host->ssc_depth = ssc_depth;
62062306a36Sopenharmony_ci		host->clock = clock;
62162306a36Sopenharmony_ci		host->ifmode = value;
62262306a36Sopenharmony_ci		break;
62362306a36Sopenharmony_ci	default:
62462306a36Sopenharmony_ci		err = -EINVAL;
62562306a36Sopenharmony_ci		break;
62662306a36Sopenharmony_ci	}
62762306a36Sopenharmony_ciout:
62862306a36Sopenharmony_ci	mutex_unlock(&ucr->dev_mutex);
62962306a36Sopenharmony_ci	pm_runtime_put_sync(ms_dev(host));
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	/* power-on delay */
63262306a36Sopenharmony_ci	if (param == MEMSTICK_POWER && value == MEMSTICK_POWER_ON) {
63362306a36Sopenharmony_ci		usleep_range(10000, 12000);
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci		if (!host->eject)
63662306a36Sopenharmony_ci			schedule_delayed_work(&host->poll_card, 100);
63762306a36Sopenharmony_ci	}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	dev_dbg(ms_dev(host), "%s: return = %d\n", __func__, err);
64062306a36Sopenharmony_ci	return err;
64162306a36Sopenharmony_ci}
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
64462306a36Sopenharmony_cistatic int rtsx_usb_ms_suspend(struct device *dev)
64562306a36Sopenharmony_ci{
64662306a36Sopenharmony_ci	struct rtsx_usb_ms *host = dev_get_drvdata(dev);
64762306a36Sopenharmony_ci	struct memstick_host *msh = host->msh;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	/* Since we use rtsx_usb's resume callback to runtime resume its
65062306a36Sopenharmony_ci	 * children to implement remote wakeup signaling, this causes
65162306a36Sopenharmony_ci	 * rtsx_usb_ms' runtime resume callback runs after its suspend
65262306a36Sopenharmony_ci	 * callback:
65362306a36Sopenharmony_ci	 * rtsx_usb_ms_suspend()
65462306a36Sopenharmony_ci	 * rtsx_usb_resume()
65562306a36Sopenharmony_ci	 *   -> rtsx_usb_ms_runtime_resume()
65662306a36Sopenharmony_ci	 *     -> memstick_detect_change()
65762306a36Sopenharmony_ci	 *
65862306a36Sopenharmony_ci	 * rtsx_usb_suspend()
65962306a36Sopenharmony_ci	 *
66062306a36Sopenharmony_ci	 * To avoid this, skip runtime resume/suspend if system suspend is
66162306a36Sopenharmony_ci	 * underway.
66262306a36Sopenharmony_ci	 */
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	host->system_suspending = true;
66562306a36Sopenharmony_ci	memstick_suspend_host(msh);
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	return 0;
66862306a36Sopenharmony_ci}
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_cistatic int rtsx_usb_ms_resume(struct device *dev)
67162306a36Sopenharmony_ci{
67262306a36Sopenharmony_ci	struct rtsx_usb_ms *host = dev_get_drvdata(dev);
67362306a36Sopenharmony_ci	struct memstick_host *msh = host->msh;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	memstick_resume_host(msh);
67662306a36Sopenharmony_ci	host->system_suspending = false;
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	return 0;
67962306a36Sopenharmony_ci}
68062306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci#ifdef CONFIG_PM
68362306a36Sopenharmony_cistatic int rtsx_usb_ms_runtime_suspend(struct device *dev)
68462306a36Sopenharmony_ci{
68562306a36Sopenharmony_ci	struct rtsx_usb_ms *host = dev_get_drvdata(dev);
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	if (host->system_suspending)
68862306a36Sopenharmony_ci		return 0;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	if (host->msh->card || host->power_mode != MEMSTICK_POWER_OFF)
69162306a36Sopenharmony_ci		return -EAGAIN;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	return 0;
69462306a36Sopenharmony_ci}
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_cistatic int rtsx_usb_ms_runtime_resume(struct device *dev)
69762306a36Sopenharmony_ci{
69862306a36Sopenharmony_ci	struct rtsx_usb_ms *host = dev_get_drvdata(dev);
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	if (host->system_suspending)
70262306a36Sopenharmony_ci		return 0;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	memstick_detect_change(host->msh);
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	return 0;
70762306a36Sopenharmony_ci}
70862306a36Sopenharmony_ci#endif /* CONFIG_PM */
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_cistatic const struct dev_pm_ops rtsx_usb_ms_pm_ops = {
71162306a36Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(rtsx_usb_ms_suspend, rtsx_usb_ms_resume)
71262306a36Sopenharmony_ci	SET_RUNTIME_PM_OPS(rtsx_usb_ms_runtime_suspend, rtsx_usb_ms_runtime_resume, NULL)
71362306a36Sopenharmony_ci};
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_cistatic void rtsx_usb_ms_poll_card(struct work_struct *work)
71762306a36Sopenharmony_ci{
71862306a36Sopenharmony_ci	struct rtsx_usb_ms *host = container_of(work, struct rtsx_usb_ms,
71962306a36Sopenharmony_ci			poll_card.work);
72062306a36Sopenharmony_ci	struct rtsx_ucr *ucr = host->ucr;
72162306a36Sopenharmony_ci	int err;
72262306a36Sopenharmony_ci	u8 val;
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	if (host->eject || host->power_mode != MEMSTICK_POWER_ON)
72562306a36Sopenharmony_ci		return;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	pm_runtime_get_sync(ms_dev(host));
72862306a36Sopenharmony_ci	mutex_lock(&ucr->dev_mutex);
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	/* Check pending MS card changes */
73162306a36Sopenharmony_ci	err = rtsx_usb_read_register(ucr, CARD_INT_PEND, &val);
73262306a36Sopenharmony_ci	if (err) {
73362306a36Sopenharmony_ci		mutex_unlock(&ucr->dev_mutex);
73462306a36Sopenharmony_ci		goto poll_again;
73562306a36Sopenharmony_ci	}
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	/* Clear the pending */
73862306a36Sopenharmony_ci	rtsx_usb_write_register(ucr, CARD_INT_PEND,
73962306a36Sopenharmony_ci			XD_INT | MS_INT | SD_INT,
74062306a36Sopenharmony_ci			XD_INT | MS_INT | SD_INT);
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	mutex_unlock(&ucr->dev_mutex);
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	if (val & MS_INT) {
74562306a36Sopenharmony_ci		dev_dbg(ms_dev(host), "MS slot change detected\n");
74662306a36Sopenharmony_ci		memstick_detect_change(host->msh);
74762306a36Sopenharmony_ci	}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_cipoll_again:
75062306a36Sopenharmony_ci	pm_runtime_put_sync(ms_dev(host));
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	if (!host->eject && host->power_mode == MEMSTICK_POWER_ON)
75362306a36Sopenharmony_ci		schedule_delayed_work(&host->poll_card, 100);
75462306a36Sopenharmony_ci}
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_cistatic int rtsx_usb_ms_drv_probe(struct platform_device *pdev)
75762306a36Sopenharmony_ci{
75862306a36Sopenharmony_ci	struct memstick_host *msh;
75962306a36Sopenharmony_ci	struct rtsx_usb_ms *host;
76062306a36Sopenharmony_ci	struct rtsx_ucr *ucr;
76162306a36Sopenharmony_ci	int err;
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	ucr = usb_get_intfdata(to_usb_interface(pdev->dev.parent));
76462306a36Sopenharmony_ci	if (!ucr)
76562306a36Sopenharmony_ci		return -ENXIO;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	dev_dbg(&(pdev->dev),
76862306a36Sopenharmony_ci			"Realtek USB Memstick controller found\n");
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	msh = memstick_alloc_host(sizeof(*host), &pdev->dev);
77162306a36Sopenharmony_ci	if (!msh)
77262306a36Sopenharmony_ci		return -ENOMEM;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	host = memstick_priv(msh);
77562306a36Sopenharmony_ci	host->ucr = ucr;
77662306a36Sopenharmony_ci	host->msh = msh;
77762306a36Sopenharmony_ci	host->pdev = pdev;
77862306a36Sopenharmony_ci	host->power_mode = MEMSTICK_POWER_OFF;
77962306a36Sopenharmony_ci	platform_set_drvdata(pdev, host);
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	mutex_init(&host->host_mutex);
78262306a36Sopenharmony_ci	INIT_WORK(&host->handle_req, rtsx_usb_ms_handle_req);
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	INIT_DELAYED_WORK(&host->poll_card, rtsx_usb_ms_poll_card);
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	msh->request = rtsx_usb_ms_request;
78762306a36Sopenharmony_ci	msh->set_param = rtsx_usb_ms_set_param;
78862306a36Sopenharmony_ci	msh->caps = MEMSTICK_CAP_PAR4;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	pm_runtime_get_noresume(ms_dev(host));
79162306a36Sopenharmony_ci	pm_runtime_set_active(ms_dev(host));
79262306a36Sopenharmony_ci	pm_runtime_enable(ms_dev(host));
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	err = memstick_add_host(msh);
79562306a36Sopenharmony_ci	if (err)
79662306a36Sopenharmony_ci		goto err_out;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	pm_runtime_put(ms_dev(host));
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	return 0;
80162306a36Sopenharmony_cierr_out:
80262306a36Sopenharmony_ci	pm_runtime_disable(ms_dev(host));
80362306a36Sopenharmony_ci	pm_runtime_put_noidle(ms_dev(host));
80462306a36Sopenharmony_ci	memstick_free_host(msh);
80562306a36Sopenharmony_ci	return err;
80662306a36Sopenharmony_ci}
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_cistatic int rtsx_usb_ms_drv_remove(struct platform_device *pdev)
80962306a36Sopenharmony_ci{
81062306a36Sopenharmony_ci	struct rtsx_usb_ms *host = platform_get_drvdata(pdev);
81162306a36Sopenharmony_ci	struct memstick_host *msh = host->msh;
81262306a36Sopenharmony_ci	int err;
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	host->eject = true;
81562306a36Sopenharmony_ci	cancel_work_sync(&host->handle_req);
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	mutex_lock(&host->host_mutex);
81862306a36Sopenharmony_ci	if (host->req) {
81962306a36Sopenharmony_ci		dev_dbg(ms_dev(host),
82062306a36Sopenharmony_ci			"%s: Controller removed during transfer\n",
82162306a36Sopenharmony_ci			dev_name(&msh->dev));
82262306a36Sopenharmony_ci		host->req->error = -ENOMEDIUM;
82362306a36Sopenharmony_ci		do {
82462306a36Sopenharmony_ci			err = memstick_next_req(msh, &host->req);
82562306a36Sopenharmony_ci			if (!err)
82662306a36Sopenharmony_ci				host->req->error = -ENOMEDIUM;
82762306a36Sopenharmony_ci		} while (!err);
82862306a36Sopenharmony_ci	}
82962306a36Sopenharmony_ci	mutex_unlock(&host->host_mutex);
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	/* Balance possible unbalanced usage count
83262306a36Sopenharmony_ci	 * e.g. unconditional module removal
83362306a36Sopenharmony_ci	 */
83462306a36Sopenharmony_ci	if (pm_runtime_active(ms_dev(host)))
83562306a36Sopenharmony_ci		pm_runtime_put(ms_dev(host));
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	pm_runtime_disable(ms_dev(host));
83862306a36Sopenharmony_ci	memstick_remove_host(msh);
83962306a36Sopenharmony_ci	dev_dbg(ms_dev(host),
84062306a36Sopenharmony_ci		": Realtek USB Memstick controller has been removed\n");
84162306a36Sopenharmony_ci	memstick_free_host(msh);
84262306a36Sopenharmony_ci	platform_set_drvdata(pdev, NULL);
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	return 0;
84562306a36Sopenharmony_ci}
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_cistatic struct platform_device_id rtsx_usb_ms_ids[] = {
84862306a36Sopenharmony_ci	{
84962306a36Sopenharmony_ci		.name = "rtsx_usb_ms",
85062306a36Sopenharmony_ci	}, {
85162306a36Sopenharmony_ci		/* sentinel */
85262306a36Sopenharmony_ci	}
85362306a36Sopenharmony_ci};
85462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(platform, rtsx_usb_ms_ids);
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_cistatic struct platform_driver rtsx_usb_ms_driver = {
85762306a36Sopenharmony_ci	.probe		= rtsx_usb_ms_drv_probe,
85862306a36Sopenharmony_ci	.remove		= rtsx_usb_ms_drv_remove,
85962306a36Sopenharmony_ci	.id_table       = rtsx_usb_ms_ids,
86062306a36Sopenharmony_ci	.driver		= {
86162306a36Sopenharmony_ci		.name	= "rtsx_usb_ms",
86262306a36Sopenharmony_ci		.pm	= &rtsx_usb_ms_pm_ops,
86362306a36Sopenharmony_ci	},
86462306a36Sopenharmony_ci};
86562306a36Sopenharmony_cimodule_platform_driver(rtsx_usb_ms_driver);
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
86862306a36Sopenharmony_ciMODULE_AUTHOR("Roger Tseng <rogerable@realtek.com>");
86962306a36Sopenharmony_ciMODULE_DESCRIPTION("Realtek USB Memstick Card Host Driver");
870