Commit b166010f6afbadb896efa37ff85eb681a8f89392
Committed by
Lee Jones
1 parent
f29ae369a4
Exists in
ti-lsk-linux-4.1.y
and in
10 other branches
mfd: rtsx_usb: Fix runtime PM deadlock
sd_set_power_mode() in derived module drivers/mmc/host/rtsx_usb_sdmmc.c acquires dev_mutex and then calls pm_runtime_get_sync() to make sure the device is awake while initializing a newly inserted card. Once it is called during suspending state and explicitly before rtsx_usb_suspend() acquires the same dev_mutex, both routine deadlock and further hang the driver because pm_runtime_get_sync() waits the pending PM operations. Fix this by using an empty suspend method. mmc_core always turns the LED off after a request is done and thus it is ok to remove the only rtsx_usb_turn_off_led() here. Cc: <stable@vger.kernel.org> # v3.16+ Fixes: 730876be2566 ("mfd: Add realtek USB card reader driver") Signed-off-by: Roger Tseng <rogerable@realtek.com> [Lee: Removed newly unused variable] Signed-off-by: Lee Jones <lee.jones@linaro.org>
Showing 1 changed file with 0 additions and 12 deletions Inline Diff
drivers/mfd/rtsx_usb.c
1 | /* Driver for Realtek USB card reader | 1 | /* Driver for Realtek USB card reader |
2 | * | 2 | * |
3 | * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. | 3 | * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. |
4 | * | 4 | * |
5 | * This program is free software; you can redistribute it and/or modify it | 5 | * This program is free software; you can redistribute it and/or modify it |
6 | * under the terms of the GNU General Public License version 2 | 6 | * under the terms of the GNU General Public License version 2 |
7 | * as published by the Free Software Foundation. | 7 | * as published by the Free Software Foundation. |
8 | * | 8 | * |
9 | * This program is distributed in the hope that it will be useful, but | 9 | * This program is distributed in the hope that it will be useful, but |
10 | * WITHOUT ANY WARRANTY; without even the implied warranty of | 10 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * General Public License for more details. | 12 | * General Public License for more details. |
13 | * | 13 | * |
14 | * You should have received a copy of the GNU General Public License along | 14 | * You should have received a copy of the GNU General Public License along |
15 | * with this program; if not, see <http://www.gnu.org/licenses/>. | 15 | * with this program; if not, see <http://www.gnu.org/licenses/>. |
16 | * | 16 | * |
17 | * Author: | 17 | * Author: |
18 | * Roger Tseng <rogerable@realtek.com> | 18 | * Roger Tseng <rogerable@realtek.com> |
19 | */ | 19 | */ |
20 | #include <linux/module.h> | 20 | #include <linux/module.h> |
21 | #include <linux/slab.h> | 21 | #include <linux/slab.h> |
22 | #include <linux/mutex.h> | 22 | #include <linux/mutex.h> |
23 | #include <linux/usb.h> | 23 | #include <linux/usb.h> |
24 | #include <linux/platform_device.h> | 24 | #include <linux/platform_device.h> |
25 | #include <linux/mfd/core.h> | 25 | #include <linux/mfd/core.h> |
26 | #include <linux/mfd/rtsx_usb.h> | 26 | #include <linux/mfd/rtsx_usb.h> |
27 | 27 | ||
28 | static int polling_pipe = 1; | 28 | static int polling_pipe = 1; |
29 | module_param(polling_pipe, int, S_IRUGO | S_IWUSR); | 29 | module_param(polling_pipe, int, S_IRUGO | S_IWUSR); |
30 | MODULE_PARM_DESC(polling_pipe, "polling pipe (0: ctl, 1: bulk)"); | 30 | MODULE_PARM_DESC(polling_pipe, "polling pipe (0: ctl, 1: bulk)"); |
31 | 31 | ||
32 | static const struct mfd_cell rtsx_usb_cells[] = { | 32 | static const struct mfd_cell rtsx_usb_cells[] = { |
33 | [RTSX_USB_SD_CARD] = { | 33 | [RTSX_USB_SD_CARD] = { |
34 | .name = "rtsx_usb_sdmmc", | 34 | .name = "rtsx_usb_sdmmc", |
35 | .pdata_size = 0, | 35 | .pdata_size = 0, |
36 | }, | 36 | }, |
37 | [RTSX_USB_MS_CARD] = { | 37 | [RTSX_USB_MS_CARD] = { |
38 | .name = "rtsx_usb_ms", | 38 | .name = "rtsx_usb_ms", |
39 | .pdata_size = 0, | 39 | .pdata_size = 0, |
40 | }, | 40 | }, |
41 | }; | 41 | }; |
42 | 42 | ||
43 | static void rtsx_usb_sg_timed_out(unsigned long data) | 43 | static void rtsx_usb_sg_timed_out(unsigned long data) |
44 | { | 44 | { |
45 | struct rtsx_ucr *ucr = (struct rtsx_ucr *)data; | 45 | struct rtsx_ucr *ucr = (struct rtsx_ucr *)data; |
46 | 46 | ||
47 | dev_dbg(&ucr->pusb_intf->dev, "%s: sg transfer timed out", __func__); | 47 | dev_dbg(&ucr->pusb_intf->dev, "%s: sg transfer timed out", __func__); |
48 | usb_sg_cancel(&ucr->current_sg); | 48 | usb_sg_cancel(&ucr->current_sg); |
49 | 49 | ||
50 | /* we know the cancellation is caused by time-out */ | 50 | /* we know the cancellation is caused by time-out */ |
51 | ucr->current_sg.status = -ETIMEDOUT; | 51 | ucr->current_sg.status = -ETIMEDOUT; |
52 | } | 52 | } |
53 | 53 | ||
54 | static int rtsx_usb_bulk_transfer_sglist(struct rtsx_ucr *ucr, | 54 | static int rtsx_usb_bulk_transfer_sglist(struct rtsx_ucr *ucr, |
55 | unsigned int pipe, struct scatterlist *sg, int num_sg, | 55 | unsigned int pipe, struct scatterlist *sg, int num_sg, |
56 | unsigned int length, unsigned int *act_len, int timeout) | 56 | unsigned int length, unsigned int *act_len, int timeout) |
57 | { | 57 | { |
58 | int ret; | 58 | int ret; |
59 | 59 | ||
60 | dev_dbg(&ucr->pusb_intf->dev, "%s: xfer %u bytes, %d entries\n", | 60 | dev_dbg(&ucr->pusb_intf->dev, "%s: xfer %u bytes, %d entries\n", |
61 | __func__, length, num_sg); | 61 | __func__, length, num_sg); |
62 | ret = usb_sg_init(&ucr->current_sg, ucr->pusb_dev, pipe, 0, | 62 | ret = usb_sg_init(&ucr->current_sg, ucr->pusb_dev, pipe, 0, |
63 | sg, num_sg, length, GFP_NOIO); | 63 | sg, num_sg, length, GFP_NOIO); |
64 | if (ret) | 64 | if (ret) |
65 | return ret; | 65 | return ret; |
66 | 66 | ||
67 | ucr->sg_timer.expires = jiffies + msecs_to_jiffies(timeout); | 67 | ucr->sg_timer.expires = jiffies + msecs_to_jiffies(timeout); |
68 | add_timer(&ucr->sg_timer); | 68 | add_timer(&ucr->sg_timer); |
69 | usb_sg_wait(&ucr->current_sg); | 69 | usb_sg_wait(&ucr->current_sg); |
70 | del_timer_sync(&ucr->sg_timer); | 70 | del_timer_sync(&ucr->sg_timer); |
71 | 71 | ||
72 | if (act_len) | 72 | if (act_len) |
73 | *act_len = ucr->current_sg.bytes; | 73 | *act_len = ucr->current_sg.bytes; |
74 | 74 | ||
75 | return ucr->current_sg.status; | 75 | return ucr->current_sg.status; |
76 | } | 76 | } |
77 | 77 | ||
78 | int rtsx_usb_transfer_data(struct rtsx_ucr *ucr, unsigned int pipe, | 78 | int rtsx_usb_transfer_data(struct rtsx_ucr *ucr, unsigned int pipe, |
79 | void *buf, unsigned int len, int num_sg, | 79 | void *buf, unsigned int len, int num_sg, |
80 | unsigned int *act_len, int timeout) | 80 | unsigned int *act_len, int timeout) |
81 | { | 81 | { |
82 | if (timeout < 600) | 82 | if (timeout < 600) |
83 | timeout = 600; | 83 | timeout = 600; |
84 | 84 | ||
85 | if (num_sg) | 85 | if (num_sg) |
86 | return rtsx_usb_bulk_transfer_sglist(ucr, pipe, | 86 | return rtsx_usb_bulk_transfer_sglist(ucr, pipe, |
87 | (struct scatterlist *)buf, num_sg, len, act_len, | 87 | (struct scatterlist *)buf, num_sg, len, act_len, |
88 | timeout); | 88 | timeout); |
89 | else | 89 | else |
90 | return usb_bulk_msg(ucr->pusb_dev, pipe, buf, len, act_len, | 90 | return usb_bulk_msg(ucr->pusb_dev, pipe, buf, len, act_len, |
91 | timeout); | 91 | timeout); |
92 | } | 92 | } |
93 | EXPORT_SYMBOL_GPL(rtsx_usb_transfer_data); | 93 | EXPORT_SYMBOL_GPL(rtsx_usb_transfer_data); |
94 | 94 | ||
95 | static inline void rtsx_usb_seq_cmd_hdr(struct rtsx_ucr *ucr, | 95 | static inline void rtsx_usb_seq_cmd_hdr(struct rtsx_ucr *ucr, |
96 | u16 addr, u16 len, u8 seq_type) | 96 | u16 addr, u16 len, u8 seq_type) |
97 | { | 97 | { |
98 | rtsx_usb_cmd_hdr_tag(ucr); | 98 | rtsx_usb_cmd_hdr_tag(ucr); |
99 | 99 | ||
100 | ucr->cmd_buf[PACKET_TYPE] = seq_type; | 100 | ucr->cmd_buf[PACKET_TYPE] = seq_type; |
101 | ucr->cmd_buf[5] = (u8)(len >> 8); | 101 | ucr->cmd_buf[5] = (u8)(len >> 8); |
102 | ucr->cmd_buf[6] = (u8)len; | 102 | ucr->cmd_buf[6] = (u8)len; |
103 | ucr->cmd_buf[8] = (u8)(addr >> 8); | 103 | ucr->cmd_buf[8] = (u8)(addr >> 8); |
104 | ucr->cmd_buf[9] = (u8)addr; | 104 | ucr->cmd_buf[9] = (u8)addr; |
105 | 105 | ||
106 | if (seq_type == SEQ_WRITE) | 106 | if (seq_type == SEQ_WRITE) |
107 | ucr->cmd_buf[STAGE_FLAG] = 0; | 107 | ucr->cmd_buf[STAGE_FLAG] = 0; |
108 | else | 108 | else |
109 | ucr->cmd_buf[STAGE_FLAG] = STAGE_R; | 109 | ucr->cmd_buf[STAGE_FLAG] = STAGE_R; |
110 | } | 110 | } |
111 | 111 | ||
112 | static int rtsx_usb_seq_write_register(struct rtsx_ucr *ucr, | 112 | static int rtsx_usb_seq_write_register(struct rtsx_ucr *ucr, |
113 | u16 addr, u16 len, u8 *data) | 113 | u16 addr, u16 len, u8 *data) |
114 | { | 114 | { |
115 | u16 cmd_len = ALIGN(SEQ_WRITE_DATA_OFFSET + len, 4); | 115 | u16 cmd_len = ALIGN(SEQ_WRITE_DATA_OFFSET + len, 4); |
116 | 116 | ||
117 | if (!data) | 117 | if (!data) |
118 | return -EINVAL; | 118 | return -EINVAL; |
119 | 119 | ||
120 | if (cmd_len > IOBUF_SIZE) | 120 | if (cmd_len > IOBUF_SIZE) |
121 | return -EINVAL; | 121 | return -EINVAL; |
122 | 122 | ||
123 | rtsx_usb_seq_cmd_hdr(ucr, addr, len, SEQ_WRITE); | 123 | rtsx_usb_seq_cmd_hdr(ucr, addr, len, SEQ_WRITE); |
124 | memcpy(ucr->cmd_buf + SEQ_WRITE_DATA_OFFSET, data, len); | 124 | memcpy(ucr->cmd_buf + SEQ_WRITE_DATA_OFFSET, data, len); |
125 | 125 | ||
126 | return rtsx_usb_transfer_data(ucr, | 126 | return rtsx_usb_transfer_data(ucr, |
127 | usb_sndbulkpipe(ucr->pusb_dev, EP_BULK_OUT), | 127 | usb_sndbulkpipe(ucr->pusb_dev, EP_BULK_OUT), |
128 | ucr->cmd_buf, cmd_len, 0, NULL, 100); | 128 | ucr->cmd_buf, cmd_len, 0, NULL, 100); |
129 | } | 129 | } |
130 | 130 | ||
131 | static int rtsx_usb_seq_read_register(struct rtsx_ucr *ucr, | 131 | static int rtsx_usb_seq_read_register(struct rtsx_ucr *ucr, |
132 | u16 addr, u16 len, u8 *data) | 132 | u16 addr, u16 len, u8 *data) |
133 | { | 133 | { |
134 | int i, ret; | 134 | int i, ret; |
135 | u16 rsp_len = round_down(len, 4); | 135 | u16 rsp_len = round_down(len, 4); |
136 | u16 res_len = len - rsp_len; | 136 | u16 res_len = len - rsp_len; |
137 | 137 | ||
138 | if (!data) | 138 | if (!data) |
139 | return -EINVAL; | 139 | return -EINVAL; |
140 | 140 | ||
141 | /* 4-byte aligned part */ | 141 | /* 4-byte aligned part */ |
142 | if (rsp_len) { | 142 | if (rsp_len) { |
143 | rtsx_usb_seq_cmd_hdr(ucr, addr, len, SEQ_READ); | 143 | rtsx_usb_seq_cmd_hdr(ucr, addr, len, SEQ_READ); |
144 | ret = rtsx_usb_transfer_data(ucr, | 144 | ret = rtsx_usb_transfer_data(ucr, |
145 | usb_sndbulkpipe(ucr->pusb_dev, EP_BULK_OUT), | 145 | usb_sndbulkpipe(ucr->pusb_dev, EP_BULK_OUT), |
146 | ucr->cmd_buf, 12, 0, NULL, 100); | 146 | ucr->cmd_buf, 12, 0, NULL, 100); |
147 | if (ret) | 147 | if (ret) |
148 | return ret; | 148 | return ret; |
149 | 149 | ||
150 | ret = rtsx_usb_transfer_data(ucr, | 150 | ret = rtsx_usb_transfer_data(ucr, |
151 | usb_rcvbulkpipe(ucr->pusb_dev, EP_BULK_IN), | 151 | usb_rcvbulkpipe(ucr->pusb_dev, EP_BULK_IN), |
152 | data, rsp_len, 0, NULL, 100); | 152 | data, rsp_len, 0, NULL, 100); |
153 | if (ret) | 153 | if (ret) |
154 | return ret; | 154 | return ret; |
155 | } | 155 | } |
156 | 156 | ||
157 | /* unaligned part */ | 157 | /* unaligned part */ |
158 | for (i = 0; i < res_len; i++) { | 158 | for (i = 0; i < res_len; i++) { |
159 | ret = rtsx_usb_read_register(ucr, addr + rsp_len + i, | 159 | ret = rtsx_usb_read_register(ucr, addr + rsp_len + i, |
160 | data + rsp_len + i); | 160 | data + rsp_len + i); |
161 | if (ret) | 161 | if (ret) |
162 | return ret; | 162 | return ret; |
163 | } | 163 | } |
164 | 164 | ||
165 | return 0; | 165 | return 0; |
166 | } | 166 | } |
167 | 167 | ||
168 | int rtsx_usb_read_ppbuf(struct rtsx_ucr *ucr, u8 *buf, int buf_len) | 168 | int rtsx_usb_read_ppbuf(struct rtsx_ucr *ucr, u8 *buf, int buf_len) |
169 | { | 169 | { |
170 | return rtsx_usb_seq_read_register(ucr, PPBUF_BASE2, (u16)buf_len, buf); | 170 | return rtsx_usb_seq_read_register(ucr, PPBUF_BASE2, (u16)buf_len, buf); |
171 | } | 171 | } |
172 | EXPORT_SYMBOL_GPL(rtsx_usb_read_ppbuf); | 172 | EXPORT_SYMBOL_GPL(rtsx_usb_read_ppbuf); |
173 | 173 | ||
174 | int rtsx_usb_write_ppbuf(struct rtsx_ucr *ucr, u8 *buf, int buf_len) | 174 | int rtsx_usb_write_ppbuf(struct rtsx_ucr *ucr, u8 *buf, int buf_len) |
175 | { | 175 | { |
176 | return rtsx_usb_seq_write_register(ucr, PPBUF_BASE2, (u16)buf_len, buf); | 176 | return rtsx_usb_seq_write_register(ucr, PPBUF_BASE2, (u16)buf_len, buf); |
177 | } | 177 | } |
178 | EXPORT_SYMBOL_GPL(rtsx_usb_write_ppbuf); | 178 | EXPORT_SYMBOL_GPL(rtsx_usb_write_ppbuf); |
179 | 179 | ||
180 | int rtsx_usb_ep0_write_register(struct rtsx_ucr *ucr, u16 addr, | 180 | int rtsx_usb_ep0_write_register(struct rtsx_ucr *ucr, u16 addr, |
181 | u8 mask, u8 data) | 181 | u8 mask, u8 data) |
182 | { | 182 | { |
183 | u16 value, index; | 183 | u16 value, index; |
184 | 184 | ||
185 | addr |= EP0_WRITE_REG_CMD << EP0_OP_SHIFT; | 185 | addr |= EP0_WRITE_REG_CMD << EP0_OP_SHIFT; |
186 | value = swab16(addr); | 186 | value = swab16(addr); |
187 | index = mask | data << 8; | 187 | index = mask | data << 8; |
188 | 188 | ||
189 | return usb_control_msg(ucr->pusb_dev, | 189 | return usb_control_msg(ucr->pusb_dev, |
190 | usb_sndctrlpipe(ucr->pusb_dev, 0), RTSX_USB_REQ_REG_OP, | 190 | usb_sndctrlpipe(ucr->pusb_dev, 0), RTSX_USB_REQ_REG_OP, |
191 | USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | 191 | USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, |
192 | value, index, NULL, 0, 100); | 192 | value, index, NULL, 0, 100); |
193 | } | 193 | } |
194 | EXPORT_SYMBOL_GPL(rtsx_usb_ep0_write_register); | 194 | EXPORT_SYMBOL_GPL(rtsx_usb_ep0_write_register); |
195 | 195 | ||
196 | int rtsx_usb_ep0_read_register(struct rtsx_ucr *ucr, u16 addr, u8 *data) | 196 | int rtsx_usb_ep0_read_register(struct rtsx_ucr *ucr, u16 addr, u8 *data) |
197 | { | 197 | { |
198 | u16 value; | 198 | u16 value; |
199 | 199 | ||
200 | if (!data) | 200 | if (!data) |
201 | return -EINVAL; | 201 | return -EINVAL; |
202 | *data = 0; | 202 | *data = 0; |
203 | 203 | ||
204 | addr |= EP0_READ_REG_CMD << EP0_OP_SHIFT; | 204 | addr |= EP0_READ_REG_CMD << EP0_OP_SHIFT; |
205 | value = swab16(addr); | 205 | value = swab16(addr); |
206 | 206 | ||
207 | return usb_control_msg(ucr->pusb_dev, | 207 | return usb_control_msg(ucr->pusb_dev, |
208 | usb_rcvctrlpipe(ucr->pusb_dev, 0), RTSX_USB_REQ_REG_OP, | 208 | usb_rcvctrlpipe(ucr->pusb_dev, 0), RTSX_USB_REQ_REG_OP, |
209 | USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | 209 | USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, |
210 | value, 0, data, 1, 100); | 210 | value, 0, data, 1, 100); |
211 | } | 211 | } |
212 | EXPORT_SYMBOL_GPL(rtsx_usb_ep0_read_register); | 212 | EXPORT_SYMBOL_GPL(rtsx_usb_ep0_read_register); |
213 | 213 | ||
214 | void rtsx_usb_add_cmd(struct rtsx_ucr *ucr, u8 cmd_type, u16 reg_addr, | 214 | void rtsx_usb_add_cmd(struct rtsx_ucr *ucr, u8 cmd_type, u16 reg_addr, |
215 | u8 mask, u8 data) | 215 | u8 mask, u8 data) |
216 | { | 216 | { |
217 | int i; | 217 | int i; |
218 | 218 | ||
219 | if (ucr->cmd_idx < (IOBUF_SIZE - CMD_OFFSET) / 4) { | 219 | if (ucr->cmd_idx < (IOBUF_SIZE - CMD_OFFSET) / 4) { |
220 | i = CMD_OFFSET + ucr->cmd_idx * 4; | 220 | i = CMD_OFFSET + ucr->cmd_idx * 4; |
221 | 221 | ||
222 | ucr->cmd_buf[i++] = ((cmd_type & 0x03) << 6) | | 222 | ucr->cmd_buf[i++] = ((cmd_type & 0x03) << 6) | |
223 | (u8)((reg_addr >> 8) & 0x3F); | 223 | (u8)((reg_addr >> 8) & 0x3F); |
224 | ucr->cmd_buf[i++] = (u8)reg_addr; | 224 | ucr->cmd_buf[i++] = (u8)reg_addr; |
225 | ucr->cmd_buf[i++] = mask; | 225 | ucr->cmd_buf[i++] = mask; |
226 | ucr->cmd_buf[i++] = data; | 226 | ucr->cmd_buf[i++] = data; |
227 | 227 | ||
228 | ucr->cmd_idx++; | 228 | ucr->cmd_idx++; |
229 | } | 229 | } |
230 | } | 230 | } |
231 | EXPORT_SYMBOL_GPL(rtsx_usb_add_cmd); | 231 | EXPORT_SYMBOL_GPL(rtsx_usb_add_cmd); |
232 | 232 | ||
233 | int rtsx_usb_send_cmd(struct rtsx_ucr *ucr, u8 flag, int timeout) | 233 | int rtsx_usb_send_cmd(struct rtsx_ucr *ucr, u8 flag, int timeout) |
234 | { | 234 | { |
235 | int ret; | 235 | int ret; |
236 | 236 | ||
237 | ucr->cmd_buf[CNT_H] = (u8)(ucr->cmd_idx >> 8); | 237 | ucr->cmd_buf[CNT_H] = (u8)(ucr->cmd_idx >> 8); |
238 | ucr->cmd_buf[CNT_L] = (u8)(ucr->cmd_idx); | 238 | ucr->cmd_buf[CNT_L] = (u8)(ucr->cmd_idx); |
239 | ucr->cmd_buf[STAGE_FLAG] = flag; | 239 | ucr->cmd_buf[STAGE_FLAG] = flag; |
240 | 240 | ||
241 | ret = rtsx_usb_transfer_data(ucr, | 241 | ret = rtsx_usb_transfer_data(ucr, |
242 | usb_sndbulkpipe(ucr->pusb_dev, EP_BULK_OUT), | 242 | usb_sndbulkpipe(ucr->pusb_dev, EP_BULK_OUT), |
243 | ucr->cmd_buf, ucr->cmd_idx * 4 + CMD_OFFSET, | 243 | ucr->cmd_buf, ucr->cmd_idx * 4 + CMD_OFFSET, |
244 | 0, NULL, timeout); | 244 | 0, NULL, timeout); |
245 | if (ret) { | 245 | if (ret) { |
246 | rtsx_usb_clear_fsm_err(ucr); | 246 | rtsx_usb_clear_fsm_err(ucr); |
247 | return ret; | 247 | return ret; |
248 | } | 248 | } |
249 | 249 | ||
250 | return 0; | 250 | return 0; |
251 | } | 251 | } |
252 | EXPORT_SYMBOL_GPL(rtsx_usb_send_cmd); | 252 | EXPORT_SYMBOL_GPL(rtsx_usb_send_cmd); |
253 | 253 | ||
254 | int rtsx_usb_get_rsp(struct rtsx_ucr *ucr, int rsp_len, int timeout) | 254 | int rtsx_usb_get_rsp(struct rtsx_ucr *ucr, int rsp_len, int timeout) |
255 | { | 255 | { |
256 | if (rsp_len <= 0) | 256 | if (rsp_len <= 0) |
257 | return -EINVAL; | 257 | return -EINVAL; |
258 | 258 | ||
259 | rsp_len = ALIGN(rsp_len, 4); | 259 | rsp_len = ALIGN(rsp_len, 4); |
260 | 260 | ||
261 | return rtsx_usb_transfer_data(ucr, | 261 | return rtsx_usb_transfer_data(ucr, |
262 | usb_rcvbulkpipe(ucr->pusb_dev, EP_BULK_IN), | 262 | usb_rcvbulkpipe(ucr->pusb_dev, EP_BULK_IN), |
263 | ucr->rsp_buf, rsp_len, 0, NULL, timeout); | 263 | ucr->rsp_buf, rsp_len, 0, NULL, timeout); |
264 | } | 264 | } |
265 | EXPORT_SYMBOL_GPL(rtsx_usb_get_rsp); | 265 | EXPORT_SYMBOL_GPL(rtsx_usb_get_rsp); |
266 | 266 | ||
267 | static int rtsx_usb_get_status_with_bulk(struct rtsx_ucr *ucr, u16 *status) | 267 | static int rtsx_usb_get_status_with_bulk(struct rtsx_ucr *ucr, u16 *status) |
268 | { | 268 | { |
269 | int ret; | 269 | int ret; |
270 | 270 | ||
271 | rtsx_usb_init_cmd(ucr); | 271 | rtsx_usb_init_cmd(ucr); |
272 | rtsx_usb_add_cmd(ucr, READ_REG_CMD, CARD_EXIST, 0x00, 0x00); | 272 | rtsx_usb_add_cmd(ucr, READ_REG_CMD, CARD_EXIST, 0x00, 0x00); |
273 | rtsx_usb_add_cmd(ucr, READ_REG_CMD, OCPSTAT, 0x00, 0x00); | 273 | rtsx_usb_add_cmd(ucr, READ_REG_CMD, OCPSTAT, 0x00, 0x00); |
274 | ret = rtsx_usb_send_cmd(ucr, MODE_CR, 100); | 274 | ret = rtsx_usb_send_cmd(ucr, MODE_CR, 100); |
275 | if (ret) | 275 | if (ret) |
276 | return ret; | 276 | return ret; |
277 | 277 | ||
278 | ret = rtsx_usb_get_rsp(ucr, 2, 100); | 278 | ret = rtsx_usb_get_rsp(ucr, 2, 100); |
279 | if (ret) | 279 | if (ret) |
280 | return ret; | 280 | return ret; |
281 | 281 | ||
282 | *status = ((ucr->rsp_buf[0] >> 2) & 0x0f) | | 282 | *status = ((ucr->rsp_buf[0] >> 2) & 0x0f) | |
283 | ((ucr->rsp_buf[1] & 0x03) << 4); | 283 | ((ucr->rsp_buf[1] & 0x03) << 4); |
284 | 284 | ||
285 | return 0; | 285 | return 0; |
286 | } | 286 | } |
287 | 287 | ||
288 | int rtsx_usb_get_card_status(struct rtsx_ucr *ucr, u16 *status) | 288 | int rtsx_usb_get_card_status(struct rtsx_ucr *ucr, u16 *status) |
289 | { | 289 | { |
290 | int ret; | 290 | int ret; |
291 | 291 | ||
292 | if (!status) | 292 | if (!status) |
293 | return -EINVAL; | 293 | return -EINVAL; |
294 | 294 | ||
295 | if (polling_pipe == 0) | 295 | if (polling_pipe == 0) |
296 | ret = usb_control_msg(ucr->pusb_dev, | 296 | ret = usb_control_msg(ucr->pusb_dev, |
297 | usb_rcvctrlpipe(ucr->pusb_dev, 0), | 297 | usb_rcvctrlpipe(ucr->pusb_dev, 0), |
298 | RTSX_USB_REQ_POLL, | 298 | RTSX_USB_REQ_POLL, |
299 | USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | 299 | USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, |
300 | 0, 0, status, 2, 100); | 300 | 0, 0, status, 2, 100); |
301 | else | 301 | else |
302 | ret = rtsx_usb_get_status_with_bulk(ucr, status); | 302 | ret = rtsx_usb_get_status_with_bulk(ucr, status); |
303 | 303 | ||
304 | /* usb_control_msg may return positive when success */ | 304 | /* usb_control_msg may return positive when success */ |
305 | if (ret < 0) | 305 | if (ret < 0) |
306 | return ret; | 306 | return ret; |
307 | 307 | ||
308 | return 0; | 308 | return 0; |
309 | } | 309 | } |
310 | EXPORT_SYMBOL_GPL(rtsx_usb_get_card_status); | 310 | EXPORT_SYMBOL_GPL(rtsx_usb_get_card_status); |
311 | 311 | ||
312 | static int rtsx_usb_write_phy_register(struct rtsx_ucr *ucr, u8 addr, u8 val) | 312 | static int rtsx_usb_write_phy_register(struct rtsx_ucr *ucr, u8 addr, u8 val) |
313 | { | 313 | { |
314 | dev_dbg(&ucr->pusb_intf->dev, "Write 0x%x to phy register 0x%x\n", | 314 | dev_dbg(&ucr->pusb_intf->dev, "Write 0x%x to phy register 0x%x\n", |
315 | val, addr); | 315 | val, addr); |
316 | 316 | ||
317 | rtsx_usb_init_cmd(ucr); | 317 | rtsx_usb_init_cmd(ucr); |
318 | 318 | ||
319 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VSTAIN, 0xFF, val); | 319 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VSTAIN, 0xFF, val); |
320 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VCONTROL, 0xFF, addr & 0x0F); | 320 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VCONTROL, 0xFF, addr & 0x0F); |
321 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00); | 321 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00); |
322 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00); | 322 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00); |
323 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x01); | 323 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x01); |
324 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VCONTROL, | 324 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VCONTROL, |
325 | 0xFF, (addr >> 4) & 0x0F); | 325 | 0xFF, (addr >> 4) & 0x0F); |
326 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00); | 326 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00); |
327 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00); | 327 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00); |
328 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x01); | 328 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x01); |
329 | 329 | ||
330 | return rtsx_usb_send_cmd(ucr, MODE_C, 100); | 330 | return rtsx_usb_send_cmd(ucr, MODE_C, 100); |
331 | } | 331 | } |
332 | 332 | ||
333 | int rtsx_usb_write_register(struct rtsx_ucr *ucr, u16 addr, u8 mask, u8 data) | 333 | int rtsx_usb_write_register(struct rtsx_ucr *ucr, u16 addr, u8 mask, u8 data) |
334 | { | 334 | { |
335 | rtsx_usb_init_cmd(ucr); | 335 | rtsx_usb_init_cmd(ucr); |
336 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, addr, mask, data); | 336 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, addr, mask, data); |
337 | return rtsx_usb_send_cmd(ucr, MODE_C, 100); | 337 | return rtsx_usb_send_cmd(ucr, MODE_C, 100); |
338 | } | 338 | } |
339 | EXPORT_SYMBOL_GPL(rtsx_usb_write_register); | 339 | EXPORT_SYMBOL_GPL(rtsx_usb_write_register); |
340 | 340 | ||
341 | int rtsx_usb_read_register(struct rtsx_ucr *ucr, u16 addr, u8 *data) | 341 | int rtsx_usb_read_register(struct rtsx_ucr *ucr, u16 addr, u8 *data) |
342 | { | 342 | { |
343 | int ret; | 343 | int ret; |
344 | 344 | ||
345 | if (data != NULL) | 345 | if (data != NULL) |
346 | *data = 0; | 346 | *data = 0; |
347 | 347 | ||
348 | rtsx_usb_init_cmd(ucr); | 348 | rtsx_usb_init_cmd(ucr); |
349 | rtsx_usb_add_cmd(ucr, READ_REG_CMD, addr, 0, 0); | 349 | rtsx_usb_add_cmd(ucr, READ_REG_CMD, addr, 0, 0); |
350 | ret = rtsx_usb_send_cmd(ucr, MODE_CR, 100); | 350 | ret = rtsx_usb_send_cmd(ucr, MODE_CR, 100); |
351 | if (ret) | 351 | if (ret) |
352 | return ret; | 352 | return ret; |
353 | 353 | ||
354 | ret = rtsx_usb_get_rsp(ucr, 1, 100); | 354 | ret = rtsx_usb_get_rsp(ucr, 1, 100); |
355 | if (ret) | 355 | if (ret) |
356 | return ret; | 356 | return ret; |
357 | 357 | ||
358 | if (data != NULL) | 358 | if (data != NULL) |
359 | *data = ucr->rsp_buf[0]; | 359 | *data = ucr->rsp_buf[0]; |
360 | 360 | ||
361 | return 0; | 361 | return 0; |
362 | } | 362 | } |
363 | EXPORT_SYMBOL_GPL(rtsx_usb_read_register); | 363 | EXPORT_SYMBOL_GPL(rtsx_usb_read_register); |
364 | 364 | ||
365 | static inline u8 double_ssc_depth(u8 depth) | 365 | static inline u8 double_ssc_depth(u8 depth) |
366 | { | 366 | { |
367 | return (depth > 1) ? (depth - 1) : depth; | 367 | return (depth > 1) ? (depth - 1) : depth; |
368 | } | 368 | } |
369 | 369 | ||
370 | static u8 revise_ssc_depth(u8 ssc_depth, u8 div) | 370 | static u8 revise_ssc_depth(u8 ssc_depth, u8 div) |
371 | { | 371 | { |
372 | if (div > CLK_DIV_1) { | 372 | if (div > CLK_DIV_1) { |
373 | if (ssc_depth > div - 1) | 373 | if (ssc_depth > div - 1) |
374 | ssc_depth -= (div - 1); | 374 | ssc_depth -= (div - 1); |
375 | else | 375 | else |
376 | ssc_depth = SSC_DEPTH_2M; | 376 | ssc_depth = SSC_DEPTH_2M; |
377 | } | 377 | } |
378 | 378 | ||
379 | return ssc_depth; | 379 | return ssc_depth; |
380 | } | 380 | } |
381 | 381 | ||
382 | int rtsx_usb_switch_clock(struct rtsx_ucr *ucr, unsigned int card_clock, | 382 | int rtsx_usb_switch_clock(struct rtsx_ucr *ucr, unsigned int card_clock, |
383 | u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk) | 383 | u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk) |
384 | { | 384 | { |
385 | int ret; | 385 | int ret; |
386 | u8 n, clk_divider, mcu_cnt, div; | 386 | u8 n, clk_divider, mcu_cnt, div; |
387 | 387 | ||
388 | if (!card_clock) { | 388 | if (!card_clock) { |
389 | ucr->cur_clk = 0; | 389 | ucr->cur_clk = 0; |
390 | return 0; | 390 | return 0; |
391 | } | 391 | } |
392 | 392 | ||
393 | if (initial_mode) { | 393 | if (initial_mode) { |
394 | /* We use 250k(around) here, in initial stage */ | 394 | /* We use 250k(around) here, in initial stage */ |
395 | clk_divider = SD_CLK_DIVIDE_128; | 395 | clk_divider = SD_CLK_DIVIDE_128; |
396 | card_clock = 30000000; | 396 | card_clock = 30000000; |
397 | } else { | 397 | } else { |
398 | clk_divider = SD_CLK_DIVIDE_0; | 398 | clk_divider = SD_CLK_DIVIDE_0; |
399 | } | 399 | } |
400 | 400 | ||
401 | ret = rtsx_usb_write_register(ucr, SD_CFG1, | 401 | ret = rtsx_usb_write_register(ucr, SD_CFG1, |
402 | SD_CLK_DIVIDE_MASK, clk_divider); | 402 | SD_CLK_DIVIDE_MASK, clk_divider); |
403 | if (ret < 0) | 403 | if (ret < 0) |
404 | return ret; | 404 | return ret; |
405 | 405 | ||
406 | card_clock /= 1000000; | 406 | card_clock /= 1000000; |
407 | dev_dbg(&ucr->pusb_intf->dev, | 407 | dev_dbg(&ucr->pusb_intf->dev, |
408 | "Switch card clock to %dMHz\n", card_clock); | 408 | "Switch card clock to %dMHz\n", card_clock); |
409 | 409 | ||
410 | if (!initial_mode && double_clk) | 410 | if (!initial_mode && double_clk) |
411 | card_clock *= 2; | 411 | card_clock *= 2; |
412 | dev_dbg(&ucr->pusb_intf->dev, | 412 | dev_dbg(&ucr->pusb_intf->dev, |
413 | "Internal SSC clock: %dMHz (cur_clk = %d)\n", | 413 | "Internal SSC clock: %dMHz (cur_clk = %d)\n", |
414 | card_clock, ucr->cur_clk); | 414 | card_clock, ucr->cur_clk); |
415 | 415 | ||
416 | if (card_clock == ucr->cur_clk) | 416 | if (card_clock == ucr->cur_clk) |
417 | return 0; | 417 | return 0; |
418 | 418 | ||
419 | /* Converting clock value into internal settings: n and div */ | 419 | /* Converting clock value into internal settings: n and div */ |
420 | n = card_clock - 2; | 420 | n = card_clock - 2; |
421 | if ((card_clock <= 2) || (n > MAX_DIV_N)) | 421 | if ((card_clock <= 2) || (n > MAX_DIV_N)) |
422 | return -EINVAL; | 422 | return -EINVAL; |
423 | 423 | ||
424 | mcu_cnt = 60/card_clock + 3; | 424 | mcu_cnt = 60/card_clock + 3; |
425 | if (mcu_cnt > 15) | 425 | if (mcu_cnt > 15) |
426 | mcu_cnt = 15; | 426 | mcu_cnt = 15; |
427 | 427 | ||
428 | /* Make sure that the SSC clock div_n is not less than MIN_DIV_N */ | 428 | /* Make sure that the SSC clock div_n is not less than MIN_DIV_N */ |
429 | 429 | ||
430 | div = CLK_DIV_1; | 430 | div = CLK_DIV_1; |
431 | while (n < MIN_DIV_N && div < CLK_DIV_4) { | 431 | while (n < MIN_DIV_N && div < CLK_DIV_4) { |
432 | n = (n + 2) * 2 - 2; | 432 | n = (n + 2) * 2 - 2; |
433 | div++; | 433 | div++; |
434 | } | 434 | } |
435 | dev_dbg(&ucr->pusb_intf->dev, "n = %d, div = %d\n", n, div); | 435 | dev_dbg(&ucr->pusb_intf->dev, "n = %d, div = %d\n", n, div); |
436 | 436 | ||
437 | if (double_clk) | 437 | if (double_clk) |
438 | ssc_depth = double_ssc_depth(ssc_depth); | 438 | ssc_depth = double_ssc_depth(ssc_depth); |
439 | 439 | ||
440 | ssc_depth = revise_ssc_depth(ssc_depth, div); | 440 | ssc_depth = revise_ssc_depth(ssc_depth, div); |
441 | dev_dbg(&ucr->pusb_intf->dev, "ssc_depth = %d\n", ssc_depth); | 441 | dev_dbg(&ucr->pusb_intf->dev, "ssc_depth = %d\n", ssc_depth); |
442 | 442 | ||
443 | rtsx_usb_init_cmd(ucr); | 443 | rtsx_usb_init_cmd(ucr); |
444 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CLK_DIV, CLK_CHANGE, CLK_CHANGE); | 444 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CLK_DIV, CLK_CHANGE, CLK_CHANGE); |
445 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CLK_DIV, | 445 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CLK_DIV, |
446 | 0x3F, (div << 4) | mcu_cnt); | 446 | 0x3F, (div << 4) | mcu_cnt); |
447 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, 0); | 447 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, 0); |
448 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_CTL2, | 448 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_CTL2, |
449 | SSC_DEPTH_MASK, ssc_depth); | 449 | SSC_DEPTH_MASK, ssc_depth); |
450 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_DIV_N_0, 0xFF, n); | 450 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_DIV_N_0, 0xFF, n); |
451 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, SSC_RSTB); | 451 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, SSC_RSTB); |
452 | if (vpclk) { | 452 | if (vpclk) { |
453 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_VPCLK0_CTL, | 453 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_VPCLK0_CTL, |
454 | PHASE_NOT_RESET, 0); | 454 | PHASE_NOT_RESET, 0); |
455 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_VPCLK0_CTL, | 455 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_VPCLK0_CTL, |
456 | PHASE_NOT_RESET, PHASE_NOT_RESET); | 456 | PHASE_NOT_RESET, PHASE_NOT_RESET); |
457 | } | 457 | } |
458 | 458 | ||
459 | ret = rtsx_usb_send_cmd(ucr, MODE_C, 2000); | 459 | ret = rtsx_usb_send_cmd(ucr, MODE_C, 2000); |
460 | if (ret < 0) | 460 | if (ret < 0) |
461 | return ret; | 461 | return ret; |
462 | 462 | ||
463 | ret = rtsx_usb_write_register(ucr, SSC_CTL1, 0xff, | 463 | ret = rtsx_usb_write_register(ucr, SSC_CTL1, 0xff, |
464 | SSC_RSTB | SSC_8X_EN | SSC_SEL_4M); | 464 | SSC_RSTB | SSC_8X_EN | SSC_SEL_4M); |
465 | if (ret < 0) | 465 | if (ret < 0) |
466 | return ret; | 466 | return ret; |
467 | 467 | ||
468 | /* Wait SSC clock stable */ | 468 | /* Wait SSC clock stable */ |
469 | usleep_range(100, 1000); | 469 | usleep_range(100, 1000); |
470 | 470 | ||
471 | ret = rtsx_usb_write_register(ucr, CLK_DIV, CLK_CHANGE, 0); | 471 | ret = rtsx_usb_write_register(ucr, CLK_DIV, CLK_CHANGE, 0); |
472 | if (ret < 0) | 472 | if (ret < 0) |
473 | return ret; | 473 | return ret; |
474 | 474 | ||
475 | ucr->cur_clk = card_clock; | 475 | ucr->cur_clk = card_clock; |
476 | 476 | ||
477 | return 0; | 477 | return 0; |
478 | } | 478 | } |
479 | EXPORT_SYMBOL_GPL(rtsx_usb_switch_clock); | 479 | EXPORT_SYMBOL_GPL(rtsx_usb_switch_clock); |
480 | 480 | ||
481 | int rtsx_usb_card_exclusive_check(struct rtsx_ucr *ucr, int card) | 481 | int rtsx_usb_card_exclusive_check(struct rtsx_ucr *ucr, int card) |
482 | { | 482 | { |
483 | int ret; | 483 | int ret; |
484 | u16 val; | 484 | u16 val; |
485 | u16 cd_mask[] = { | 485 | u16 cd_mask[] = { |
486 | [RTSX_USB_SD_CARD] = (CD_MASK & ~SD_CD), | 486 | [RTSX_USB_SD_CARD] = (CD_MASK & ~SD_CD), |
487 | [RTSX_USB_MS_CARD] = (CD_MASK & ~MS_CD) | 487 | [RTSX_USB_MS_CARD] = (CD_MASK & ~MS_CD) |
488 | }; | 488 | }; |
489 | 489 | ||
490 | ret = rtsx_usb_get_card_status(ucr, &val); | 490 | ret = rtsx_usb_get_card_status(ucr, &val); |
491 | /* | 491 | /* |
492 | * If get status fails, return 0 (ok) for the exclusive check | 492 | * If get status fails, return 0 (ok) for the exclusive check |
493 | * and let the flow fail at somewhere else. | 493 | * and let the flow fail at somewhere else. |
494 | */ | 494 | */ |
495 | if (ret) | 495 | if (ret) |
496 | return 0; | 496 | return 0; |
497 | 497 | ||
498 | if (val & cd_mask[card]) | 498 | if (val & cd_mask[card]) |
499 | return -EIO; | 499 | return -EIO; |
500 | 500 | ||
501 | return 0; | 501 | return 0; |
502 | } | 502 | } |
503 | EXPORT_SYMBOL_GPL(rtsx_usb_card_exclusive_check); | 503 | EXPORT_SYMBOL_GPL(rtsx_usb_card_exclusive_check); |
504 | 504 | ||
505 | static int rtsx_usb_reset_chip(struct rtsx_ucr *ucr) | 505 | static int rtsx_usb_reset_chip(struct rtsx_ucr *ucr) |
506 | { | 506 | { |
507 | int ret; | 507 | int ret; |
508 | u8 val; | 508 | u8 val; |
509 | 509 | ||
510 | rtsx_usb_init_cmd(ucr); | 510 | rtsx_usb_init_cmd(ucr); |
511 | 511 | ||
512 | if (CHECK_PKG(ucr, LQFP48)) { | 512 | if (CHECK_PKG(ucr, LQFP48)) { |
513 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL, | 513 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL, |
514 | LDO3318_PWR_MASK, LDO_SUSPEND); | 514 | LDO3318_PWR_MASK, LDO_SUSPEND); |
515 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL, | 515 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL, |
516 | FORCE_LDO_POWERB, FORCE_LDO_POWERB); | 516 | FORCE_LDO_POWERB, FORCE_LDO_POWERB); |
517 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1, | 517 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1, |
518 | 0x30, 0x10); | 518 | 0x30, 0x10); |
519 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, | 519 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, |
520 | 0x03, 0x01); | 520 | 0x03, 0x01); |
521 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, | 521 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, |
522 | 0x0C, 0x04); | 522 | 0x0C, 0x04); |
523 | } | 523 | } |
524 | 524 | ||
525 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SYS_DUMMY0, NYET_MSAK, NYET_EN); | 525 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SYS_DUMMY0, NYET_MSAK, NYET_EN); |
526 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CD_DEGLITCH_WIDTH, 0xFF, 0x08); | 526 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CD_DEGLITCH_WIDTH, 0xFF, 0x08); |
527 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, | 527 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, |
528 | CD_DEGLITCH_EN, XD_CD_DEGLITCH_EN, 0x0); | 528 | CD_DEGLITCH_EN, XD_CD_DEGLITCH_EN, 0x0); |
529 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD30_DRIVE_SEL, | 529 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD30_DRIVE_SEL, |
530 | SD30_DRIVE_MASK, DRIVER_TYPE_D); | 530 | SD30_DRIVE_MASK, DRIVER_TYPE_D); |
531 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, | 531 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, |
532 | CARD_DRIVE_SEL, SD20_DRIVE_MASK, 0x0); | 532 | CARD_DRIVE_SEL, SD20_DRIVE_MASK, 0x0); |
533 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, LDO_POWER_CFG, 0xE0, 0x0); | 533 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, LDO_POWER_CFG, 0xE0, 0x0); |
534 | 534 | ||
535 | if (ucr->is_rts5179) | 535 | if (ucr->is_rts5179) |
536 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, | 536 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, |
537 | CARD_PULL_CTL5, 0x03, 0x01); | 537 | CARD_PULL_CTL5, 0x03, 0x01); |
538 | 538 | ||
539 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_DMA1_CTL, | 539 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_DMA1_CTL, |
540 | EXTEND_DMA1_ASYNC_SIGNAL, EXTEND_DMA1_ASYNC_SIGNAL); | 540 | EXTEND_DMA1_ASYNC_SIGNAL, EXTEND_DMA1_ASYNC_SIGNAL); |
541 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_INT_PEND, | 541 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_INT_PEND, |
542 | XD_INT | MS_INT | SD_INT, | 542 | XD_INT | MS_INT | SD_INT, |
543 | XD_INT | MS_INT | SD_INT); | 543 | XD_INT | MS_INT | SD_INT); |
544 | 544 | ||
545 | ret = rtsx_usb_send_cmd(ucr, MODE_C, 100); | 545 | ret = rtsx_usb_send_cmd(ucr, MODE_C, 100); |
546 | if (ret) | 546 | if (ret) |
547 | return ret; | 547 | return ret; |
548 | 548 | ||
549 | /* config non-crystal mode */ | 549 | /* config non-crystal mode */ |
550 | rtsx_usb_read_register(ucr, CFG_MODE, &val); | 550 | rtsx_usb_read_register(ucr, CFG_MODE, &val); |
551 | if ((val & XTAL_FREE) || ((val & CLK_MODE_MASK) == CLK_MODE_NON_XTAL)) { | 551 | if ((val & XTAL_FREE) || ((val & CLK_MODE_MASK) == CLK_MODE_NON_XTAL)) { |
552 | ret = rtsx_usb_write_phy_register(ucr, 0xC2, 0x7C); | 552 | ret = rtsx_usb_write_phy_register(ucr, 0xC2, 0x7C); |
553 | if (ret) | 553 | if (ret) |
554 | return ret; | 554 | return ret; |
555 | } | 555 | } |
556 | 556 | ||
557 | return 0; | 557 | return 0; |
558 | } | 558 | } |
559 | 559 | ||
560 | static int rtsx_usb_init_chip(struct rtsx_ucr *ucr) | 560 | static int rtsx_usb_init_chip(struct rtsx_ucr *ucr) |
561 | { | 561 | { |
562 | int ret; | 562 | int ret; |
563 | u8 val; | 563 | u8 val; |
564 | 564 | ||
565 | rtsx_usb_clear_fsm_err(ucr); | 565 | rtsx_usb_clear_fsm_err(ucr); |
566 | 566 | ||
567 | /* power on SSC */ | 567 | /* power on SSC */ |
568 | ret = rtsx_usb_write_register(ucr, | 568 | ret = rtsx_usb_write_register(ucr, |
569 | FPDCTL, SSC_POWER_MASK, SSC_POWER_ON); | 569 | FPDCTL, SSC_POWER_MASK, SSC_POWER_ON); |
570 | if (ret) | 570 | if (ret) |
571 | return ret; | 571 | return ret; |
572 | 572 | ||
573 | usleep_range(100, 1000); | 573 | usleep_range(100, 1000); |
574 | ret = rtsx_usb_write_register(ucr, CLK_DIV, CLK_CHANGE, 0x00); | 574 | ret = rtsx_usb_write_register(ucr, CLK_DIV, CLK_CHANGE, 0x00); |
575 | if (ret) | 575 | if (ret) |
576 | return ret; | 576 | return ret; |
577 | 577 | ||
578 | /* determine IC version */ | 578 | /* determine IC version */ |
579 | ret = rtsx_usb_read_register(ucr, HW_VERSION, &val); | 579 | ret = rtsx_usb_read_register(ucr, HW_VERSION, &val); |
580 | if (ret) | 580 | if (ret) |
581 | return ret; | 581 | return ret; |
582 | 582 | ||
583 | ucr->ic_version = val & HW_VER_MASK; | 583 | ucr->ic_version = val & HW_VER_MASK; |
584 | 584 | ||
585 | /* determine package */ | 585 | /* determine package */ |
586 | ret = rtsx_usb_read_register(ucr, CARD_SHARE_MODE, &val); | 586 | ret = rtsx_usb_read_register(ucr, CARD_SHARE_MODE, &val); |
587 | if (ret) | 587 | if (ret) |
588 | return ret; | 588 | return ret; |
589 | 589 | ||
590 | if (val & CARD_SHARE_LQFP_SEL) { | 590 | if (val & CARD_SHARE_LQFP_SEL) { |
591 | ucr->package = LQFP48; | 591 | ucr->package = LQFP48; |
592 | dev_dbg(&ucr->pusb_intf->dev, "Package: LQFP48\n"); | 592 | dev_dbg(&ucr->pusb_intf->dev, "Package: LQFP48\n"); |
593 | } else { | 593 | } else { |
594 | ucr->package = QFN24; | 594 | ucr->package = QFN24; |
595 | dev_dbg(&ucr->pusb_intf->dev, "Package: QFN24\n"); | 595 | dev_dbg(&ucr->pusb_intf->dev, "Package: QFN24\n"); |
596 | } | 596 | } |
597 | 597 | ||
598 | /* determine IC variations */ | 598 | /* determine IC variations */ |
599 | rtsx_usb_read_register(ucr, CFG_MODE_1, &val); | 599 | rtsx_usb_read_register(ucr, CFG_MODE_1, &val); |
600 | if (val & RTS5179) { | 600 | if (val & RTS5179) { |
601 | ucr->is_rts5179 = true; | 601 | ucr->is_rts5179 = true; |
602 | dev_dbg(&ucr->pusb_intf->dev, "Device is rts5179\n"); | 602 | dev_dbg(&ucr->pusb_intf->dev, "Device is rts5179\n"); |
603 | } else { | 603 | } else { |
604 | ucr->is_rts5179 = false; | 604 | ucr->is_rts5179 = false; |
605 | } | 605 | } |
606 | 606 | ||
607 | return rtsx_usb_reset_chip(ucr); | 607 | return rtsx_usb_reset_chip(ucr); |
608 | } | 608 | } |
609 | 609 | ||
610 | static int rtsx_usb_probe(struct usb_interface *intf, | 610 | static int rtsx_usb_probe(struct usb_interface *intf, |
611 | const struct usb_device_id *id) | 611 | const struct usb_device_id *id) |
612 | { | 612 | { |
613 | struct usb_device *usb_dev = interface_to_usbdev(intf); | 613 | struct usb_device *usb_dev = interface_to_usbdev(intf); |
614 | struct rtsx_ucr *ucr; | 614 | struct rtsx_ucr *ucr; |
615 | int ret; | 615 | int ret; |
616 | 616 | ||
617 | dev_dbg(&intf->dev, | 617 | dev_dbg(&intf->dev, |
618 | ": Realtek USB Card Reader found at bus %03d address %03d\n", | 618 | ": Realtek USB Card Reader found at bus %03d address %03d\n", |
619 | usb_dev->bus->busnum, usb_dev->devnum); | 619 | usb_dev->bus->busnum, usb_dev->devnum); |
620 | 620 | ||
621 | ucr = devm_kzalloc(&intf->dev, sizeof(*ucr), GFP_KERNEL); | 621 | ucr = devm_kzalloc(&intf->dev, sizeof(*ucr), GFP_KERNEL); |
622 | if (!ucr) | 622 | if (!ucr) |
623 | return -ENOMEM; | 623 | return -ENOMEM; |
624 | 624 | ||
625 | ucr->pusb_dev = usb_dev; | 625 | ucr->pusb_dev = usb_dev; |
626 | 626 | ||
627 | ucr->iobuf = usb_alloc_coherent(ucr->pusb_dev, IOBUF_SIZE, | 627 | ucr->iobuf = usb_alloc_coherent(ucr->pusb_dev, IOBUF_SIZE, |
628 | GFP_KERNEL, &ucr->iobuf_dma); | 628 | GFP_KERNEL, &ucr->iobuf_dma); |
629 | if (!ucr->iobuf) | 629 | if (!ucr->iobuf) |
630 | return -ENOMEM; | 630 | return -ENOMEM; |
631 | 631 | ||
632 | usb_set_intfdata(intf, ucr); | 632 | usb_set_intfdata(intf, ucr); |
633 | 633 | ||
634 | ucr->vendor_id = id->idVendor; | 634 | ucr->vendor_id = id->idVendor; |
635 | ucr->product_id = id->idProduct; | 635 | ucr->product_id = id->idProduct; |
636 | ucr->cmd_buf = ucr->rsp_buf = ucr->iobuf; | 636 | ucr->cmd_buf = ucr->rsp_buf = ucr->iobuf; |
637 | 637 | ||
638 | mutex_init(&ucr->dev_mutex); | 638 | mutex_init(&ucr->dev_mutex); |
639 | 639 | ||
640 | ucr->pusb_intf = intf; | 640 | ucr->pusb_intf = intf; |
641 | 641 | ||
642 | /* initialize */ | 642 | /* initialize */ |
643 | ret = rtsx_usb_init_chip(ucr); | 643 | ret = rtsx_usb_init_chip(ucr); |
644 | if (ret) | 644 | if (ret) |
645 | goto out_init_fail; | 645 | goto out_init_fail; |
646 | 646 | ||
647 | /* initialize USB SG transfer timer */ | 647 | /* initialize USB SG transfer timer */ |
648 | setup_timer(&ucr->sg_timer, rtsx_usb_sg_timed_out, (unsigned long) ucr); | 648 | setup_timer(&ucr->sg_timer, rtsx_usb_sg_timed_out, (unsigned long) ucr); |
649 | 649 | ||
650 | ret = mfd_add_hotplug_devices(&intf->dev, rtsx_usb_cells, | 650 | ret = mfd_add_hotplug_devices(&intf->dev, rtsx_usb_cells, |
651 | ARRAY_SIZE(rtsx_usb_cells)); | 651 | ARRAY_SIZE(rtsx_usb_cells)); |
652 | if (ret) | 652 | if (ret) |
653 | goto out_init_fail; | 653 | goto out_init_fail; |
654 | 654 | ||
655 | #ifdef CONFIG_PM | 655 | #ifdef CONFIG_PM |
656 | intf->needs_remote_wakeup = 1; | 656 | intf->needs_remote_wakeup = 1; |
657 | usb_enable_autosuspend(usb_dev); | 657 | usb_enable_autosuspend(usb_dev); |
658 | #endif | 658 | #endif |
659 | 659 | ||
660 | return 0; | 660 | return 0; |
661 | 661 | ||
662 | out_init_fail: | 662 | out_init_fail: |
663 | usb_free_coherent(ucr->pusb_dev, IOBUF_SIZE, ucr->iobuf, | 663 | usb_free_coherent(ucr->pusb_dev, IOBUF_SIZE, ucr->iobuf, |
664 | ucr->iobuf_dma); | 664 | ucr->iobuf_dma); |
665 | return ret; | 665 | return ret; |
666 | } | 666 | } |
667 | 667 | ||
668 | static void rtsx_usb_disconnect(struct usb_interface *intf) | 668 | static void rtsx_usb_disconnect(struct usb_interface *intf) |
669 | { | 669 | { |
670 | struct rtsx_ucr *ucr = (struct rtsx_ucr *)usb_get_intfdata(intf); | 670 | struct rtsx_ucr *ucr = (struct rtsx_ucr *)usb_get_intfdata(intf); |
671 | 671 | ||
672 | dev_dbg(&intf->dev, "%s called\n", __func__); | 672 | dev_dbg(&intf->dev, "%s called\n", __func__); |
673 | 673 | ||
674 | mfd_remove_devices(&intf->dev); | 674 | mfd_remove_devices(&intf->dev); |
675 | 675 | ||
676 | usb_set_intfdata(ucr->pusb_intf, NULL); | 676 | usb_set_intfdata(ucr->pusb_intf, NULL); |
677 | usb_free_coherent(ucr->pusb_dev, IOBUF_SIZE, ucr->iobuf, | 677 | usb_free_coherent(ucr->pusb_dev, IOBUF_SIZE, ucr->iobuf, |
678 | ucr->iobuf_dma); | 678 | ucr->iobuf_dma); |
679 | } | 679 | } |
680 | 680 | ||
681 | #ifdef CONFIG_PM | 681 | #ifdef CONFIG_PM |
682 | static int rtsx_usb_suspend(struct usb_interface *intf, pm_message_t message) | 682 | static int rtsx_usb_suspend(struct usb_interface *intf, pm_message_t message) |
683 | { | 683 | { |
684 | struct rtsx_ucr *ucr = | ||
685 | (struct rtsx_ucr *)usb_get_intfdata(intf); | ||
686 | |||
687 | dev_dbg(&intf->dev, "%s called with pm message 0x%04x\n", | 684 | dev_dbg(&intf->dev, "%s called with pm message 0x%04x\n", |
688 | __func__, message.event); | 685 | __func__, message.event); |
689 | |||
690 | /* | ||
691 | * Call to make sure LED is off during suspend to save more power. | ||
692 | * It is NOT a permanent state and could be turned on anytime later. | ||
693 | * Thus no need to call turn_on when resunming. | ||
694 | */ | ||
695 | mutex_lock(&ucr->dev_mutex); | ||
696 | rtsx_usb_turn_off_led(ucr); | ||
697 | mutex_unlock(&ucr->dev_mutex); | ||
698 | 686 | ||
699 | return 0; | 687 | return 0; |
700 | } | 688 | } |
701 | 689 | ||
702 | static int rtsx_usb_resume(struct usb_interface *intf) | 690 | static int rtsx_usb_resume(struct usb_interface *intf) |
703 | { | 691 | { |
704 | return 0; | 692 | return 0; |
705 | } | 693 | } |
706 | 694 | ||
707 | static int rtsx_usb_reset_resume(struct usb_interface *intf) | 695 | static int rtsx_usb_reset_resume(struct usb_interface *intf) |
708 | { | 696 | { |
709 | struct rtsx_ucr *ucr = | 697 | struct rtsx_ucr *ucr = |
710 | (struct rtsx_ucr *)usb_get_intfdata(intf); | 698 | (struct rtsx_ucr *)usb_get_intfdata(intf); |
711 | 699 | ||
712 | rtsx_usb_reset_chip(ucr); | 700 | rtsx_usb_reset_chip(ucr); |
713 | return 0; | 701 | return 0; |
714 | } | 702 | } |
715 | 703 | ||
716 | #else /* CONFIG_PM */ | 704 | #else /* CONFIG_PM */ |
717 | 705 | ||
718 | #define rtsx_usb_suspend NULL | 706 | #define rtsx_usb_suspend NULL |
719 | #define rtsx_usb_resume NULL | 707 | #define rtsx_usb_resume NULL |
720 | #define rtsx_usb_reset_resume NULL | 708 | #define rtsx_usb_reset_resume NULL |
721 | 709 | ||
722 | #endif /* CONFIG_PM */ | 710 | #endif /* CONFIG_PM */ |
723 | 711 | ||
724 | 712 | ||
725 | static int rtsx_usb_pre_reset(struct usb_interface *intf) | 713 | static int rtsx_usb_pre_reset(struct usb_interface *intf) |
726 | { | 714 | { |
727 | struct rtsx_ucr *ucr = (struct rtsx_ucr *)usb_get_intfdata(intf); | 715 | struct rtsx_ucr *ucr = (struct rtsx_ucr *)usb_get_intfdata(intf); |
728 | 716 | ||
729 | mutex_lock(&ucr->dev_mutex); | 717 | mutex_lock(&ucr->dev_mutex); |
730 | return 0; | 718 | return 0; |
731 | } | 719 | } |
732 | 720 | ||
733 | static int rtsx_usb_post_reset(struct usb_interface *intf) | 721 | static int rtsx_usb_post_reset(struct usb_interface *intf) |
734 | { | 722 | { |
735 | struct rtsx_ucr *ucr = (struct rtsx_ucr *)usb_get_intfdata(intf); | 723 | struct rtsx_ucr *ucr = (struct rtsx_ucr *)usb_get_intfdata(intf); |
736 | 724 | ||
737 | mutex_unlock(&ucr->dev_mutex); | 725 | mutex_unlock(&ucr->dev_mutex); |
738 | return 0; | 726 | return 0; |
739 | } | 727 | } |
740 | 728 | ||
741 | static struct usb_device_id rtsx_usb_usb_ids[] = { | 729 | static struct usb_device_id rtsx_usb_usb_ids[] = { |
742 | { USB_DEVICE(0x0BDA, 0x0129) }, | 730 | { USB_DEVICE(0x0BDA, 0x0129) }, |
743 | { USB_DEVICE(0x0BDA, 0x0139) }, | 731 | { USB_DEVICE(0x0BDA, 0x0139) }, |
744 | { USB_DEVICE(0x0BDA, 0x0140) }, | 732 | { USB_DEVICE(0x0BDA, 0x0140) }, |
745 | { } | 733 | { } |
746 | }; | 734 | }; |
747 | MODULE_DEVICE_TABLE(usb, rtsx_usb_usb_ids); | 735 | MODULE_DEVICE_TABLE(usb, rtsx_usb_usb_ids); |
748 | 736 | ||
749 | static struct usb_driver rtsx_usb_driver = { | 737 | static struct usb_driver rtsx_usb_driver = { |
750 | .name = "rtsx_usb", | 738 | .name = "rtsx_usb", |
751 | .probe = rtsx_usb_probe, | 739 | .probe = rtsx_usb_probe, |
752 | .disconnect = rtsx_usb_disconnect, | 740 | .disconnect = rtsx_usb_disconnect, |
753 | .suspend = rtsx_usb_suspend, | 741 | .suspend = rtsx_usb_suspend, |
754 | .resume = rtsx_usb_resume, | 742 | .resume = rtsx_usb_resume, |
755 | .reset_resume = rtsx_usb_reset_resume, | 743 | .reset_resume = rtsx_usb_reset_resume, |
756 | .pre_reset = rtsx_usb_pre_reset, | 744 | .pre_reset = rtsx_usb_pre_reset, |
757 | .post_reset = rtsx_usb_post_reset, | 745 | .post_reset = rtsx_usb_post_reset, |
758 | .id_table = rtsx_usb_usb_ids, | 746 | .id_table = rtsx_usb_usb_ids, |
759 | .supports_autosuspend = 1, | 747 | .supports_autosuspend = 1, |
760 | .soft_unbind = 1, | 748 | .soft_unbind = 1, |
761 | }; | 749 | }; |
762 | 750 | ||
763 | module_usb_driver(rtsx_usb_driver); | 751 | module_usb_driver(rtsx_usb_driver); |
764 | 752 | ||
765 | MODULE_LICENSE("GPL v2"); | 753 | MODULE_LICENSE("GPL v2"); |
766 | MODULE_AUTHOR("Roger Tseng <rogerable@realtek.com>"); | 754 | MODULE_AUTHOR("Roger Tseng <rogerable@realtek.com>"); |
767 | MODULE_DESCRIPTION("Realtek USB Card Reader Driver"); | 755 | MODULE_DESCRIPTION("Realtek USB Card Reader Driver"); |
768 | 756 |