// SPDX-License-Identifier: GPL-2.0-only
/* Realtek USB Memstick Card Interface driver
*
* Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
*
* Author:
* Roger Tseng <rogerable@realtek.com>
*/
#include <linux/module.h>
#include <linux/highmem.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>
#include <linux/memstick.h>
#include <linux/kthread.h>
#include <linux/rtsx_usb.h>
#include <linux/pm_runtime.h>
#include <linux/mutex.h>
#include <linux/sched.h>
#include <linux/completion.h>
#include <asm/unaligned.h>
struct rtsx_usb_ms {
struct platform_device *pdev;
struct rtsx_ucr *ucr;
struct memstick_host *msh;
struct memstick_request *req;
struct mutex host_mutex;
struct work_struct handle_req;
struct delayed_work poll_card;
u8 ssc_depth;
unsigned int clock;
int power_mode;
unsigned char ifmode;
bool eject;
bool system_suspending;
};
static inline struct device *ms_dev(struct rtsx_usb_ms *host)
{
return &(host->pdev->dev);
}
static inline void ms_clear_error(struct rtsx_usb_ms *host)
{
struct rtsx_ucr *ucr = host->ucr;
rtsx_usb_ep0_write_register(ucr, CARD_STOP,
MS_STOP | MS_CLR_ERR,
MS_STOP | MS_CLR_ERR);
rtsx_usb_clear_dma_err(ucr);
rtsx_usb_clear_fsm_err(ucr);
}
#ifdef DEBUG
static void ms_print_debug_regs(struct rtsx_usb_ms *host)
{
struct rtsx_ucr *ucr = host->ucr;
u16 i;
u8 *ptr;
/* Print MS host internal registers */
rtsx_usb_init_cmd(ucr);
/* MS_CFG to MS_INT_REG */
for (i = 0xFD40; i <= 0xFD44; i++)
rtsx_usb_add_cmd(ucr, READ_REG_CMD, i, 0, 0);
/* CARD_SHARE_MODE to CARD_GPIO */
for (i = 0xFD51; i <= 0xFD56; i++)
rtsx_usb_add_cmd(ucr, READ_REG_CMD, i, 0, 0);
/* CARD_PULL_CTLx */
for (i = 0xFD60; i <= 0xFD65; i++)
rtsx_usb_add_cmd(ucr, READ_REG_CMD, i, 0, 0);
/* CARD_DATA_SOURCE, CARD_SELECT, CARD_CLK_EN, CARD_PWR_CTL */
rtsx_usb_add_cmd(ucr, READ_REG_CMD, CARD_DATA_SOURCE, 0, 0);
rtsx_usb_add_cmd(ucr, READ_REG_CMD, CARD_SELECT, 0, 0);
rtsx_usb_add_cmd(ucr, READ_REG_CMD, CARD_CLK_EN, 0, 0);
rtsx_usb_add_cmd(ucr, READ_REG_CMD, CARD_PWR_CTL, 0, 0);
rtsx_usb_send_cmd(ucr, MODE_CR, 100);
rtsx_usb_get_rsp(ucr, 21, 100);
ptr = ucr->rsp_buf;
for (i = 0xFD40; i <= 0xFD44; i++)
dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++));
for (i = 0xFD51; i <= 0xFD56; i++)
dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++));
for (i = 0xFD60; i <= 0xFD65; i++)
dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++));
dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", CARD_DATA_SOURCE, *(ptr++));
dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", CARD_SELECT, *(ptr++));
dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", CARD_CLK_EN, *(ptr++));
dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", CARD_PWR_CTL, *(ptr++));
}
#else
static void ms_print_debug_regs(struct rtsx_usb_ms *host)
{
}
#endif
static int ms_pull_ctl_disable_lqfp48(struct rtsx_ucr *ucr)
{
rtsx_usb_init_cmd(ucr);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF, 0x55);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, 0x55);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, 0x95);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, 0x55);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF, 0x55);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF, 0xA5);
return rtsx_usb_send_cmd(ucr, MODE_C, 100);
}
static int ms_pull_ctl_disable_qfn24(struct rtsx_ucr *ucr)
{
rtsx_usb_init_cmd(ucr);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF, 0x65);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, 0x55);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, 0x95);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, 0x55);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF, 0x56);
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF, 0x59);
return rtsx_usb_send_cmd(ucr, MODE_C,