linux/drivers/mfd/rtl8411.c
<<
>>
Prefs
   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
  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
  38static 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
  50static 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
  56static 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
  65static 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
 136static 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
 161static 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
 212static 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
 224static 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
 238static 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 */
 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
 314static 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
 382void 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
 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}
 423
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.