blob: c436bf27e78d232340825ad12bf9d493d9ae26a2 [file] [log] [blame]
Wei WANG67d16a42012-11-09 20:53:33 +08001/* Driver for Realtek PCI-Express card reader
2 *
3 * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2, or (at your option) any
8 * later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, see <http://www.gnu.org/licenses/>.
17 *
18 * Author:
19 * Wei WANG <wei_wang@realsil.com.cn>
20 * No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
21 */
22
23#include <linux/module.h>
24#include <linux/bitops.h>
25#include <linux/delay.h>
26#include <linux/mfd/rtsx_pci.h>
27
28#include "rtsx_pcr.h"
29
30static u8 rtl8411_get_ic_version(struct rtsx_pcr *pcr)
31{
32 u8 val;
33
34 rtsx_pci_read_register(pcr, SYS_VER, &val);
35 return val & 0x0F;
36}
37
Roger Tseng9032eab2013-04-19 21:52:42 +080038static int rtl8411b_is_qfn48(struct rtsx_pcr *pcr)
39{
40 u8 val = 0;
41
42 rtsx_pci_read_register(pcr, RTL8411B_PACKAGE_MODE, &val);
43
44 if (val & 0x2)
45 return 1;
46 else
47 return 0;
48}
49
Wei WANG67d16a42012-11-09 20:53:33 +080050static int rtl8411_extra_init_hw(struct rtsx_pcr *pcr)
51{
52 return rtsx_pci_write_register(pcr, CD_PAD_CTL,
53 CD_DISABLE_MASK | CD_AUTO_DISABLE, CD_ENABLE);
54}
55
Roger Tseng9032eab2013-04-19 21:52:42 +080056static int rtl8411b_extra_init_hw(struct rtsx_pcr *pcr)
57{
58 if (rtl8411b_is_qfn48(pcr))
59 rtsx_pci_write_register(pcr, CARD_PULL_CTL3, 0xFF, 0xF5);
60
61 return rtsx_pci_write_register(pcr, CD_PAD_CTL,
62 CD_DISABLE_MASK | CD_AUTO_DISABLE, CD_ENABLE);
63}
64
Wei WANG67d16a42012-11-09 20:53:33 +080065static int rtl8411_turn_on_led(struct rtsx_pcr *pcr)
66{
67 return rtsx_pci_write_register(pcr, CARD_GPIO, 0x01, 0x00);
68}
69
70static int rtl8411_turn_off_led(struct rtsx_pcr *pcr)
71{
72 return rtsx_pci_write_register(pcr, CARD_GPIO, 0x01, 0x01);
73}
74
75static int rtl8411_enable_auto_blink(struct rtsx_pcr *pcr)
76{
77 return rtsx_pci_write_register(pcr, CARD_AUTO_BLINK, 0xFF, 0x0D);
78}
79
80static int rtl8411_disable_auto_blink(struct rtsx_pcr *pcr)
81{
82 return rtsx_pci_write_register(pcr, CARD_AUTO_BLINK, 0x08, 0x00);
83}
84
85static int rtl8411_card_power_on(struct rtsx_pcr *pcr, int card)
86{
87 int err;
88
89 rtsx_pci_init_cmd(pcr);
90 rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL,
91 BPP_POWER_MASK, BPP_POWER_5_PERCENT_ON);
92 rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_CTL,
93 BPP_LDO_POWB, BPP_LDO_SUSPEND);
94 err = rtsx_pci_send_cmd(pcr, 100);
95 if (err < 0)
96 return err;
97
98 /* To avoid too large in-rush current */
99 udelay(150);
100
101 err = rtsx_pci_write_register(pcr, CARD_PWR_CTL,
102 BPP_POWER_MASK, BPP_POWER_10_PERCENT_ON);
103 if (err < 0)
104 return err;
105
106 udelay(150);
107
108 err = rtsx_pci_write_register(pcr, CARD_PWR_CTL,
109 BPP_POWER_MASK, BPP_POWER_15_PERCENT_ON);
110 if (err < 0)
111 return err;
112
113 udelay(150);
114
115 err = rtsx_pci_write_register(pcr, CARD_PWR_CTL,
116 BPP_POWER_MASK, BPP_POWER_ON);
117 if (err < 0)
118 return err;
119
120 return rtsx_pci_write_register(pcr, LDO_CTL, BPP_LDO_POWB, BPP_LDO_ON);
121}
122
123static int rtl8411_card_power_off(struct rtsx_pcr *pcr, int card)
124{
125 int err;
126
127 err = rtsx_pci_write_register(pcr, CARD_PWR_CTL,
128 BPP_POWER_MASK, BPP_POWER_OFF);
129 if (err < 0)
130 return err;
131
132 return rtsx_pci_write_register(pcr, LDO_CTL,
133 BPP_LDO_POWB, BPP_LDO_SUSPEND);
134}
135
Wei WANGd817ac42013-01-23 09:51:04 +0800136static int rtl8411_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage)
137{
138 u8 mask, val;
Roger Tseng88a7ee32013-02-04 15:45:58 +0800139 int err;
Wei WANGd817ac42013-01-23 09:51:04 +0800140
141 mask = (BPP_REG_TUNED18 << BPP_TUNED18_SHIFT_8411) | BPP_PAD_MASK;
Roger Tseng88a7ee32013-02-04 15:45:58 +0800142 if (voltage == OUTPUT_3V3) {
143 err = rtsx_pci_write_register(pcr,
144 SD30_DRIVE_SEL, 0x07, DRIVER_TYPE_D);
145 if (err < 0)
146 return err;
Wei WANGd817ac42013-01-23 09:51:04 +0800147 val = (BPP_ASIC_3V3 << BPP_TUNED18_SHIFT_8411) | BPP_PAD_3V3;
Roger Tseng88a7ee32013-02-04 15:45:58 +0800148 } else if (voltage == OUTPUT_1V8) {
149 err = rtsx_pci_write_register(pcr,
150 SD30_DRIVE_SEL, 0x07, DRIVER_TYPE_B);
151 if (err < 0)
152 return err;
Wei WANGd817ac42013-01-23 09:51:04 +0800153 val = (BPP_ASIC_1V8 << BPP_TUNED18_SHIFT_8411) | BPP_PAD_1V8;
Roger Tseng88a7ee32013-02-04 15:45:58 +0800154 } else {
Wei WANGd817ac42013-01-23 09:51:04 +0800155 return -EINVAL;
Roger Tseng88a7ee32013-02-04 15:45:58 +0800156 }
Wei WANGd817ac42013-01-23 09:51:04 +0800157
158 return rtsx_pci_write_register(pcr, LDO_CTL, mask, val);
159}
160
Wei WANG67d16a42012-11-09 20:53:33 +0800161static unsigned int rtl8411_cd_deglitch(struct rtsx_pcr *pcr)
162{
163 unsigned int card_exist;
164
165 card_exist = rtsx_pci_readl(pcr, RTSX_BIPR);
166 card_exist &= CARD_EXIST;
167 if (!card_exist) {
168 /* Enable card CD */
169 rtsx_pci_write_register(pcr, CD_PAD_CTL,
170 CD_DISABLE_MASK, CD_ENABLE);
171 /* Enable card interrupt */
172 rtsx_pci_write_register(pcr, EFUSE_CONTENT, 0xe0, 0x00);
173 return 0;
174 }
175
176 if (hweight32(card_exist) > 1) {
177 rtsx_pci_write_register(pcr, CARD_PWR_CTL,
178 BPP_POWER_MASK, BPP_POWER_5_PERCENT_ON);
179 msleep(100);
180
181 card_exist = rtsx_pci_readl(pcr, RTSX_BIPR);
182 if (card_exist & MS_EXIST)
183 card_exist = MS_EXIST;
184 else if (card_exist & SD_EXIST)
185 card_exist = SD_EXIST;
186 else
187 card_exist = 0;
188
189 rtsx_pci_write_register(pcr, CARD_PWR_CTL,
190 BPP_POWER_MASK, BPP_POWER_OFF);
191
192 dev_dbg(&(pcr->pci->dev),
193 "After CD deglitch, card_exist = 0x%x\n",
194 card_exist);
195 }
196
197 if (card_exist & MS_EXIST) {
198 /* Disable SD interrupt */
199 rtsx_pci_write_register(pcr, EFUSE_CONTENT, 0xe0, 0x40);
200 rtsx_pci_write_register(pcr, CD_PAD_CTL,
201 CD_DISABLE_MASK, MS_CD_EN_ONLY);
202 } else if (card_exist & SD_EXIST) {
203 /* Disable MS interrupt */
204 rtsx_pci_write_register(pcr, EFUSE_CONTENT, 0xe0, 0x80);
205 rtsx_pci_write_register(pcr, CD_PAD_CTL,
206 CD_DISABLE_MASK, SD_CD_EN_ONLY);
207 }
208
209 return card_exist;
210}
211
Wei WANGab4e8f82013-01-23 09:51:06 +0800212static int rtl8411_conv_clk_and_div_n(int input, int dir)
213{
214 int output;
215
216 if (dir == CLK_TO_DIV_N)
217 output = input * 4 / 5 - 2;
218 else
219 output = (input + 2) * 5 / 4;
220
221 return output;
222}
223
Wei WANG67d16a42012-11-09 20:53:33 +0800224static const struct pcr_ops rtl8411_pcr_ops = {
225 .extra_init_hw = rtl8411_extra_init_hw,
226 .optimize_phy = NULL,
227 .turn_on_led = rtl8411_turn_on_led,
228 .turn_off_led = rtl8411_turn_off_led,
229 .enable_auto_blink = rtl8411_enable_auto_blink,
230 .disable_auto_blink = rtl8411_disable_auto_blink,
231 .card_power_on = rtl8411_card_power_on,
232 .card_power_off = rtl8411_card_power_off,
Wei WANGd817ac42013-01-23 09:51:04 +0800233 .switch_output_voltage = rtl8411_switch_output_voltage,
Wei WANG67d16a42012-11-09 20:53:33 +0800234 .cd_deglitch = rtl8411_cd_deglitch,
Wei WANGab4e8f82013-01-23 09:51:06 +0800235 .conv_clk_and_div_n = rtl8411_conv_clk_and_div_n,
Wei WANG67d16a42012-11-09 20:53:33 +0800236};
237
Roger Tseng9032eab2013-04-19 21:52:42 +0800238static const struct pcr_ops rtl8411b_pcr_ops = {
239 .extra_init_hw = rtl8411b_extra_init_hw,
240 .optimize_phy = NULL,
241 .turn_on_led = rtl8411_turn_on_led,
242 .turn_off_led = rtl8411_turn_off_led,
243 .enable_auto_blink = rtl8411_enable_auto_blink,
244 .disable_auto_blink = rtl8411_disable_auto_blink,
245 .card_power_on = rtl8411_card_power_on,
246 .card_power_off = rtl8411_card_power_off,
247 .switch_output_voltage = rtl8411_switch_output_voltage,
248 .cd_deglitch = rtl8411_cd_deglitch,
249 .conv_clk_and_div_n = rtl8411_conv_clk_and_div_n,
250};
251
Wei WANG67d16a42012-11-09 20:53:33 +0800252/* SD Pull Control Enable:
253 * SD_DAT[3:0] ==> pull up
254 * SD_CD ==> pull up
255 * SD_WP ==> pull up
256 * SD_CMD ==> pull up
257 * SD_CLK ==> pull down
258 */
259static const u32 rtl8411_sd_pull_ctl_enable_tbl[] = {
260 RTSX_REG_PAIR(CARD_PULL_CTL1, 0xAA),
261 RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA),
262 RTSX_REG_PAIR(CARD_PULL_CTL3, 0xA9),
263 RTSX_REG_PAIR(CARD_PULL_CTL4, 0x09),
264 RTSX_REG_PAIR(CARD_PULL_CTL5, 0x09),
265 RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04),
266 0,
267};
268
269/* SD Pull Control Disable:
270 * SD_DAT[3:0] ==> pull down
271 * SD_CD ==> pull up
272 * SD_WP ==> pull down
273 * SD_CMD ==> pull down
274 * SD_CLK ==> pull down
275 */
276static const u32 rtl8411_sd_pull_ctl_disable_tbl[] = {
277 RTSX_REG_PAIR(CARD_PULL_CTL1, 0x65),
278 RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55),
279 RTSX_REG_PAIR(CARD_PULL_CTL3, 0x95),
280 RTSX_REG_PAIR(CARD_PULL_CTL4, 0x09),
281 RTSX_REG_PAIR(CARD_PULL_CTL5, 0x05),
282 RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04),
283 0,
284};
285
286/* MS Pull Control Enable:
287 * MS CD ==> pull up
288 * others ==> pull down
289 */
290static const u32 rtl8411_ms_pull_ctl_enable_tbl[] = {
291 RTSX_REG_PAIR(CARD_PULL_CTL1, 0x65),
292 RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55),
293 RTSX_REG_PAIR(CARD_PULL_CTL3, 0x95),
294 RTSX_REG_PAIR(CARD_PULL_CTL4, 0x05),
295 RTSX_REG_PAIR(CARD_PULL_CTL5, 0x05),
296 RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04),
297 0,
298};
299
300/* MS Pull Control Disable:
301 * MS CD ==> pull up
302 * others ==> pull down
303 */
304static const u32 rtl8411_ms_pull_ctl_disable_tbl[] = {
305 RTSX_REG_PAIR(CARD_PULL_CTL1, 0x65),
306 RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55),
307 RTSX_REG_PAIR(CARD_PULL_CTL3, 0x95),
308 RTSX_REG_PAIR(CARD_PULL_CTL4, 0x09),
309 RTSX_REG_PAIR(CARD_PULL_CTL5, 0x05),
310 RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04),
311 0,
312};
313
Roger Tseng9032eab2013-04-19 21:52:42 +0800314static const u32 rtl8411b_qfn64_sd_pull_ctl_enable_tbl[] = {
315 RTSX_REG_PAIR(CARD_PULL_CTL1, 0xAA),
316 RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA),
317 RTSX_REG_PAIR(CARD_PULL_CTL3, 0x09 | 0xD0),
318 RTSX_REG_PAIR(CARD_PULL_CTL4, 0x09 | 0x50),
319 RTSX_REG_PAIR(CARD_PULL_CTL5, 0x05 | 0x50),
320 RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04 | 0x11),
321 0,
322};
323
324static const u32 rtl8411b_qfn48_sd_pull_ctl_enable_tbl[] = {
325 RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA),
326 RTSX_REG_PAIR(CARD_PULL_CTL3, 0x69 | 0x90),
327 RTSX_REG_PAIR(CARD_PULL_CTL6, 0x08 | 0x11),
328 0,
329};
330
331static const u32 rtl8411b_qfn64_sd_pull_ctl_disable_tbl[] = {
332 RTSX_REG_PAIR(CARD_PULL_CTL1, 0x65),
333 RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55),
334 RTSX_REG_PAIR(CARD_PULL_CTL3, 0x05 | 0xD0),
335 RTSX_REG_PAIR(CARD_PULL_CTL4, 0x09 | 0x50),
336 RTSX_REG_PAIR(CARD_PULL_CTL5, 0x05 | 0x50),
337 RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04 | 0x11),
338 0,
339};
340
341static const u32 rtl8411b_qfn48_sd_pull_ctl_disable_tbl[] = {
342 RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55),
343 RTSX_REG_PAIR(CARD_PULL_CTL3, 0x65 | 0x90),
344 RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04 | 0x11),
345 0,
346};
347
348static const u32 rtl8411b_qfn64_ms_pull_ctl_enable_tbl[] = {
349 RTSX_REG_PAIR(CARD_PULL_CTL1, 0x65),
350 RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55),
351 RTSX_REG_PAIR(CARD_PULL_CTL3, 0x05 | 0xD0),
352 RTSX_REG_PAIR(CARD_PULL_CTL4, 0x05 | 0x50),
353 RTSX_REG_PAIR(CARD_PULL_CTL5, 0x05 | 0x50),
354 RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04 | 0x11),
355 0,
356};
357
358static const u32 rtl8411b_qfn48_ms_pull_ctl_enable_tbl[] = {
359 RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55),
360 RTSX_REG_PAIR(CARD_PULL_CTL3, 0x65 | 0x90),
361 RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04 | 0x11),
362 0,
363};
364
365static const u32 rtl8411b_qfn64_ms_pull_ctl_disable_tbl[] = {
366 RTSX_REG_PAIR(CARD_PULL_CTL1, 0x65),
367 RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55),
368 RTSX_REG_PAIR(CARD_PULL_CTL3, 0x05 | 0xD0),
369 RTSX_REG_PAIR(CARD_PULL_CTL4, 0x09 | 0x50),
370 RTSX_REG_PAIR(CARD_PULL_CTL5, 0x05 | 0x50),
371 RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04 | 0x11),
372 0,
373};
374
375static const u32 rtl8411b_qfn48_ms_pull_ctl_disable_tbl[] = {
376 RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55),
377 RTSX_REG_PAIR(CARD_PULL_CTL3, 0x65 | 0x90),
378 RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04 | 0x11),
379 0,
380};
381
Wei WANG67d16a42012-11-09 20:53:33 +0800382void rtl8411_init_params(struct rtsx_pcr *pcr)
383{
384 pcr->extra_caps = EXTRA_CAPS_SD_SDR50 | EXTRA_CAPS_SD_SDR104;
385 pcr->num_slots = 2;
386 pcr->ops = &rtl8411_pcr_ops;
387
388 pcr->ic_version = rtl8411_get_ic_version(pcr);
389 pcr->sd_pull_ctl_enable_tbl = rtl8411_sd_pull_ctl_enable_tbl;
390 pcr->sd_pull_ctl_disable_tbl = rtl8411_sd_pull_ctl_disable_tbl;
391 pcr->ms_pull_ctl_enable_tbl = rtl8411_ms_pull_ctl_enable_tbl;
392 pcr->ms_pull_ctl_disable_tbl = rtl8411_ms_pull_ctl_disable_tbl;
393}
Roger Tseng9032eab2013-04-19 21:52:42 +0800394
395void rtl8411b_init_params(struct rtsx_pcr *pcr)
396{
397 pcr->extra_caps = EXTRA_CAPS_SD_SDR50 | EXTRA_CAPS_SD_SDR104;
398 pcr->num_slots = 2;
399 pcr->ops = &rtl8411b_pcr_ops;
400
401 pcr->ic_version = rtl8411_get_ic_version(pcr);
402
403 if (rtl8411b_is_qfn48(pcr)) {
404 pcr->sd_pull_ctl_enable_tbl =
405 rtl8411b_qfn48_sd_pull_ctl_enable_tbl;
406 pcr->sd_pull_ctl_disable_tbl =
407 rtl8411b_qfn48_sd_pull_ctl_disable_tbl;
408 pcr->ms_pull_ctl_enable_tbl =
409 rtl8411b_qfn48_ms_pull_ctl_enable_tbl;
410 pcr->ms_pull_ctl_disable_tbl =
411 rtl8411b_qfn48_ms_pull_ctl_disable_tbl;
412 } else {
413 pcr->sd_pull_ctl_enable_tbl =
414 rtl8411b_qfn64_sd_pull_ctl_enable_tbl;
415 pcr->sd_pull_ctl_disable_tbl =
416 rtl8411b_qfn64_sd_pull_ctl_disable_tbl;
417 pcr->ms_pull_ctl_enable_tbl =
418 rtl8411b_qfn64_ms_pull_ctl_enable_tbl;
419 pcr->ms_pull_ctl_disable_tbl =
420 rtl8411b_qfn64_ms_pull_ctl_disable_tbl;
421 }
422}