]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/mfd/rtl8411.c
Merge branch 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab...
[karo-tx-linux.git] / drivers / mfd / rtl8411.c
1 /* 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
30 static 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
38 static 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
50 static 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
56 static 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
65 static int rtl8411_turn_on_led(struct rtsx_pcr *pcr)
66 {
67         return rtsx_pci_write_register(pcr, CARD_GPIO, 0x01, 0x00);
68 }
69
70 static int rtl8411_turn_off_led(struct rtsx_pcr *pcr)
71 {
72         return rtsx_pci_write_register(pcr, CARD_GPIO, 0x01, 0x01);
73 }
74
75 static int rtl8411_enable_auto_blink(struct rtsx_pcr *pcr)
76 {
77         return rtsx_pci_write_register(pcr, CARD_AUTO_BLINK, 0xFF, 0x0D);
78 }
79
80 static int rtl8411_disable_auto_blink(struct rtsx_pcr *pcr)
81 {
82         return rtsx_pci_write_register(pcr, CARD_AUTO_BLINK, 0x08, 0x00);
83 }
84
85 static 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
123 static 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
136 static int rtl8411_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage)
137 {
138         u8 mask, val;
139         int err;
140
141         mask = (BPP_REG_TUNED18 << BPP_TUNED18_SHIFT_8411) | BPP_PAD_MASK;
142         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;
147                 val = (BPP_ASIC_3V3 << BPP_TUNED18_SHIFT_8411) | BPP_PAD_3V3;
148         } 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;
153                 val = (BPP_ASIC_1V8 << BPP_TUNED18_SHIFT_8411) | BPP_PAD_1V8;
154         } else {
155                 return -EINVAL;
156         }
157
158         return rtsx_pci_write_register(pcr, LDO_CTL, mask, val);
159 }
160
161 static 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
212 static 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
224 static 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,
233         .switch_output_voltage = rtl8411_switch_output_voltage,
234         .cd_deglitch = rtl8411_cd_deglitch,
235         .conv_clk_and_div_n = rtl8411_conv_clk_and_div_n,
236 };
237
238 static 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
252 /* 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  */
259 static 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  */
276 static 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  */
290 static 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  */
304 static 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
314 static 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
324 static 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
331 static 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
341 static 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
348 static 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
358 static 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
365 static 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
375 static 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
382 void 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 }
394
395 void 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 }