18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* Driver for Realtek USB card reader 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Author: 78c2ecf20Sopenharmony_ci * Roger Tseng <rogerable@realtek.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <linux/mutex.h> 128c2ecf20Sopenharmony_ci#include <linux/usb.h> 138c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 148c2ecf20Sopenharmony_ci#include <linux/mfd/core.h> 158c2ecf20Sopenharmony_ci#include <linux/rtsx_usb.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistatic int polling_pipe = 1; 188c2ecf20Sopenharmony_cimodule_param(polling_pipe, int, S_IRUGO | S_IWUSR); 198c2ecf20Sopenharmony_ciMODULE_PARM_DESC(polling_pipe, "polling pipe (0: ctl, 1: bulk)"); 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic const struct mfd_cell rtsx_usb_cells[] = { 228c2ecf20Sopenharmony_ci [RTSX_USB_SD_CARD] = { 238c2ecf20Sopenharmony_ci .name = "rtsx_usb_sdmmc", 248c2ecf20Sopenharmony_ci .pdata_size = 0, 258c2ecf20Sopenharmony_ci }, 268c2ecf20Sopenharmony_ci [RTSX_USB_MS_CARD] = { 278c2ecf20Sopenharmony_ci .name = "rtsx_usb_ms", 288c2ecf20Sopenharmony_ci .pdata_size = 0, 298c2ecf20Sopenharmony_ci }, 308c2ecf20Sopenharmony_ci}; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic void rtsx_usb_sg_timed_out(struct timer_list *t) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci struct rtsx_ucr *ucr = from_timer(ucr, t, sg_timer); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci dev_dbg(&ucr->pusb_intf->dev, "%s: sg transfer timed out", __func__); 378c2ecf20Sopenharmony_ci usb_sg_cancel(&ucr->current_sg); 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic int rtsx_usb_bulk_transfer_sglist(struct rtsx_ucr *ucr, 418c2ecf20Sopenharmony_ci unsigned int pipe, struct scatterlist *sg, int num_sg, 428c2ecf20Sopenharmony_ci unsigned int length, unsigned int *act_len, int timeout) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci int ret; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci dev_dbg(&ucr->pusb_intf->dev, "%s: xfer %u bytes, %d entries\n", 478c2ecf20Sopenharmony_ci __func__, length, num_sg); 488c2ecf20Sopenharmony_ci ret = usb_sg_init(&ucr->current_sg, ucr->pusb_dev, pipe, 0, 498c2ecf20Sopenharmony_ci sg, num_sg, length, GFP_NOIO); 508c2ecf20Sopenharmony_ci if (ret) 518c2ecf20Sopenharmony_ci return ret; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci ucr->sg_timer.expires = jiffies + msecs_to_jiffies(timeout); 548c2ecf20Sopenharmony_ci add_timer(&ucr->sg_timer); 558c2ecf20Sopenharmony_ci usb_sg_wait(&ucr->current_sg); 568c2ecf20Sopenharmony_ci if (!del_timer_sync(&ucr->sg_timer)) 578c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 588c2ecf20Sopenharmony_ci else 598c2ecf20Sopenharmony_ci ret = ucr->current_sg.status; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci if (act_len) 628c2ecf20Sopenharmony_ci *act_len = ucr->current_sg.bytes; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci return ret; 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ciint rtsx_usb_transfer_data(struct rtsx_ucr *ucr, unsigned int pipe, 688c2ecf20Sopenharmony_ci void *buf, unsigned int len, int num_sg, 698c2ecf20Sopenharmony_ci unsigned int *act_len, int timeout) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci if (timeout < 600) 728c2ecf20Sopenharmony_ci timeout = 600; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if (num_sg) 758c2ecf20Sopenharmony_ci return rtsx_usb_bulk_transfer_sglist(ucr, pipe, 768c2ecf20Sopenharmony_ci (struct scatterlist *)buf, num_sg, len, act_len, 778c2ecf20Sopenharmony_ci timeout); 788c2ecf20Sopenharmony_ci else 798c2ecf20Sopenharmony_ci return usb_bulk_msg(ucr->pusb_dev, pipe, buf, len, act_len, 808c2ecf20Sopenharmony_ci timeout); 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtsx_usb_transfer_data); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic inline void rtsx_usb_seq_cmd_hdr(struct rtsx_ucr *ucr, 858c2ecf20Sopenharmony_ci u16 addr, u16 len, u8 seq_type) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci rtsx_usb_cmd_hdr_tag(ucr); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci ucr->cmd_buf[PACKET_TYPE] = seq_type; 908c2ecf20Sopenharmony_ci ucr->cmd_buf[5] = (u8)(len >> 8); 918c2ecf20Sopenharmony_ci ucr->cmd_buf[6] = (u8)len; 928c2ecf20Sopenharmony_ci ucr->cmd_buf[8] = (u8)(addr >> 8); 938c2ecf20Sopenharmony_ci ucr->cmd_buf[9] = (u8)addr; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci if (seq_type == SEQ_WRITE) 968c2ecf20Sopenharmony_ci ucr->cmd_buf[STAGE_FLAG] = 0; 978c2ecf20Sopenharmony_ci else 988c2ecf20Sopenharmony_ci ucr->cmd_buf[STAGE_FLAG] = STAGE_R; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic int rtsx_usb_seq_write_register(struct rtsx_ucr *ucr, 1028c2ecf20Sopenharmony_ci u16 addr, u16 len, u8 *data) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci u16 cmd_len = ALIGN(SEQ_WRITE_DATA_OFFSET + len, 4); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci if (!data) 1078c2ecf20Sopenharmony_ci return -EINVAL; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci if (cmd_len > IOBUF_SIZE) 1108c2ecf20Sopenharmony_ci return -EINVAL; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci rtsx_usb_seq_cmd_hdr(ucr, addr, len, SEQ_WRITE); 1138c2ecf20Sopenharmony_ci memcpy(ucr->cmd_buf + SEQ_WRITE_DATA_OFFSET, data, len); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci return rtsx_usb_transfer_data(ucr, 1168c2ecf20Sopenharmony_ci usb_sndbulkpipe(ucr->pusb_dev, EP_BULK_OUT), 1178c2ecf20Sopenharmony_ci ucr->cmd_buf, cmd_len, 0, NULL, 100); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic int rtsx_usb_seq_read_register(struct rtsx_ucr *ucr, 1218c2ecf20Sopenharmony_ci u16 addr, u16 len, u8 *data) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci int i, ret; 1248c2ecf20Sopenharmony_ci u16 rsp_len = round_down(len, 4); 1258c2ecf20Sopenharmony_ci u16 res_len = len - rsp_len; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (!data) 1288c2ecf20Sopenharmony_ci return -EINVAL; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci /* 4-byte aligned part */ 1318c2ecf20Sopenharmony_ci if (rsp_len) { 1328c2ecf20Sopenharmony_ci rtsx_usb_seq_cmd_hdr(ucr, addr, len, SEQ_READ); 1338c2ecf20Sopenharmony_ci ret = rtsx_usb_transfer_data(ucr, 1348c2ecf20Sopenharmony_ci usb_sndbulkpipe(ucr->pusb_dev, EP_BULK_OUT), 1358c2ecf20Sopenharmony_ci ucr->cmd_buf, 12, 0, NULL, 100); 1368c2ecf20Sopenharmony_ci if (ret) 1378c2ecf20Sopenharmony_ci return ret; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci ret = rtsx_usb_transfer_data(ucr, 1408c2ecf20Sopenharmony_ci usb_rcvbulkpipe(ucr->pusb_dev, EP_BULK_IN), 1418c2ecf20Sopenharmony_ci data, rsp_len, 0, NULL, 100); 1428c2ecf20Sopenharmony_ci if (ret) 1438c2ecf20Sopenharmony_ci return ret; 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci /* unaligned part */ 1478c2ecf20Sopenharmony_ci for (i = 0; i < res_len; i++) { 1488c2ecf20Sopenharmony_ci ret = rtsx_usb_read_register(ucr, addr + rsp_len + i, 1498c2ecf20Sopenharmony_ci data + rsp_len + i); 1508c2ecf20Sopenharmony_ci if (ret) 1518c2ecf20Sopenharmony_ci return ret; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci return 0; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ciint rtsx_usb_read_ppbuf(struct rtsx_ucr *ucr, u8 *buf, int buf_len) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci return rtsx_usb_seq_read_register(ucr, PPBUF_BASE2, (u16)buf_len, buf); 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtsx_usb_read_ppbuf); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ciint rtsx_usb_write_ppbuf(struct rtsx_ucr *ucr, u8 *buf, int buf_len) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci return rtsx_usb_seq_write_register(ucr, PPBUF_BASE2, (u16)buf_len, buf); 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtsx_usb_write_ppbuf); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ciint rtsx_usb_ep0_write_register(struct rtsx_ucr *ucr, u16 addr, 1708c2ecf20Sopenharmony_ci u8 mask, u8 data) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci u16 value, index; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci addr |= EP0_WRITE_REG_CMD << EP0_OP_SHIFT; 1758c2ecf20Sopenharmony_ci value = swab16(addr); 1768c2ecf20Sopenharmony_ci index = mask | data << 8; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci return usb_control_msg(ucr->pusb_dev, 1798c2ecf20Sopenharmony_ci usb_sndctrlpipe(ucr->pusb_dev, 0), RTSX_USB_REQ_REG_OP, 1808c2ecf20Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 1818c2ecf20Sopenharmony_ci value, index, NULL, 0, 100); 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtsx_usb_ep0_write_register); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ciint rtsx_usb_ep0_read_register(struct rtsx_ucr *ucr, u16 addr, u8 *data) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci u16 value; 1888c2ecf20Sopenharmony_ci u8 *buf; 1898c2ecf20Sopenharmony_ci int ret; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci if (!data) 1928c2ecf20Sopenharmony_ci return -EINVAL; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci buf = kzalloc(sizeof(u8), GFP_KERNEL); 1958c2ecf20Sopenharmony_ci if (!buf) 1968c2ecf20Sopenharmony_ci return -ENOMEM; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci addr |= EP0_READ_REG_CMD << EP0_OP_SHIFT; 1998c2ecf20Sopenharmony_ci value = swab16(addr); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci ret = usb_control_msg(ucr->pusb_dev, 2028c2ecf20Sopenharmony_ci usb_rcvctrlpipe(ucr->pusb_dev, 0), RTSX_USB_REQ_REG_OP, 2038c2ecf20Sopenharmony_ci USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 2048c2ecf20Sopenharmony_ci value, 0, buf, 1, 100); 2058c2ecf20Sopenharmony_ci *data = *buf; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci kfree(buf); 2088c2ecf20Sopenharmony_ci return ret; 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtsx_usb_ep0_read_register); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_civoid rtsx_usb_add_cmd(struct rtsx_ucr *ucr, u8 cmd_type, u16 reg_addr, 2138c2ecf20Sopenharmony_ci u8 mask, u8 data) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci int i; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci if (ucr->cmd_idx < (IOBUF_SIZE - CMD_OFFSET) / 4) { 2188c2ecf20Sopenharmony_ci i = CMD_OFFSET + ucr->cmd_idx * 4; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci ucr->cmd_buf[i++] = ((cmd_type & 0x03) << 6) | 2218c2ecf20Sopenharmony_ci (u8)((reg_addr >> 8) & 0x3F); 2228c2ecf20Sopenharmony_ci ucr->cmd_buf[i++] = (u8)reg_addr; 2238c2ecf20Sopenharmony_ci ucr->cmd_buf[i++] = mask; 2248c2ecf20Sopenharmony_ci ucr->cmd_buf[i++] = data; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci ucr->cmd_idx++; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtsx_usb_add_cmd); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ciint rtsx_usb_send_cmd(struct rtsx_ucr *ucr, u8 flag, int timeout) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci int ret; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci ucr->cmd_buf[CNT_H] = (u8)(ucr->cmd_idx >> 8); 2368c2ecf20Sopenharmony_ci ucr->cmd_buf[CNT_L] = (u8)(ucr->cmd_idx); 2378c2ecf20Sopenharmony_ci ucr->cmd_buf[STAGE_FLAG] = flag; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci ret = rtsx_usb_transfer_data(ucr, 2408c2ecf20Sopenharmony_ci usb_sndbulkpipe(ucr->pusb_dev, EP_BULK_OUT), 2418c2ecf20Sopenharmony_ci ucr->cmd_buf, ucr->cmd_idx * 4 + CMD_OFFSET, 2428c2ecf20Sopenharmony_ci 0, NULL, timeout); 2438c2ecf20Sopenharmony_ci if (ret) { 2448c2ecf20Sopenharmony_ci rtsx_usb_clear_fsm_err(ucr); 2458c2ecf20Sopenharmony_ci return ret; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci return 0; 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtsx_usb_send_cmd); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ciint rtsx_usb_get_rsp(struct rtsx_ucr *ucr, int rsp_len, int timeout) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci if (rsp_len <= 0) 2558c2ecf20Sopenharmony_ci return -EINVAL; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci rsp_len = ALIGN(rsp_len, 4); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci return rtsx_usb_transfer_data(ucr, 2608c2ecf20Sopenharmony_ci usb_rcvbulkpipe(ucr->pusb_dev, EP_BULK_IN), 2618c2ecf20Sopenharmony_ci ucr->rsp_buf, rsp_len, 0, NULL, timeout); 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtsx_usb_get_rsp); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic int rtsx_usb_get_status_with_bulk(struct rtsx_ucr *ucr, u16 *status) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci int ret; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci rtsx_usb_init_cmd(ucr); 2708c2ecf20Sopenharmony_ci rtsx_usb_add_cmd(ucr, READ_REG_CMD, CARD_EXIST, 0x00, 0x00); 2718c2ecf20Sopenharmony_ci rtsx_usb_add_cmd(ucr, READ_REG_CMD, OCPSTAT, 0x00, 0x00); 2728c2ecf20Sopenharmony_ci ret = rtsx_usb_send_cmd(ucr, MODE_CR, 100); 2738c2ecf20Sopenharmony_ci if (ret) 2748c2ecf20Sopenharmony_ci return ret; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci ret = rtsx_usb_get_rsp(ucr, 2, 100); 2778c2ecf20Sopenharmony_ci if (ret) 2788c2ecf20Sopenharmony_ci return ret; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci *status = ((ucr->rsp_buf[0] >> 2) & 0x0f) | 2818c2ecf20Sopenharmony_ci ((ucr->rsp_buf[1] & 0x03) << 4); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci return 0; 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ciint rtsx_usb_get_card_status(struct rtsx_ucr *ucr, u16 *status) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci int ret; 2898c2ecf20Sopenharmony_ci u16 *buf; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (!status) 2928c2ecf20Sopenharmony_ci return -EINVAL; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci if (polling_pipe == 0) { 2958c2ecf20Sopenharmony_ci buf = kzalloc(sizeof(u16), GFP_KERNEL); 2968c2ecf20Sopenharmony_ci if (!buf) 2978c2ecf20Sopenharmony_ci return -ENOMEM; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci ret = usb_control_msg(ucr->pusb_dev, 3008c2ecf20Sopenharmony_ci usb_rcvctrlpipe(ucr->pusb_dev, 0), 3018c2ecf20Sopenharmony_ci RTSX_USB_REQ_POLL, 3028c2ecf20Sopenharmony_ci USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 3038c2ecf20Sopenharmony_ci 0, 0, buf, 2, 100); 3048c2ecf20Sopenharmony_ci *status = *buf; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci kfree(buf); 3078c2ecf20Sopenharmony_ci } else { 3088c2ecf20Sopenharmony_ci ret = rtsx_usb_get_status_with_bulk(ucr, status); 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci /* usb_control_msg may return positive when success */ 3128c2ecf20Sopenharmony_ci if (ret < 0) 3138c2ecf20Sopenharmony_ci return ret; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci return 0; 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtsx_usb_get_card_status); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cistatic int rtsx_usb_write_phy_register(struct rtsx_ucr *ucr, u8 addr, u8 val) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci dev_dbg(&ucr->pusb_intf->dev, "Write 0x%x to phy register 0x%x\n", 3228c2ecf20Sopenharmony_ci val, addr); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci rtsx_usb_init_cmd(ucr); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VSTAIN, 0xFF, val); 3278c2ecf20Sopenharmony_ci rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VCONTROL, 0xFF, addr & 0x0F); 3288c2ecf20Sopenharmony_ci rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00); 3298c2ecf20Sopenharmony_ci rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00); 3308c2ecf20Sopenharmony_ci rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x01); 3318c2ecf20Sopenharmony_ci rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VCONTROL, 3328c2ecf20Sopenharmony_ci 0xFF, (addr >> 4) & 0x0F); 3338c2ecf20Sopenharmony_ci rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00); 3348c2ecf20Sopenharmony_ci rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00); 3358c2ecf20Sopenharmony_ci rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x01); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci return rtsx_usb_send_cmd(ucr, MODE_C, 100); 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ciint rtsx_usb_write_register(struct rtsx_ucr *ucr, u16 addr, u8 mask, u8 data) 3418c2ecf20Sopenharmony_ci{ 3428c2ecf20Sopenharmony_ci rtsx_usb_init_cmd(ucr); 3438c2ecf20Sopenharmony_ci rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, addr, mask, data); 3448c2ecf20Sopenharmony_ci return rtsx_usb_send_cmd(ucr, MODE_C, 100); 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtsx_usb_write_register); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ciint rtsx_usb_read_register(struct rtsx_ucr *ucr, u16 addr, u8 *data) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci int ret; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci if (data != NULL) 3538c2ecf20Sopenharmony_ci *data = 0; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci rtsx_usb_init_cmd(ucr); 3568c2ecf20Sopenharmony_ci rtsx_usb_add_cmd(ucr, READ_REG_CMD, addr, 0, 0); 3578c2ecf20Sopenharmony_ci ret = rtsx_usb_send_cmd(ucr, MODE_CR, 100); 3588c2ecf20Sopenharmony_ci if (ret) 3598c2ecf20Sopenharmony_ci return ret; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci ret = rtsx_usb_get_rsp(ucr, 1, 100); 3628c2ecf20Sopenharmony_ci if (ret) 3638c2ecf20Sopenharmony_ci return ret; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci if (data != NULL) 3668c2ecf20Sopenharmony_ci *data = ucr->rsp_buf[0]; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci return 0; 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtsx_usb_read_register); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic inline u8 double_ssc_depth(u8 depth) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci return (depth > 1) ? (depth - 1) : depth; 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_cistatic u8 revise_ssc_depth(u8 ssc_depth, u8 div) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci if (div > CLK_DIV_1) { 3808c2ecf20Sopenharmony_ci if (ssc_depth > div - 1) 3818c2ecf20Sopenharmony_ci ssc_depth -= (div - 1); 3828c2ecf20Sopenharmony_ci else 3838c2ecf20Sopenharmony_ci ssc_depth = SSC_DEPTH_2M; 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci return ssc_depth; 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ciint rtsx_usb_switch_clock(struct rtsx_ucr *ucr, unsigned int card_clock, 3908c2ecf20Sopenharmony_ci u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci int ret; 3938c2ecf20Sopenharmony_ci u8 n, clk_divider, mcu_cnt, div; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci if (!card_clock) { 3968c2ecf20Sopenharmony_ci ucr->cur_clk = 0; 3978c2ecf20Sopenharmony_ci return 0; 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci if (initial_mode) { 4018c2ecf20Sopenharmony_ci /* We use 250k(around) here, in initial stage */ 4028c2ecf20Sopenharmony_ci clk_divider = SD_CLK_DIVIDE_128; 4038c2ecf20Sopenharmony_ci card_clock = 30000000; 4048c2ecf20Sopenharmony_ci } else { 4058c2ecf20Sopenharmony_ci clk_divider = SD_CLK_DIVIDE_0; 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci ret = rtsx_usb_write_register(ucr, SD_CFG1, 4098c2ecf20Sopenharmony_ci SD_CLK_DIVIDE_MASK, clk_divider); 4108c2ecf20Sopenharmony_ci if (ret < 0) 4118c2ecf20Sopenharmony_ci return ret; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci card_clock /= 1000000; 4148c2ecf20Sopenharmony_ci dev_dbg(&ucr->pusb_intf->dev, 4158c2ecf20Sopenharmony_ci "Switch card clock to %dMHz\n", card_clock); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci if (!initial_mode && double_clk) 4188c2ecf20Sopenharmony_ci card_clock *= 2; 4198c2ecf20Sopenharmony_ci dev_dbg(&ucr->pusb_intf->dev, 4208c2ecf20Sopenharmony_ci "Internal SSC clock: %dMHz (cur_clk = %d)\n", 4218c2ecf20Sopenharmony_ci card_clock, ucr->cur_clk); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci if (card_clock == ucr->cur_clk) 4248c2ecf20Sopenharmony_ci return 0; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci /* Converting clock value into internal settings: n and div */ 4278c2ecf20Sopenharmony_ci n = card_clock - 2; 4288c2ecf20Sopenharmony_ci if ((card_clock <= 2) || (n > MAX_DIV_N)) 4298c2ecf20Sopenharmony_ci return -EINVAL; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci mcu_cnt = 60/card_clock + 3; 4328c2ecf20Sopenharmony_ci if (mcu_cnt > 15) 4338c2ecf20Sopenharmony_ci mcu_cnt = 15; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci /* Make sure that the SSC clock div_n is not less than MIN_DIV_N */ 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci div = CLK_DIV_1; 4388c2ecf20Sopenharmony_ci while (n < MIN_DIV_N && div < CLK_DIV_4) { 4398c2ecf20Sopenharmony_ci n = (n + 2) * 2 - 2; 4408c2ecf20Sopenharmony_ci div++; 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci dev_dbg(&ucr->pusb_intf->dev, "n = %d, div = %d\n", n, div); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (double_clk) 4458c2ecf20Sopenharmony_ci ssc_depth = double_ssc_depth(ssc_depth); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci ssc_depth = revise_ssc_depth(ssc_depth, div); 4488c2ecf20Sopenharmony_ci dev_dbg(&ucr->pusb_intf->dev, "ssc_depth = %d\n", ssc_depth); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci rtsx_usb_init_cmd(ucr); 4518c2ecf20Sopenharmony_ci rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CLK_DIV, CLK_CHANGE, CLK_CHANGE); 4528c2ecf20Sopenharmony_ci rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CLK_DIV, 4538c2ecf20Sopenharmony_ci 0x3F, (div << 4) | mcu_cnt); 4548c2ecf20Sopenharmony_ci rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, 0); 4558c2ecf20Sopenharmony_ci rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_CTL2, 4568c2ecf20Sopenharmony_ci SSC_DEPTH_MASK, ssc_depth); 4578c2ecf20Sopenharmony_ci rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_DIV_N_0, 0xFF, n); 4588c2ecf20Sopenharmony_ci rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, SSC_RSTB); 4598c2ecf20Sopenharmony_ci if (vpclk) { 4608c2ecf20Sopenharmony_ci rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_VPCLK0_CTL, 4618c2ecf20Sopenharmony_ci PHASE_NOT_RESET, 0); 4628c2ecf20Sopenharmony_ci rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_VPCLK0_CTL, 4638c2ecf20Sopenharmony_ci PHASE_NOT_RESET, PHASE_NOT_RESET); 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci ret = rtsx_usb_send_cmd(ucr, MODE_C, 2000); 4678c2ecf20Sopenharmony_ci if (ret < 0) 4688c2ecf20Sopenharmony_ci return ret; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci ret = rtsx_usb_write_register(ucr, SSC_CTL1, 0xff, 4718c2ecf20Sopenharmony_ci SSC_RSTB | SSC_8X_EN | SSC_SEL_4M); 4728c2ecf20Sopenharmony_ci if (ret < 0) 4738c2ecf20Sopenharmony_ci return ret; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci /* Wait SSC clock stable */ 4768c2ecf20Sopenharmony_ci usleep_range(100, 1000); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci ret = rtsx_usb_write_register(ucr, CLK_DIV, CLK_CHANGE, 0); 4798c2ecf20Sopenharmony_ci if (ret < 0) 4808c2ecf20Sopenharmony_ci return ret; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci ucr->cur_clk = card_clock; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci return 0; 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtsx_usb_switch_clock); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ciint rtsx_usb_card_exclusive_check(struct rtsx_ucr *ucr, int card) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci int ret; 4918c2ecf20Sopenharmony_ci u16 val; 4928c2ecf20Sopenharmony_ci u16 cd_mask[] = { 4938c2ecf20Sopenharmony_ci [RTSX_USB_SD_CARD] = (CD_MASK & ~SD_CD), 4948c2ecf20Sopenharmony_ci [RTSX_USB_MS_CARD] = (CD_MASK & ~MS_CD) 4958c2ecf20Sopenharmony_ci }; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci ret = rtsx_usb_get_card_status(ucr, &val); 4988c2ecf20Sopenharmony_ci /* 4998c2ecf20Sopenharmony_ci * If get status fails, return 0 (ok) for the exclusive check 5008c2ecf20Sopenharmony_ci * and let the flow fail at somewhere else. 5018c2ecf20Sopenharmony_ci */ 5028c2ecf20Sopenharmony_ci if (ret) 5038c2ecf20Sopenharmony_ci return 0; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci if (val & cd_mask[card]) 5068c2ecf20Sopenharmony_ci return -EIO; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci return 0; 5098c2ecf20Sopenharmony_ci} 5108c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtsx_usb_card_exclusive_check); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_cistatic int rtsx_usb_reset_chip(struct rtsx_ucr *ucr) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci int ret; 5158c2ecf20Sopenharmony_ci u8 val; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci rtsx_usb_init_cmd(ucr); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci if (CHECK_PKG(ucr, LQFP48)) { 5208c2ecf20Sopenharmony_ci rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL, 5218c2ecf20Sopenharmony_ci LDO3318_PWR_MASK, LDO_SUSPEND); 5228c2ecf20Sopenharmony_ci rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL, 5238c2ecf20Sopenharmony_ci FORCE_LDO_POWERB, FORCE_LDO_POWERB); 5248c2ecf20Sopenharmony_ci rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1, 5258c2ecf20Sopenharmony_ci 0x30, 0x10); 5268c2ecf20Sopenharmony_ci rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, 5278c2ecf20Sopenharmony_ci 0x03, 0x01); 5288c2ecf20Sopenharmony_ci rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, 5298c2ecf20Sopenharmony_ci 0x0C, 0x04); 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SYS_DUMMY0, NYET_MSAK, NYET_EN); 5338c2ecf20Sopenharmony_ci rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CD_DEGLITCH_WIDTH, 0xFF, 0x08); 5348c2ecf20Sopenharmony_ci rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, 5358c2ecf20Sopenharmony_ci CD_DEGLITCH_EN, XD_CD_DEGLITCH_EN, 0x0); 5368c2ecf20Sopenharmony_ci rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD30_DRIVE_SEL, 5378c2ecf20Sopenharmony_ci SD30_DRIVE_MASK, DRIVER_TYPE_D); 5388c2ecf20Sopenharmony_ci rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, 5398c2ecf20Sopenharmony_ci CARD_DRIVE_SEL, SD20_DRIVE_MASK, 0x0); 5408c2ecf20Sopenharmony_ci rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, LDO_POWER_CFG, 0xE0, 0x0); 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci if (ucr->is_rts5179) 5438c2ecf20Sopenharmony_ci rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, 5448c2ecf20Sopenharmony_ci CARD_PULL_CTL5, 0x03, 0x01); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_DMA1_CTL, 5478c2ecf20Sopenharmony_ci EXTEND_DMA1_ASYNC_SIGNAL, EXTEND_DMA1_ASYNC_SIGNAL); 5488c2ecf20Sopenharmony_ci rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_INT_PEND, 5498c2ecf20Sopenharmony_ci XD_INT | MS_INT | SD_INT, 5508c2ecf20Sopenharmony_ci XD_INT | MS_INT | SD_INT); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci ret = rtsx_usb_send_cmd(ucr, MODE_C, 100); 5538c2ecf20Sopenharmony_ci if (ret) 5548c2ecf20Sopenharmony_ci return ret; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci /* config non-crystal mode */ 5578c2ecf20Sopenharmony_ci rtsx_usb_read_register(ucr, CFG_MODE, &val); 5588c2ecf20Sopenharmony_ci if ((val & XTAL_FREE) || ((val & CLK_MODE_MASK) == CLK_MODE_NON_XTAL)) { 5598c2ecf20Sopenharmony_ci ret = rtsx_usb_write_phy_register(ucr, 0xC2, 0x7C); 5608c2ecf20Sopenharmony_ci if (ret) 5618c2ecf20Sopenharmony_ci return ret; 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci return 0; 5658c2ecf20Sopenharmony_ci} 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_cistatic int rtsx_usb_init_chip(struct rtsx_ucr *ucr) 5688c2ecf20Sopenharmony_ci{ 5698c2ecf20Sopenharmony_ci int ret; 5708c2ecf20Sopenharmony_ci u8 val; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci rtsx_usb_clear_fsm_err(ucr); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci /* power on SSC */ 5758c2ecf20Sopenharmony_ci ret = rtsx_usb_write_register(ucr, 5768c2ecf20Sopenharmony_ci FPDCTL, SSC_POWER_MASK, SSC_POWER_ON); 5778c2ecf20Sopenharmony_ci if (ret) 5788c2ecf20Sopenharmony_ci return ret; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci usleep_range(100, 1000); 5818c2ecf20Sopenharmony_ci ret = rtsx_usb_write_register(ucr, CLK_DIV, CLK_CHANGE, 0x00); 5828c2ecf20Sopenharmony_ci if (ret) 5838c2ecf20Sopenharmony_ci return ret; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci /* determine IC version */ 5868c2ecf20Sopenharmony_ci ret = rtsx_usb_read_register(ucr, HW_VERSION, &val); 5878c2ecf20Sopenharmony_ci if (ret) 5888c2ecf20Sopenharmony_ci return ret; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci ucr->ic_version = val & HW_VER_MASK; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci /* determine package */ 5938c2ecf20Sopenharmony_ci ret = rtsx_usb_read_register(ucr, CARD_SHARE_MODE, &val); 5948c2ecf20Sopenharmony_ci if (ret) 5958c2ecf20Sopenharmony_ci return ret; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci if (val & CARD_SHARE_LQFP_SEL) { 5988c2ecf20Sopenharmony_ci ucr->package = LQFP48; 5998c2ecf20Sopenharmony_ci dev_dbg(&ucr->pusb_intf->dev, "Package: LQFP48\n"); 6008c2ecf20Sopenharmony_ci } else { 6018c2ecf20Sopenharmony_ci ucr->package = QFN24; 6028c2ecf20Sopenharmony_ci dev_dbg(&ucr->pusb_intf->dev, "Package: QFN24\n"); 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci /* determine IC variations */ 6068c2ecf20Sopenharmony_ci rtsx_usb_read_register(ucr, CFG_MODE_1, &val); 6078c2ecf20Sopenharmony_ci if (val & RTS5179) { 6088c2ecf20Sopenharmony_ci ucr->is_rts5179 = true; 6098c2ecf20Sopenharmony_ci dev_dbg(&ucr->pusb_intf->dev, "Device is rts5179\n"); 6108c2ecf20Sopenharmony_ci } else { 6118c2ecf20Sopenharmony_ci ucr->is_rts5179 = false; 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci return rtsx_usb_reset_chip(ucr); 6158c2ecf20Sopenharmony_ci} 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_cistatic int rtsx_usb_probe(struct usb_interface *intf, 6188c2ecf20Sopenharmony_ci const struct usb_device_id *id) 6198c2ecf20Sopenharmony_ci{ 6208c2ecf20Sopenharmony_ci struct usb_device *usb_dev = interface_to_usbdev(intf); 6218c2ecf20Sopenharmony_ci struct rtsx_ucr *ucr; 6228c2ecf20Sopenharmony_ci int ret; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci dev_dbg(&intf->dev, 6258c2ecf20Sopenharmony_ci ": Realtek USB Card Reader found at bus %03d address %03d\n", 6268c2ecf20Sopenharmony_ci usb_dev->bus->busnum, usb_dev->devnum); 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci ucr = devm_kzalloc(&intf->dev, sizeof(*ucr), GFP_KERNEL); 6298c2ecf20Sopenharmony_ci if (!ucr) 6308c2ecf20Sopenharmony_ci return -ENOMEM; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci ucr->pusb_dev = usb_dev; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci ucr->cmd_buf = kmalloc(IOBUF_SIZE, GFP_KERNEL); 6358c2ecf20Sopenharmony_ci if (!ucr->cmd_buf) 6368c2ecf20Sopenharmony_ci return -ENOMEM; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci ucr->rsp_buf = kmalloc(IOBUF_SIZE, GFP_KERNEL); 6398c2ecf20Sopenharmony_ci if (!ucr->rsp_buf) { 6408c2ecf20Sopenharmony_ci ret = -ENOMEM; 6418c2ecf20Sopenharmony_ci goto out_free_cmd_buf; 6428c2ecf20Sopenharmony_ci } 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci usb_set_intfdata(intf, ucr); 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci ucr->vendor_id = id->idVendor; 6478c2ecf20Sopenharmony_ci ucr->product_id = id->idProduct; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci mutex_init(&ucr->dev_mutex); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci ucr->pusb_intf = intf; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci /* initialize */ 6548c2ecf20Sopenharmony_ci ret = rtsx_usb_init_chip(ucr); 6558c2ecf20Sopenharmony_ci if (ret) 6568c2ecf20Sopenharmony_ci goto out_init_fail; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci /* initialize USB SG transfer timer */ 6598c2ecf20Sopenharmony_ci timer_setup(&ucr->sg_timer, rtsx_usb_sg_timed_out, 0); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci ret = mfd_add_hotplug_devices(&intf->dev, rtsx_usb_cells, 6628c2ecf20Sopenharmony_ci ARRAY_SIZE(rtsx_usb_cells)); 6638c2ecf20Sopenharmony_ci if (ret) 6648c2ecf20Sopenharmony_ci goto out_init_fail; 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 6678c2ecf20Sopenharmony_ci intf->needs_remote_wakeup = 1; 6688c2ecf20Sopenharmony_ci usb_enable_autosuspend(usb_dev); 6698c2ecf20Sopenharmony_ci#endif 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci return 0; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ciout_init_fail: 6748c2ecf20Sopenharmony_ci usb_set_intfdata(ucr->pusb_intf, NULL); 6758c2ecf20Sopenharmony_ci kfree(ucr->rsp_buf); 6768c2ecf20Sopenharmony_ci ucr->rsp_buf = NULL; 6778c2ecf20Sopenharmony_ciout_free_cmd_buf: 6788c2ecf20Sopenharmony_ci kfree(ucr->cmd_buf); 6798c2ecf20Sopenharmony_ci ucr->cmd_buf = NULL; 6808c2ecf20Sopenharmony_ci return ret; 6818c2ecf20Sopenharmony_ci} 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_cistatic void rtsx_usb_disconnect(struct usb_interface *intf) 6848c2ecf20Sopenharmony_ci{ 6858c2ecf20Sopenharmony_ci struct rtsx_ucr *ucr = (struct rtsx_ucr *)usb_get_intfdata(intf); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci dev_dbg(&intf->dev, "%s called\n", __func__); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci mfd_remove_devices(&intf->dev); 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci usb_set_intfdata(ucr->pusb_intf, NULL); 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci kfree(ucr->cmd_buf); 6948c2ecf20Sopenharmony_ci ucr->cmd_buf = NULL; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci kfree(ucr->rsp_buf); 6978c2ecf20Sopenharmony_ci ucr->rsp_buf = NULL; 6988c2ecf20Sopenharmony_ci} 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 7018c2ecf20Sopenharmony_cistatic int rtsx_usb_suspend(struct usb_interface *intf, pm_message_t message) 7028c2ecf20Sopenharmony_ci{ 7038c2ecf20Sopenharmony_ci struct rtsx_ucr *ucr = 7048c2ecf20Sopenharmony_ci (struct rtsx_ucr *)usb_get_intfdata(intf); 7058c2ecf20Sopenharmony_ci u16 val = 0; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci dev_dbg(&intf->dev, "%s called with pm message 0x%04x\n", 7088c2ecf20Sopenharmony_ci __func__, message.event); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci if (PMSG_IS_AUTO(message)) { 7118c2ecf20Sopenharmony_ci if (mutex_trylock(&ucr->dev_mutex)) { 7128c2ecf20Sopenharmony_ci rtsx_usb_get_card_status(ucr, &val); 7138c2ecf20Sopenharmony_ci mutex_unlock(&ucr->dev_mutex); 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci /* Defer the autosuspend if card exists */ 7168c2ecf20Sopenharmony_ci if (val & (SD_CD | MS_CD)) 7178c2ecf20Sopenharmony_ci return -EAGAIN; 7188c2ecf20Sopenharmony_ci } else { 7198c2ecf20Sopenharmony_ci /* There is an ongoing operation*/ 7208c2ecf20Sopenharmony_ci return -EAGAIN; 7218c2ecf20Sopenharmony_ci } 7228c2ecf20Sopenharmony_ci } 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci return 0; 7258c2ecf20Sopenharmony_ci} 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_cistatic int rtsx_usb_resume_child(struct device *dev, void *data) 7288c2ecf20Sopenharmony_ci{ 7298c2ecf20Sopenharmony_ci pm_request_resume(dev); 7308c2ecf20Sopenharmony_ci return 0; 7318c2ecf20Sopenharmony_ci} 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_cistatic int rtsx_usb_resume(struct usb_interface *intf) 7348c2ecf20Sopenharmony_ci{ 7358c2ecf20Sopenharmony_ci device_for_each_child(&intf->dev, NULL, rtsx_usb_resume_child); 7368c2ecf20Sopenharmony_ci return 0; 7378c2ecf20Sopenharmony_ci} 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_cistatic int rtsx_usb_reset_resume(struct usb_interface *intf) 7408c2ecf20Sopenharmony_ci{ 7418c2ecf20Sopenharmony_ci struct rtsx_ucr *ucr = 7428c2ecf20Sopenharmony_ci (struct rtsx_ucr *)usb_get_intfdata(intf); 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci rtsx_usb_reset_chip(ucr); 7458c2ecf20Sopenharmony_ci device_for_each_child(&intf->dev, NULL, rtsx_usb_resume_child); 7468c2ecf20Sopenharmony_ci return 0; 7478c2ecf20Sopenharmony_ci} 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci#else /* CONFIG_PM */ 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci#define rtsx_usb_suspend NULL 7528c2ecf20Sopenharmony_ci#define rtsx_usb_resume NULL 7538c2ecf20Sopenharmony_ci#define rtsx_usb_reset_resume NULL 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_cistatic int rtsx_usb_pre_reset(struct usb_interface *intf) 7598c2ecf20Sopenharmony_ci{ 7608c2ecf20Sopenharmony_ci struct rtsx_ucr *ucr = (struct rtsx_ucr *)usb_get_intfdata(intf); 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci mutex_lock(&ucr->dev_mutex); 7638c2ecf20Sopenharmony_ci return 0; 7648c2ecf20Sopenharmony_ci} 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_cistatic int rtsx_usb_post_reset(struct usb_interface *intf) 7678c2ecf20Sopenharmony_ci{ 7688c2ecf20Sopenharmony_ci struct rtsx_ucr *ucr = (struct rtsx_ucr *)usb_get_intfdata(intf); 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci mutex_unlock(&ucr->dev_mutex); 7718c2ecf20Sopenharmony_ci return 0; 7728c2ecf20Sopenharmony_ci} 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_cistatic const struct usb_device_id rtsx_usb_usb_ids[] = { 7758c2ecf20Sopenharmony_ci { USB_DEVICE(0x0BDA, 0x0129) }, 7768c2ecf20Sopenharmony_ci { USB_DEVICE(0x0BDA, 0x0139) }, 7778c2ecf20Sopenharmony_ci { USB_DEVICE(0x0BDA, 0x0140) }, 7788c2ecf20Sopenharmony_ci { } 7798c2ecf20Sopenharmony_ci}; 7808c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, rtsx_usb_usb_ids); 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_cistatic struct usb_driver rtsx_usb_driver = { 7838c2ecf20Sopenharmony_ci .name = "rtsx_usb", 7848c2ecf20Sopenharmony_ci .probe = rtsx_usb_probe, 7858c2ecf20Sopenharmony_ci .disconnect = rtsx_usb_disconnect, 7868c2ecf20Sopenharmony_ci .suspend = rtsx_usb_suspend, 7878c2ecf20Sopenharmony_ci .resume = rtsx_usb_resume, 7888c2ecf20Sopenharmony_ci .reset_resume = rtsx_usb_reset_resume, 7898c2ecf20Sopenharmony_ci .pre_reset = rtsx_usb_pre_reset, 7908c2ecf20Sopenharmony_ci .post_reset = rtsx_usb_post_reset, 7918c2ecf20Sopenharmony_ci .id_table = rtsx_usb_usb_ids, 7928c2ecf20Sopenharmony_ci .supports_autosuspend = 1, 7938c2ecf20Sopenharmony_ci .soft_unbind = 1, 7948c2ecf20Sopenharmony_ci}; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_cimodule_usb_driver(rtsx_usb_driver); 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 7998c2ecf20Sopenharmony_ciMODULE_AUTHOR("Roger Tseng <rogerable@realtek.com>"); 8008c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Realtek USB Card Reader Driver"); 801