From e455b69ddf9b69326d0cab28d374faf3325489c9 Mon Sep 17 00:00:00 2001 From: Rui Feng Date: Wed, 29 Nov 2017 17:08:03 +0800 Subject: misc: rtsx: Move Realtek Card Reader Driver to misc Because Realtek card reader drivers are pcie and usb drivers, and they bridge mmc subsystem and memstick subsystem, they are not mfd drivers. Greg and Lee Jones had a discussion about where to put the drivers, the result is that misc is a good place for them, so I move all files to misc. If I don't move them to a right place, I can't add any patch for this driver. Signed-off-by: Rui Feng Reviewed-by: Daniel Bristot de Oliveira Acked-by: Arnd Bergmann Acked-by: Ulf Hansson Acked-by: Greg Kroah-Hartman Tested-by: Perry Yuan Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 21 - drivers/mfd/Makefile | 4 - drivers/mfd/rtl8411.c | 508 ---------------- drivers/mfd/rts5209.c | 277 --------- drivers/mfd/rts5227.c | 374 ------------ drivers/mfd/rts5229.c | 273 --------- drivers/mfd/rts5249.c | 741 ----------------------- drivers/mfd/rtsx_pcr.c | 1569 ------------------------------------------------ drivers/mfd/rtsx_pcr.h | 103 ---- drivers/mfd/rtsx_usb.c | 791 ------------------------ 10 files changed, 4661 deletions(-) delete mode 100644 drivers/mfd/rtl8411.c delete mode 100644 drivers/mfd/rts5209.c delete mode 100644 drivers/mfd/rts5227.c delete mode 100644 drivers/mfd/rts5229.c delete mode 100644 drivers/mfd/rts5249.c delete mode 100644 drivers/mfd/rtsx_pcr.c delete mode 100644 drivers/mfd/rtsx_pcr.h delete mode 100644 drivers/mfd/rtsx_usb.c (limited to 'drivers/mfd') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 1d20a800e967..1246ba1832d7 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -929,17 +929,6 @@ config MFD_RDC321X southbridge which provides access to GPIOs and Watchdog using the southbridge PCI device configuration space. -config MFD_RTSX_PCI - tristate "Realtek PCI-E card reader" - depends on PCI - select MFD_CORE - help - This supports for Realtek PCI-Express card reader including rts5209, - rts5227, rts522A, rts5229, rts5249, rts524A, rts525A, rtl8411, etc. - Realtek card reader supports access to many types of memory cards, - such as Memory Stick, Memory Stick Pro, Secure Digital and - MultiMediaCard. - config MFD_RT5033 tristate "Richtek RT5033 Power Management IC" depends on I2C @@ -953,16 +942,6 @@ config MFD_RT5033 sub-devices like charger, fuel gauge, flash LED, current source, LDO and Buck. -config MFD_RTSX_USB - tristate "Realtek USB card reader" - depends on USB - select MFD_CORE - help - Select this option to get support for Realtek USB 2.0 card readers - including RTS5129, RTS5139, RTS5179 and RTS5170. - Realtek card reader supports access to many types of memory cards, - such as Memory Stick Pro, Secure Digital and MultiMediaCard. - config MFD_RC5T583 bool "Ricoh RC5T583 Power Management system device" depends on I2C=y diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index d9474ade32e6..293e223c373d 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -19,10 +19,6 @@ obj-$(CONFIG_MFD_CROS_EC_I2C) += cros_ec_i2c.o obj-$(CONFIG_MFD_CROS_EC_SPI) += cros_ec_spi.o obj-$(CONFIG_MFD_EXYNOS_LPASS) += exynos-lpass.o -rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o -obj-$(CONFIG_MFD_RTSX_PCI) += rtsx_pci.o -obj-$(CONFIG_MFD_RTSX_USB) += rtsx_usb.o - obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o diff --git a/drivers/mfd/rtl8411.c b/drivers/mfd/rtl8411.c deleted file mode 100644 index b3ae6592014a..000000000000 --- a/drivers/mfd/rtl8411.c +++ /dev/null @@ -1,508 +0,0 @@ -/* Driver for Realtek PCI-Express card reader - * - * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - * - * Author: - * Wei WANG - * Roger Tseng - */ - -#include -#include -#include -#include - -#include "rtsx_pcr.h" - -static u8 rtl8411_get_ic_version(struct rtsx_pcr *pcr) -{ - u8 val; - - rtsx_pci_read_register(pcr, SYS_VER, &val); - return val & 0x0F; -} - -static int rtl8411b_is_qfn48(struct rtsx_pcr *pcr) -{ - u8 val = 0; - - rtsx_pci_read_register(pcr, RTL8411B_PACKAGE_MODE, &val); - - if (val & 0x2) - return 1; - else - return 0; -} - -static void rtl8411_fetch_vendor_settings(struct rtsx_pcr *pcr) -{ - u32 reg1 = 0; - u8 reg3 = 0; - - rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, ®1); - pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg1); - - if (!rtsx_vendor_setting_valid(reg1)) - return; - - pcr->aspm_en = rtsx_reg_to_aspm(reg1); - pcr->sd30_drive_sel_1v8 = - map_sd_drive(rtsx_reg_to_sd30_drive_sel_1v8(reg1)); - pcr->card_drive_sel &= 0x3F; - pcr->card_drive_sel |= rtsx_reg_to_card_drive_sel(reg1); - - rtsx_pci_read_config_byte(pcr, PCR_SETTING_REG3, ®3); - pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG3, reg3); - pcr->sd30_drive_sel_3v3 = rtl8411_reg_to_sd30_drive_sel_3v3(reg3); -} - -static void rtl8411b_fetch_vendor_settings(struct rtsx_pcr *pcr) -{ - u32 reg = 0; - - rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, ®); - pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg); - - if (!rtsx_vendor_setting_valid(reg)) - return; - - pcr->aspm_en = rtsx_reg_to_aspm(reg); - pcr->sd30_drive_sel_1v8 = - map_sd_drive(rtsx_reg_to_sd30_drive_sel_1v8(reg)); - pcr->sd30_drive_sel_3v3 = - map_sd_drive(rtl8411b_reg_to_sd30_drive_sel_3v3(reg)); -} - -static void rtl8411_force_power_down(struct rtsx_pcr *pcr, u8 pm_state) -{ - rtsx_pci_write_register(pcr, FPDCTL, 0x07, 0x07); -} - -static int rtl8411_extra_init_hw(struct rtsx_pcr *pcr) -{ - rtsx_pci_init_cmd(pcr); - - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_DRIVE_SEL, - 0xFF, pcr->sd30_drive_sel_3v3); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CD_PAD_CTL, - CD_DISABLE_MASK | CD_AUTO_DISABLE, CD_ENABLE); - - return rtsx_pci_send_cmd(pcr, 100); -} - -static int rtl8411b_extra_init_hw(struct rtsx_pcr *pcr) -{ - rtsx_pci_init_cmd(pcr); - - if (rtl8411b_is_qfn48(pcr)) - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, - CARD_PULL_CTL3, 0xFF, 0xF5); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_DRIVE_SEL, - 0xFF, pcr->sd30_drive_sel_3v3); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CD_PAD_CTL, - CD_DISABLE_MASK | CD_AUTO_DISABLE, CD_ENABLE); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, FUNC_FORCE_CTL, - 0x06, 0x00); - - return rtsx_pci_send_cmd(pcr, 100); -} - -static int rtl8411_turn_on_led(struct rtsx_pcr *pcr) -{ - return rtsx_pci_write_register(pcr, CARD_GPIO, 0x01, 0x00); -} - -static int rtl8411_turn_off_led(struct rtsx_pcr *pcr) -{ - return rtsx_pci_write_register(pcr, CARD_GPIO, 0x01, 0x01); -} - -static int rtl8411_enable_auto_blink(struct rtsx_pcr *pcr) -{ - return rtsx_pci_write_register(pcr, CARD_AUTO_BLINK, 0xFF, 0x0D); -} - -static int rtl8411_disable_auto_blink(struct rtsx_pcr *pcr) -{ - return rtsx_pci_write_register(pcr, CARD_AUTO_BLINK, 0x08, 0x00); -} - -static int rtl8411_card_power_on(struct rtsx_pcr *pcr, int card) -{ - int err; - - rtsx_pci_init_cmd(pcr); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, - BPP_POWER_MASK, BPP_POWER_5_PERCENT_ON); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_CTL, - BPP_LDO_POWB, BPP_LDO_SUSPEND); - err = rtsx_pci_send_cmd(pcr, 100); - if (err < 0) - return err; - - /* To avoid too large in-rush current */ - udelay(150); - - err = rtsx_pci_write_register(pcr, CARD_PWR_CTL, - BPP_POWER_MASK, BPP_POWER_10_PERCENT_ON); - if (err < 0) - return err; - - udelay(150); - - err = rtsx_pci_write_register(pcr, CARD_PWR_CTL, - BPP_POWER_MASK, BPP_POWER_15_PERCENT_ON); - if (err < 0) - return err; - - udelay(150); - - err = rtsx_pci_write_register(pcr, CARD_PWR_CTL, - BPP_POWER_MASK, BPP_POWER_ON); - if (err < 0) - return err; - - return rtsx_pci_write_register(pcr, LDO_CTL, BPP_LDO_POWB, BPP_LDO_ON); -} - -static int rtl8411_card_power_off(struct rtsx_pcr *pcr, int card) -{ - int err; - - err = rtsx_pci_write_register(pcr, CARD_PWR_CTL, - BPP_POWER_MASK, BPP_POWER_OFF); - if (err < 0) - return err; - - return rtsx_pci_write_register(pcr, LDO_CTL, - BPP_LDO_POWB, BPP_LDO_SUSPEND); -} - -static int rtl8411_do_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage, - int bpp_tuned18_shift, int bpp_asic_1v8) -{ - u8 mask, val; - int err; - - mask = (BPP_REG_TUNED18 << bpp_tuned18_shift) | BPP_PAD_MASK; - if (voltage == OUTPUT_3V3) { - err = rtsx_pci_write_register(pcr, - SD30_DRIVE_SEL, 0x07, pcr->sd30_drive_sel_3v3); - if (err < 0) - return err; - val = (BPP_ASIC_3V3 << bpp_tuned18_shift) | BPP_PAD_3V3; - } else if (voltage == OUTPUT_1V8) { - err = rtsx_pci_write_register(pcr, - SD30_DRIVE_SEL, 0x07, pcr->sd30_drive_sel_1v8); - if (err < 0) - return err; - val = (bpp_asic_1v8 << bpp_tuned18_shift) | BPP_PAD_1V8; - } else { - return -EINVAL; - } - - return rtsx_pci_write_register(pcr, LDO_CTL, mask, val); -} - -static int rtl8411_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) -{ - return rtl8411_do_switch_output_voltage(pcr, voltage, - BPP_TUNED18_SHIFT_8411, BPP_ASIC_1V8); -} - -static int rtl8402_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) -{ - return rtl8411_do_switch_output_voltage(pcr, voltage, - BPP_TUNED18_SHIFT_8402, BPP_ASIC_2V0); -} - -static unsigned int rtl8411_cd_deglitch(struct rtsx_pcr *pcr) -{ - unsigned int card_exist; - - card_exist = rtsx_pci_readl(pcr, RTSX_BIPR); - card_exist &= CARD_EXIST; - if (!card_exist) { - /* Enable card CD */ - rtsx_pci_write_register(pcr, CD_PAD_CTL, - CD_DISABLE_MASK, CD_ENABLE); - /* Enable card interrupt */ - rtsx_pci_write_register(pcr, EFUSE_CONTENT, 0xe0, 0x00); - return 0; - } - - if (hweight32(card_exist) > 1) { - rtsx_pci_write_register(pcr, CARD_PWR_CTL, - BPP_POWER_MASK, BPP_POWER_5_PERCENT_ON); - msleep(100); - - card_exist = rtsx_pci_readl(pcr, RTSX_BIPR); - if (card_exist & MS_EXIST) - card_exist = MS_EXIST; - else if (card_exist & SD_EXIST) - card_exist = SD_EXIST; - else - card_exist = 0; - - rtsx_pci_write_register(pcr, CARD_PWR_CTL, - BPP_POWER_MASK, BPP_POWER_OFF); - - pcr_dbg(pcr, "After CD deglitch, card_exist = 0x%x\n", - card_exist); - } - - if (card_exist & MS_EXIST) { - /* Disable SD interrupt */ - rtsx_pci_write_register(pcr, EFUSE_CONTENT, 0xe0, 0x40); - rtsx_pci_write_register(pcr, CD_PAD_CTL, - CD_DISABLE_MASK, MS_CD_EN_ONLY); - } else if (card_exist & SD_EXIST) { - /* Disable MS interrupt */ - rtsx_pci_write_register(pcr, EFUSE_CONTENT, 0xe0, 0x80); - rtsx_pci_write_register(pcr, CD_PAD_CTL, - CD_DISABLE_MASK, SD_CD_EN_ONLY); - } - - return card_exist; -} - -static int rtl8411_conv_clk_and_div_n(int input, int dir) -{ - int output; - - if (dir == CLK_TO_DIV_N) - output = input * 4 / 5 - 2; - else - output = (input + 2) * 5 / 4; - - return output; -} - -static const struct pcr_ops rtl8411_pcr_ops = { - .fetch_vendor_settings = rtl8411_fetch_vendor_settings, - .extra_init_hw = rtl8411_extra_init_hw, - .optimize_phy = NULL, - .turn_on_led = rtl8411_turn_on_led, - .turn_off_led = rtl8411_turn_off_led, - .enable_auto_blink = rtl8411_enable_auto_blink, - .disable_auto_blink = rtl8411_disable_auto_blink, - .card_power_on = rtl8411_card_power_on, - .card_power_off = rtl8411_card_power_off, - .switch_output_voltage = rtl8411_switch_output_voltage, - .cd_deglitch = rtl8411_cd_deglitch, - .conv_clk_and_div_n = rtl8411_conv_clk_and_div_n, - .force_power_down = rtl8411_force_power_down, -}; - -static const struct pcr_ops rtl8402_pcr_ops = { - .fetch_vendor_settings = rtl8411_fetch_vendor_settings, - .extra_init_hw = rtl8411_extra_init_hw, - .optimize_phy = NULL, - .turn_on_led = rtl8411_turn_on_led, - .turn_off_led = rtl8411_turn_off_led, - .enable_auto_blink = rtl8411_enable_auto_blink, - .disable_auto_blink = rtl8411_disable_auto_blink, - .card_power_on = rtl8411_card_power_on, - .card_power_off = rtl8411_card_power_off, - .switch_output_voltage = rtl8402_switch_output_voltage, - .cd_deglitch = rtl8411_cd_deglitch, - .conv_clk_and_div_n = rtl8411_conv_clk_and_div_n, - .force_power_down = rtl8411_force_power_down, -}; - -static const struct pcr_ops rtl8411b_pcr_ops = { - .fetch_vendor_settings = rtl8411b_fetch_vendor_settings, - .extra_init_hw = rtl8411b_extra_init_hw, - .optimize_phy = NULL, - .turn_on_led = rtl8411_turn_on_led, - .turn_off_led = rtl8411_turn_off_led, - .enable_auto_blink = rtl8411_enable_auto_blink, - .disable_auto_blink = rtl8411_disable_auto_blink, - .card_power_on = rtl8411_card_power_on, - .card_power_off = rtl8411_card_power_off, - .switch_output_voltage = rtl8411_switch_output_voltage, - .cd_deglitch = rtl8411_cd_deglitch, - .conv_clk_and_div_n = rtl8411_conv_clk_and_div_n, - .force_power_down = rtl8411_force_power_down, -}; - -/* SD Pull Control Enable: - * SD_DAT[3:0] ==> pull up - * SD_CD ==> pull up - * SD_WP ==> pull up - * SD_CMD ==> pull up - * SD_CLK ==> pull down - */ -static const u32 rtl8411_sd_pull_ctl_enable_tbl[] = { - RTSX_REG_PAIR(CARD_PULL_CTL1, 0xAA), - RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA), - RTSX_REG_PAIR(CARD_PULL_CTL3, 0xA9), - RTSX_REG_PAIR(CARD_PULL_CTL4, 0x09), - RTSX_REG_PAIR(CARD_PULL_CTL5, 0x09), - RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04), - 0, -}; - -/* SD Pull Control Disable: - * SD_DAT[3:0] ==> pull down - * SD_CD ==> pull up - * SD_WP ==> pull down - * SD_CMD ==> pull down - * SD_CLK ==> pull down - */ -static const u32 rtl8411_sd_pull_ctl_disable_tbl[] = { - RTSX_REG_PAIR(CARD_PULL_CTL1, 0x65), - RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55), - RTSX_REG_PAIR(CARD_PULL_CTL3, 0x95), - RTSX_REG_PAIR(CARD_PULL_CTL4, 0x09), - RTSX_REG_PAIR(CARD_PULL_CTL5, 0x05), - RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04), - 0, -}; - -/* MS Pull Control Enable: - * MS CD ==> pull up - * others ==> pull down - */ -static const u32 rtl8411_ms_pull_ctl_enable_tbl[] = { - RTSX_REG_PAIR(CARD_PULL_CTL1, 0x65), - RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55), - RTSX_REG_PAIR(CARD_PULL_CTL3, 0x95), - RTSX_REG_PAIR(CARD_PULL_CTL4, 0x05), - RTSX_REG_PAIR(CARD_PULL_CTL5, 0x05), - RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04), - 0, -}; - -/* MS Pull Control Disable: - * MS CD ==> pull up - * others ==> pull down - */ -static const u32 rtl8411_ms_pull_ctl_disable_tbl[] = { - RTSX_REG_PAIR(CARD_PULL_CTL1, 0x65), - RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55), - RTSX_REG_PAIR(CARD_PULL_CTL3, 0x95), - RTSX_REG_PAIR(CARD_PULL_CTL4, 0x09), - RTSX_REG_PAIR(CARD_PULL_CTL5, 0x05), - RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04), - 0, -}; - -static const u32 rtl8411b_qfn64_sd_pull_ctl_enable_tbl[] = { - RTSX_REG_PAIR(CARD_PULL_CTL1, 0xAA), - RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA), - RTSX_REG_PAIR(CARD_PULL_CTL3, 0x09 | 0xD0), - RTSX_REG_PAIR(CARD_PULL_CTL4, 0x09 | 0x50), - RTSX_REG_PAIR(CARD_PULL_CTL5, 0x05 | 0x50), - RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04 | 0x11), - 0, -}; - -static const u32 rtl8411b_qfn48_sd_pull_ctl_enable_tbl[] = { - RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA), - RTSX_REG_PAIR(CARD_PULL_CTL3, 0x69 | 0x90), - RTSX_REG_PAIR(CARD_PULL_CTL6, 0x08 | 0x11), - 0, -}; - -static const u32 rtl8411b_qfn64_sd_pull_ctl_disable_tbl[] = { - RTSX_REG_PAIR(CARD_PULL_CTL1, 0x65), - RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55), - RTSX_REG_PAIR(CARD_PULL_CTL3, 0x05 | 0xD0), - RTSX_REG_PAIR(CARD_PULL_CTL4, 0x09 | 0x50), - RTSX_REG_PAIR(CARD_PULL_CTL5, 0x05 | 0x50), - RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04 | 0x11), - 0, -}; - -static const u32 rtl8411b_qfn48_sd_pull_ctl_disable_tbl[] = { - RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55), - RTSX_REG_PAIR(CARD_PULL_CTL3, 0x65 | 0x90), - RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04 | 0x11), - 0, -}; - -static const u32 rtl8411b_qfn64_ms_pull_ctl_enable_tbl[] = { - RTSX_REG_PAIR(CARD_PULL_CTL1, 0x65), - RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55), - RTSX_REG_PAIR(CARD_PULL_CTL3, 0x05 | 0xD0), - RTSX_REG_PAIR(CARD_PULL_CTL4, 0x05 | 0x50), - RTSX_REG_PAIR(CARD_PULL_CTL5, 0x05 | 0x50), - RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04 | 0x11), - 0, -}; - -static const u32 rtl8411b_qfn48_ms_pull_ctl_enable_tbl[] = { - RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55), - RTSX_REG_PAIR(CARD_PULL_CTL3, 0x65 | 0x90), - RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04 | 0x11), - 0, -}; - -static const u32 rtl8411b_qfn64_ms_pull_ctl_disable_tbl[] = { - RTSX_REG_PAIR(CARD_PULL_CTL1, 0x65), - RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55), - RTSX_REG_PAIR(CARD_PULL_CTL3, 0x05 | 0xD0), - RTSX_REG_PAIR(CARD_PULL_CTL4, 0x09 | 0x50), - RTSX_REG_PAIR(CARD_PULL_CTL5, 0x05 | 0x50), - RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04 | 0x11), - 0, -}; - -static const u32 rtl8411b_qfn48_ms_pull_ctl_disable_tbl[] = { - RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55), - RTSX_REG_PAIR(CARD_PULL_CTL3, 0x65 | 0x90), - RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04 | 0x11), - 0, -}; - -static void rtl8411_init_common_params(struct rtsx_pcr *pcr) -{ - pcr->extra_caps = EXTRA_CAPS_SD_SDR50 | EXTRA_CAPS_SD_SDR104; - pcr->num_slots = 2; - pcr->flags = 0; - pcr->card_drive_sel = RTL8411_CARD_DRIVE_DEFAULT; - pcr->sd30_drive_sel_1v8 = DRIVER_TYPE_B; - pcr->sd30_drive_sel_3v3 = DRIVER_TYPE_D; - pcr->aspm_en = ASPM_L1_EN; - pcr->tx_initial_phase = SET_CLOCK_PHASE(23, 7, 14); - pcr->rx_initial_phase = SET_CLOCK_PHASE(4, 3, 10); - pcr->ic_version = rtl8411_get_ic_version(pcr); -} - -void rtl8411_init_params(struct rtsx_pcr *pcr) -{ - rtl8411_init_common_params(pcr); - pcr->ops = &rtl8411_pcr_ops; - set_pull_ctrl_tables(pcr, rtl8411); -} - -void rtl8411b_init_params(struct rtsx_pcr *pcr) -{ - rtl8411_init_common_params(pcr); - pcr->ops = &rtl8411b_pcr_ops; - if (rtl8411b_is_qfn48(pcr)) - set_pull_ctrl_tables(pcr, rtl8411b_qfn48); - else - set_pull_ctrl_tables(pcr, rtl8411b_qfn64); -} - -void rtl8402_init_params(struct rtsx_pcr *pcr) -{ - rtl8411_init_common_params(pcr); - pcr->ops = &rtl8402_pcr_ops; - set_pull_ctrl_tables(pcr, rtl8411); -} diff --git a/drivers/mfd/rts5209.c b/drivers/mfd/rts5209.c deleted file mode 100644 index b95beecf767f..000000000000 --- a/drivers/mfd/rts5209.c +++ /dev/null @@ -1,277 +0,0 @@ -/* Driver for Realtek PCI-Express card reader - * - * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - * - * Author: - * Wei WANG - */ - -#include -#include -#include - -#include "rtsx_pcr.h" - -static u8 rts5209_get_ic_version(struct rtsx_pcr *pcr) -{ - u8 val; - - val = rtsx_pci_readb(pcr, 0x1C); - return val & 0x0F; -} - -static void rts5209_fetch_vendor_settings(struct rtsx_pcr *pcr) -{ - u32 reg; - - rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, ®); - pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg); - - if (rts5209_vendor_setting1_valid(reg)) { - if (rts5209_reg_check_ms_pmos(reg)) - pcr->flags |= PCR_MS_PMOS; - pcr->aspm_en = rts5209_reg_to_aspm(reg); - } - - rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG2, ®); - pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG2, reg); - - if (rts5209_vendor_setting2_valid(reg)) { - pcr->sd30_drive_sel_1v8 = - rts5209_reg_to_sd30_drive_sel_1v8(reg); - pcr->sd30_drive_sel_3v3 = - rts5209_reg_to_sd30_drive_sel_3v3(reg); - pcr->card_drive_sel = rts5209_reg_to_card_drive_sel(reg); - } -} - -static void rts5209_force_power_down(struct rtsx_pcr *pcr, u8 pm_state) -{ - rtsx_pci_write_register(pcr, FPDCTL, 0x07, 0x07); -} - -static int rts5209_extra_init_hw(struct rtsx_pcr *pcr) -{ - rtsx_pci_init_cmd(pcr); - - /* Turn off LED */ - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_GPIO, 0xFF, 0x03); - /* Reset ASPM state to default value */ - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, ASPM_FORCE_CTL, 0x3F, 0); - /* Force CLKREQ# PIN to drive 0 to request clock */ - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PETXCFG, 0x08, 0x08); - /* Configure GPIO as output */ - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_GPIO_DIR, 0xFF, 0x03); - /* Configure driving */ - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_DRIVE_SEL, - 0xFF, pcr->sd30_drive_sel_3v3); - - return rtsx_pci_send_cmd(pcr, 100); -} - -static int rts5209_optimize_phy(struct rtsx_pcr *pcr) -{ - return rtsx_pci_write_phy_register(pcr, 0x00, 0xB966); -} - -static int rts5209_turn_on_led(struct rtsx_pcr *pcr) -{ - return rtsx_pci_write_register(pcr, CARD_GPIO, 0x01, 0x00); -} - -static int rts5209_turn_off_led(struct rtsx_pcr *pcr) -{ - return rtsx_pci_write_register(pcr, CARD_GPIO, 0x01, 0x01); -} - -static int rts5209_enable_auto_blink(struct rtsx_pcr *pcr) -{ - return rtsx_pci_write_register(pcr, CARD_AUTO_BLINK, 0xFF, 0x0D); -} - -static int rts5209_disable_auto_blink(struct rtsx_pcr *pcr) -{ - return rtsx_pci_write_register(pcr, CARD_AUTO_BLINK, 0x08, 0x00); -} - -static int rts5209_card_power_on(struct rtsx_pcr *pcr, int card) -{ - int err; - u8 pwr_mask, partial_pwr_on, pwr_on; - - pwr_mask = SD_POWER_MASK; - partial_pwr_on = SD_PARTIAL_POWER_ON; - pwr_on = SD_POWER_ON; - - if ((pcr->flags & PCR_MS_PMOS) && (card == RTSX_MS_CARD)) { - pwr_mask = MS_POWER_MASK; - partial_pwr_on = MS_PARTIAL_POWER_ON; - pwr_on = MS_POWER_ON; - } - - rtsx_pci_init_cmd(pcr); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, - pwr_mask, partial_pwr_on); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, - LDO3318_PWR_MASK, 0x04); - err = rtsx_pci_send_cmd(pcr, 100); - if (err < 0) - return err; - - /* To avoid too large in-rush current */ - udelay(150); - - rtsx_pci_init_cmd(pcr); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, pwr_mask, pwr_on); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, - LDO3318_PWR_MASK, 0x00); - return rtsx_pci_send_cmd(pcr, 100); -} - -static int rts5209_card_power_off(struct rtsx_pcr *pcr, int card) -{ - u8 pwr_mask, pwr_off; - - pwr_mask = SD_POWER_MASK; - pwr_off = SD_POWER_OFF; - - if ((pcr->flags & PCR_MS_PMOS) && (card == RTSX_MS_CARD)) { - pwr_mask = MS_POWER_MASK; - pwr_off = MS_POWER_OFF; - } - - rtsx_pci_init_cmd(pcr); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, - pwr_mask | PMOS_STRG_MASK, pwr_off | PMOS_STRG_400mA); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, - LDO3318_PWR_MASK, 0x06); - return rtsx_pci_send_cmd(pcr, 100); -} - -static int rts5209_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) -{ - int err; - - if (voltage == OUTPUT_3V3) { - err = rtsx_pci_write_register(pcr, - SD30_DRIVE_SEL, 0x07, pcr->sd30_drive_sel_3v3); - if (err < 0) - return err; - err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4FC0 | 0x24); - if (err < 0) - return err; - } else if (voltage == OUTPUT_1V8) { - err = rtsx_pci_write_register(pcr, - SD30_DRIVE_SEL, 0x07, pcr->sd30_drive_sel_1v8); - if (err < 0) - return err; - err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4C40 | 0x24); - if (err < 0) - return err; - } else { - return -EINVAL; - } - - return 0; -} - -static const struct pcr_ops rts5209_pcr_ops = { - .fetch_vendor_settings = rts5209_fetch_vendor_settings, - .extra_init_hw = rts5209_extra_init_hw, - .optimize_phy = rts5209_optimize_phy, - .turn_on_led = rts5209_turn_on_led, - .turn_off_led = rts5209_turn_off_led, - .enable_auto_blink = rts5209_enable_auto_blink, - .disable_auto_blink = rts5209_disable_auto_blink, - .card_power_on = rts5209_card_power_on, - .card_power_off = rts5209_card_power_off, - .switch_output_voltage = rts5209_switch_output_voltage, - .cd_deglitch = NULL, - .conv_clk_and_div_n = NULL, - .force_power_down = rts5209_force_power_down, -}; - -/* SD Pull Control Enable: - * SD_DAT[3:0] ==> pull up - * SD_CD ==> pull up - * SD_WP ==> pull up - * SD_CMD ==> pull up - * SD_CLK ==> pull down - */ -static const u32 rts5209_sd_pull_ctl_enable_tbl[] = { - RTSX_REG_PAIR(CARD_PULL_CTL1, 0xAA), - RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA), - RTSX_REG_PAIR(CARD_PULL_CTL3, 0xE9), - 0, -}; - -/* SD Pull Control Disable: - * SD_DAT[3:0] ==> pull down - * SD_CD ==> pull up - * SD_WP ==> pull down - * SD_CMD ==> pull down - * SD_CLK ==> pull down - */ -static const u32 rts5209_sd_pull_ctl_disable_tbl[] = { - RTSX_REG_PAIR(CARD_PULL_CTL1, 0x55), - RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55), - RTSX_REG_PAIR(CARD_PULL_CTL3, 0xD5), - 0, -}; - -/* MS Pull Control Enable: - * MS CD ==> pull up - * others ==> pull down - */ -static const u32 rts5209_ms_pull_ctl_enable_tbl[] = { - RTSX_REG_PAIR(CARD_PULL_CTL4, 0x55), - RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55), - RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15), - 0, -}; - -/* MS Pull Control Disable: - * MS CD ==> pull up - * others ==> pull down - */ -static const u32 rts5209_ms_pull_ctl_disable_tbl[] = { - RTSX_REG_PAIR(CARD_PULL_CTL4, 0x55), - RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55), - RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15), - 0, -}; - -void rts5209_init_params(struct rtsx_pcr *pcr) -{ - pcr->extra_caps = EXTRA_CAPS_SD_SDR50 | - EXTRA_CAPS_SD_SDR104 | EXTRA_CAPS_MMC_8BIT; - pcr->num_slots = 2; - pcr->ops = &rts5209_pcr_ops; - - pcr->flags = 0; - pcr->card_drive_sel = RTS5209_CARD_DRIVE_DEFAULT; - pcr->sd30_drive_sel_1v8 = DRIVER_TYPE_B; - pcr->sd30_drive_sel_3v3 = DRIVER_TYPE_D; - pcr->aspm_en = ASPM_L1_EN; - pcr->tx_initial_phase = SET_CLOCK_PHASE(27, 27, 16); - pcr->rx_initial_phase = SET_CLOCK_PHASE(24, 6, 5); - - pcr->ic_version = rts5209_get_ic_version(pcr); - pcr->sd_pull_ctl_enable_tbl = rts5209_sd_pull_ctl_enable_tbl; - pcr->sd_pull_ctl_disable_tbl = rts5209_sd_pull_ctl_disable_tbl; - pcr->ms_pull_ctl_enable_tbl = rts5209_ms_pull_ctl_enable_tbl; - pcr->ms_pull_ctl_disable_tbl = rts5209_ms_pull_ctl_disable_tbl; -} diff --git a/drivers/mfd/rts5227.c b/drivers/mfd/rts5227.c deleted file mode 100644 index ff296a4bf3d2..000000000000 --- a/drivers/mfd/rts5227.c +++ /dev/null @@ -1,374 +0,0 @@ -/* Driver for Realtek PCI-Express card reader - * - * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - * - * Author: - * Wei WANG - * Roger Tseng - */ - -#include -#include -#include - -#include "rtsx_pcr.h" - -static u8 rts5227_get_ic_version(struct rtsx_pcr *pcr) -{ - u8 val; - - rtsx_pci_read_register(pcr, DUMMY_REG_RESET_0, &val); - return val & 0x0F; -} - -static void rts5227_fill_driving(struct rtsx_pcr *pcr, u8 voltage) -{ - u8 driving_3v3[4][3] = { - {0x13, 0x13, 0x13}, - {0x96, 0x96, 0x96}, - {0x7F, 0x7F, 0x7F}, - {0x96, 0x96, 0x96}, - }; - u8 driving_1v8[4][3] = { - {0x99, 0x99, 0x99}, - {0xAA, 0xAA, 0xAA}, - {0xFE, 0xFE, 0xFE}, - {0xB3, 0xB3, 0xB3}, - }; - u8 (*driving)[3], drive_sel; - - if (voltage == OUTPUT_3V3) { - driving = driving_3v3; - drive_sel = pcr->sd30_drive_sel_3v3; - } else { - driving = driving_1v8; - drive_sel = pcr->sd30_drive_sel_1v8; - } - - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_CLK_DRIVE_SEL, - 0xFF, driving[drive_sel][0]); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_CMD_DRIVE_SEL, - 0xFF, driving[drive_sel][1]); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_DAT_DRIVE_SEL, - 0xFF, driving[drive_sel][2]); -} - -static void rts5227_fetch_vendor_settings(struct rtsx_pcr *pcr) -{ - u32 reg; - - rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, ®); - pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg); - - if (!rtsx_vendor_setting_valid(reg)) - return; - - pcr->aspm_en = rtsx_reg_to_aspm(reg); - pcr->sd30_drive_sel_1v8 = rtsx_reg_to_sd30_drive_sel_1v8(reg); - pcr->card_drive_sel &= 0x3F; - pcr->card_drive_sel |= rtsx_reg_to_card_drive_sel(reg); - - rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG2, ®); - pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG2, reg); - pcr->sd30_drive_sel_3v3 = rtsx_reg_to_sd30_drive_sel_3v3(reg); - if (rtsx_reg_check_reverse_socket(reg)) - pcr->flags |= PCR_REVERSE_SOCKET; -} - -static void rts5227_force_power_down(struct rtsx_pcr *pcr, u8 pm_state) -{ - /* Set relink_time to 0 */ - rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 1, 0xFF, 0); - rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 2, 0xFF, 0); - rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 3, 0x01, 0); - - if (pm_state == HOST_ENTER_S3) - rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, 0x10, 0x10); - - rtsx_pci_write_register(pcr, FPDCTL, 0x03, 0x03); -} - -static int rts5227_extra_init_hw(struct rtsx_pcr *pcr) -{ - u16 cap; - - rtsx_pci_init_cmd(pcr); - - /* Configure GPIO as output */ - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, GPIO_CTL, 0x02, 0x02); - /* Reset ASPM state to default value */ - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, ASPM_FORCE_CTL, 0x3F, 0); - /* Switch LDO3318 source from DV33 to card_3v3 */ - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_PWR_SEL, 0x03, 0x00); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_PWR_SEL, 0x03, 0x01); - /* LED shine disabled, set initial shine cycle period */ - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, OLT_LED_CTL, 0x0F, 0x02); - /* Configure LTR */ - pcie_capability_read_word(pcr->pci, PCI_EXP_DEVCTL2, &cap); - if (cap & PCI_EXP_DEVCTL2_LTR_EN) - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LTR_CTL, 0xFF, 0xA3); - /* Configure OBFF */ - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, OBFF_CFG, 0x03, 0x03); - /* Configure driving */ - rts5227_fill_driving(pcr, OUTPUT_3V3); - /* Configure force_clock_req */ - if (pcr->flags & PCR_REVERSE_SOCKET) - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PETXCFG, 0xB8, 0xB8); - else - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PETXCFG, 0xB8, 0x88); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, pcr->reg_pm_ctrl3, 0x10, 0x00); - - return rtsx_pci_send_cmd(pcr, 100); -} - -static int rts5227_optimize_phy(struct rtsx_pcr *pcr) -{ - int err; - - err = rtsx_pci_write_register(pcr, PM_CTRL3, D3_DELINK_MODE_EN, 0x00); - if (err < 0) - return err; - - /* Optimize RX sensitivity */ - return rtsx_pci_write_phy_register(pcr, 0x00, 0xBA42); -} - -static int rts5227_turn_on_led(struct rtsx_pcr *pcr) -{ - return rtsx_pci_write_register(pcr, GPIO_CTL, 0x02, 0x02); -} - -static int rts5227_turn_off_led(struct rtsx_pcr *pcr) -{ - return rtsx_pci_write_register(pcr, GPIO_CTL, 0x02, 0x00); -} - -static int rts5227_enable_auto_blink(struct rtsx_pcr *pcr) -{ - return rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x08, 0x08); -} - -static int rts5227_disable_auto_blink(struct rtsx_pcr *pcr) -{ - return rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x08, 0x00); -} - -static int rts5227_card_power_on(struct rtsx_pcr *pcr, int card) -{ - int err; - - rtsx_pci_init_cmd(pcr); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, - SD_POWER_MASK, SD_PARTIAL_POWER_ON); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, - LDO3318_PWR_MASK, 0x02); - err = rtsx_pci_send_cmd(pcr, 100); - if (err < 0) - return err; - - /* To avoid too large in-rush current */ - udelay(150); - - rtsx_pci_init_cmd(pcr); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, - SD_POWER_MASK, SD_POWER_ON); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, - LDO3318_PWR_MASK, 0x06); - return rtsx_pci_send_cmd(pcr, 100); -} - -static int rts5227_card_power_off(struct rtsx_pcr *pcr, int card) -{ - rtsx_pci_init_cmd(pcr); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, - SD_POWER_MASK | PMOS_STRG_MASK, - SD_POWER_OFF | PMOS_STRG_400mA); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, - LDO3318_PWR_MASK, 0X00); - return rtsx_pci_send_cmd(pcr, 100); -} - -static int rts5227_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) -{ - int err; - - if (voltage == OUTPUT_3V3) { - err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4FC0 | 0x24); - if (err < 0) - return err; - } else if (voltage == OUTPUT_1V8) { - err = rtsx_pci_write_phy_register(pcr, 0x11, 0x3C02); - if (err < 0) - return err; - err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4C80 | 0x24); - if (err < 0) - return err; - } else { - return -EINVAL; - } - - /* set pad drive */ - rtsx_pci_init_cmd(pcr); - rts5227_fill_driving(pcr, voltage); - return rtsx_pci_send_cmd(pcr, 100); -} - -static const struct pcr_ops rts5227_pcr_ops = { - .fetch_vendor_settings = rts5227_fetch_vendor_settings, - .extra_init_hw = rts5227_extra_init_hw, - .optimize_phy = rts5227_optimize_phy, - .turn_on_led = rts5227_turn_on_led, - .turn_off_led = rts5227_turn_off_led, - .enable_auto_blink = rts5227_enable_auto_blink, - .disable_auto_blink = rts5227_disable_auto_blink, - .card_power_on = rts5227_card_power_on, - .card_power_off = rts5227_card_power_off, - .switch_output_voltage = rts5227_switch_output_voltage, - .cd_deglitch = NULL, - .conv_clk_and_div_n = NULL, - .force_power_down = rts5227_force_power_down, -}; - -/* SD Pull Control Enable: - * SD_DAT[3:0] ==> pull up - * SD_CD ==> pull up - * SD_WP ==> pull up - * SD_CMD ==> pull up - * SD_CLK ==> pull down - */ -static const u32 rts5227_sd_pull_ctl_enable_tbl[] = { - RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA), - RTSX_REG_PAIR(CARD_PULL_CTL3, 0xE9), - 0, -}; - -/* SD Pull Control Disable: - * SD_DAT[3:0] ==> pull down - * SD_CD ==> pull up - * SD_WP ==> pull down - * SD_CMD ==> pull down - * SD_CLK ==> pull down - */ -static const u32 rts5227_sd_pull_ctl_disable_tbl[] = { - RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55), - RTSX_REG_PAIR(CARD_PULL_CTL3, 0xD5), - 0, -}; - -/* MS Pull Control Enable: - * MS CD ==> pull up - * others ==> pull down - */ -static const u32 rts5227_ms_pull_ctl_enable_tbl[] = { - RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55), - RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15), - 0, -}; - -/* MS Pull Control Disable: - * MS CD ==> pull up - * others ==> pull down - */ -static const u32 rts5227_ms_pull_ctl_disable_tbl[] = { - RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55), - RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15), - 0, -}; - -void rts5227_init_params(struct rtsx_pcr *pcr) -{ - pcr->extra_caps = EXTRA_CAPS_SD_SDR50 | EXTRA_CAPS_SD_SDR104; - pcr->num_slots = 2; - pcr->ops = &rts5227_pcr_ops; - - pcr->flags = 0; - pcr->card_drive_sel = RTSX_CARD_DRIVE_DEFAULT; - pcr->sd30_drive_sel_1v8 = CFG_DRIVER_TYPE_B; - pcr->sd30_drive_sel_3v3 = CFG_DRIVER_TYPE_B; - pcr->aspm_en = ASPM_L1_EN; - pcr->tx_initial_phase = SET_CLOCK_PHASE(27, 27, 15); - pcr->rx_initial_phase = SET_CLOCK_PHASE(30, 7, 7); - - pcr->ic_version = rts5227_get_ic_version(pcr); - pcr->sd_pull_ctl_enable_tbl = rts5227_sd_pull_ctl_enable_tbl; - pcr->sd_pull_ctl_disable_tbl = rts5227_sd_pull_ctl_disable_tbl; - pcr->ms_pull_ctl_enable_tbl = rts5227_ms_pull_ctl_enable_tbl; - pcr->ms_pull_ctl_disable_tbl = rts5227_ms_pull_ctl_disable_tbl; - - pcr->reg_pm_ctrl3 = PM_CTRL3; -} - -static int rts522a_optimize_phy(struct rtsx_pcr *pcr) -{ - int err; - - err = rtsx_pci_write_register(pcr, RTS522A_PM_CTRL3, D3_DELINK_MODE_EN, - 0x00); - if (err < 0) - return err; - - if (is_version(pcr, 0x522A, IC_VER_A)) { - err = rtsx_pci_write_phy_register(pcr, PHY_RCR2, - PHY_RCR2_INIT_27S); - if (err) - return err; - - rtsx_pci_write_phy_register(pcr, PHY_RCR1, PHY_RCR1_INIT_27S); - rtsx_pci_write_phy_register(pcr, PHY_FLD0, PHY_FLD0_INIT_27S); - rtsx_pci_write_phy_register(pcr, PHY_FLD3, PHY_FLD3_INIT_27S); - rtsx_pci_write_phy_register(pcr, PHY_FLD4, PHY_FLD4_INIT_27S); - } - - return 0; -} - -static int rts522a_extra_init_hw(struct rtsx_pcr *pcr) -{ - rts5227_extra_init_hw(pcr); - - rtsx_pci_write_register(pcr, FUNC_FORCE_CTL, FUNC_FORCE_UPME_XMT_DBG, - FUNC_FORCE_UPME_XMT_DBG); - rtsx_pci_write_register(pcr, PCLK_CTL, 0x04, 0x04); - rtsx_pci_write_register(pcr, PM_EVENT_DEBUG, PME_DEBUG_0, PME_DEBUG_0); - rtsx_pci_write_register(pcr, PM_CLK_FORCE_CTL, 0xFF, 0x11); - - return 0; -} - -/* rts522a operations mainly derived from rts5227, except phy/hw init setting. - */ -static const struct pcr_ops rts522a_pcr_ops = { - .fetch_vendor_settings = rts5227_fetch_vendor_settings, - .extra_init_hw = rts522a_extra_init_hw, - .optimize_phy = rts522a_optimize_phy, - .turn_on_led = rts5227_turn_on_led, - .turn_off_led = rts5227_turn_off_led, - .enable_auto_blink = rts5227_enable_auto_blink, - .disable_auto_blink = rts5227_disable_auto_blink, - .card_power_on = rts5227_card_power_on, - .card_power_off = rts5227_card_power_off, - .switch_output_voltage = rts5227_switch_output_voltage, - .cd_deglitch = NULL, - .conv_clk_and_div_n = NULL, - .force_power_down = rts5227_force_power_down, -}; - -void rts522a_init_params(struct rtsx_pcr *pcr) -{ - rts5227_init_params(pcr); - - pcr->reg_pm_ctrl3 = RTS522A_PM_CTRL3; -} diff --git a/drivers/mfd/rts5229.c b/drivers/mfd/rts5229.c deleted file mode 100644 index 9ed9dc84eac8..000000000000 --- a/drivers/mfd/rts5229.c +++ /dev/null @@ -1,273 +0,0 @@ -/* Driver for Realtek PCI-Express card reader - * - * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - * - * Author: - * Wei WANG - */ - -#include -#include -#include - -#include "rtsx_pcr.h" - -static u8 rts5229_get_ic_version(struct rtsx_pcr *pcr) -{ - u8 val; - - rtsx_pci_read_register(pcr, DUMMY_REG_RESET_0, &val); - return val & 0x0F; -} - -static void rts5229_fetch_vendor_settings(struct rtsx_pcr *pcr) -{ - u32 reg; - - rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, ®); - pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg); - - if (!rtsx_vendor_setting_valid(reg)) - return; - - pcr->aspm_en = rtsx_reg_to_aspm(reg); - pcr->sd30_drive_sel_1v8 = - map_sd_drive(rtsx_reg_to_sd30_drive_sel_1v8(reg)); - pcr->card_drive_sel &= 0x3F; - pcr->card_drive_sel |= rtsx_reg_to_card_drive_sel(reg); - - rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG2, ®); - pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG2, reg); - pcr->sd30_drive_sel_3v3 = - map_sd_drive(rtsx_reg_to_sd30_drive_sel_3v3(reg)); -} - -static void rts5229_force_power_down(struct rtsx_pcr *pcr, u8 pm_state) -{ - rtsx_pci_write_register(pcr, FPDCTL, 0x03, 0x03); -} - -static int rts5229_extra_init_hw(struct rtsx_pcr *pcr) -{ - rtsx_pci_init_cmd(pcr); - - /* Configure GPIO as output */ - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, GPIO_CTL, 0x02, 0x02); - /* Reset ASPM state to default value */ - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, ASPM_FORCE_CTL, 0x3F, 0); - /* Force CLKREQ# PIN to drive 0 to request clock */ - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PETXCFG, 0x08, 0x08); - /* Switch LDO3318 source from DV33 to card_3v3 */ - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_PWR_SEL, 0x03, 0x00); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_PWR_SEL, 0x03, 0x01); - /* LED shine disabled, set initial shine cycle period */ - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, OLT_LED_CTL, 0x0F, 0x02); - /* Configure driving */ - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_DRIVE_SEL, - 0xFF, pcr->sd30_drive_sel_3v3); - - return rtsx_pci_send_cmd(pcr, 100); -} - -static int rts5229_optimize_phy(struct rtsx_pcr *pcr) -{ - /* Optimize RX sensitivity */ - return rtsx_pci_write_phy_register(pcr, 0x00, 0xBA42); -} - -static int rts5229_turn_on_led(struct rtsx_pcr *pcr) -{ - return rtsx_pci_write_register(pcr, GPIO_CTL, 0x02, 0x02); -} - -static int rts5229_turn_off_led(struct rtsx_pcr *pcr) -{ - return rtsx_pci_write_register(pcr, GPIO_CTL, 0x02, 0x00); -} - -static int rts5229_enable_auto_blink(struct rtsx_pcr *pcr) -{ - return rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x08, 0x08); -} - -static int rts5229_disable_auto_blink(struct rtsx_pcr *pcr) -{ - return rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x08, 0x00); -} - -static int rts5229_card_power_on(struct rtsx_pcr *pcr, int card) -{ - int err; - - rtsx_pci_init_cmd(pcr); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, - SD_POWER_MASK, SD_PARTIAL_POWER_ON); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, - LDO3318_PWR_MASK, 0x02); - err = rtsx_pci_send_cmd(pcr, 100); - if (err < 0) - return err; - - /* To avoid too large in-rush current */ - udelay(150); - - rtsx_pci_init_cmd(pcr); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, - SD_POWER_MASK, SD_POWER_ON); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, - LDO3318_PWR_MASK, 0x06); - return rtsx_pci_send_cmd(pcr, 100); -} - -static int rts5229_card_power_off(struct rtsx_pcr *pcr, int card) -{ - rtsx_pci_init_cmd(pcr); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, - SD_POWER_MASK | PMOS_STRG_MASK, - SD_POWER_OFF | PMOS_STRG_400mA); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, - LDO3318_PWR_MASK, 0x00); - return rtsx_pci_send_cmd(pcr, 100); -} - -static int rts5229_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) -{ - int err; - - if (voltage == OUTPUT_3V3) { - err = rtsx_pci_write_register(pcr, - SD30_DRIVE_SEL, 0x07, pcr->sd30_drive_sel_3v3); - if (err < 0) - return err; - err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4FC0 | 0x24); - if (err < 0) - return err; - } else if (voltage == OUTPUT_1V8) { - err = rtsx_pci_write_register(pcr, - SD30_DRIVE_SEL, 0x07, pcr->sd30_drive_sel_1v8); - if (err < 0) - return err; - err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4C40 | 0x24); - if (err < 0) - return err; - } else { - return -EINVAL; - } - - return 0; -} - -static const struct pcr_ops rts5229_pcr_ops = { - .fetch_vendor_settings = rts5229_fetch_vendor_settings, - .extra_init_hw = rts5229_extra_init_hw, - .optimize_phy = rts5229_optimize_phy, - .turn_on_led = rts5229_turn_on_led, - .turn_off_led = rts5229_turn_off_led, - .enable_auto_blink = rts5229_enable_auto_blink, - .disable_auto_blink = rts5229_disable_auto_blink, - .card_power_on = rts5229_card_power_on, - .card_power_off = rts5229_card_power_off, - .switch_output_voltage = rts5229_switch_output_voltage, - .cd_deglitch = NULL, - .conv_clk_and_div_n = NULL, - .force_power_down = rts5229_force_power_down, -}; - -/* SD Pull Control Enable: - * SD_DAT[3:0] ==> pull up - * SD_CD ==> pull up - * SD_WP ==> pull up - * SD_CMD ==> pull up - * SD_CLK ==> pull down - */ -static const u32 rts5229_sd_pull_ctl_enable_tbl1[] = { - RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA), - RTSX_REG_PAIR(CARD_PULL_CTL3, 0xE9), - 0, -}; - -/* For RTS5229 version C */ -static const u32 rts5229_sd_pull_ctl_enable_tbl2[] = { - RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA), - RTSX_REG_PAIR(CARD_PULL_CTL3, 0xD9), - 0, -}; - -/* SD Pull Control Disable: - * SD_DAT[3:0] ==> pull down - * SD_CD ==> pull up - * SD_WP ==> pull down - * SD_CMD ==> pull down - * SD_CLK ==> pull down - */ -static const u32 rts5229_sd_pull_ctl_disable_tbl1[] = { - RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55), - RTSX_REG_PAIR(CARD_PULL_CTL3, 0xD5), - 0, -}; - -/* For RTS5229 version C */ -static const u32 rts5229_sd_pull_ctl_disable_tbl2[] = { - RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55), - RTSX_REG_PAIR(CARD_PULL_CTL3, 0xE5), - 0, -}; - -/* MS Pull Control Enable: - * MS CD ==> pull up - * others ==> pull down - */ -static const u32 rts5229_ms_pull_ctl_enable_tbl[] = { - RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55), - RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15), - 0, -}; - -/* MS Pull Control Disable: - * MS CD ==> pull up - * others ==> pull down - */ -static const u32 rts5229_ms_pull_ctl_disable_tbl[] = { - RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55), - RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15), - 0, -}; - -void rts5229_init_params(struct rtsx_pcr *pcr) -{ - pcr->extra_caps = EXTRA_CAPS_SD_SDR50 | EXTRA_CAPS_SD_SDR104; - pcr->num_slots = 2; - pcr->ops = &rts5229_pcr_ops; - - pcr->flags = 0; - pcr->card_drive_sel = RTSX_CARD_DRIVE_DEFAULT; - pcr->sd30_drive_sel_1v8 = DRIVER_TYPE_B; - pcr->sd30_drive_sel_3v3 = DRIVER_TYPE_D; - pcr->aspm_en = ASPM_L1_EN; - pcr->tx_initial_phase = SET_CLOCK_PHASE(27, 27, 15); - pcr->rx_initial_phase = SET_CLOCK_PHASE(30, 6, 6); - - pcr->ic_version = rts5229_get_ic_version(pcr); - if (pcr->ic_version == IC_VER_C) { - pcr->sd_pull_ctl_enable_tbl = rts5229_sd_pull_ctl_enable_tbl2; - pcr->sd_pull_ctl_disable_tbl = rts5229_sd_pull_ctl_disable_tbl2; - } else { - pcr->sd_pull_ctl_enable_tbl = rts5229_sd_pull_ctl_enable_tbl1; - pcr->sd_pull_ctl_disable_tbl = rts5229_sd_pull_ctl_disable_tbl1; - } - pcr->ms_pull_ctl_enable_tbl = rts5229_ms_pull_ctl_enable_tbl; - pcr->ms_pull_ctl_disable_tbl = rts5229_ms_pull_ctl_disable_tbl; -} diff --git a/drivers/mfd/rts5249.c b/drivers/mfd/rts5249.c deleted file mode 100644 index 7fcf37ba922c..000000000000 --- a/drivers/mfd/rts5249.c +++ /dev/null @@ -1,741 +0,0 @@ -/* Driver for Realtek PCI-Express card reader - * - * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - * - * Author: - * Wei WANG - */ - -#include -#include -#include - -#include "rtsx_pcr.h" - -static u8 rts5249_get_ic_version(struct rtsx_pcr *pcr) -{ - u8 val; - - rtsx_pci_read_register(pcr, DUMMY_REG_RESET_0, &val); - return val & 0x0F; -} - -static void rts5249_fill_driving(struct rtsx_pcr *pcr, u8 voltage) -{ - u8 driving_3v3[4][3] = { - {0x11, 0x11, 0x18}, - {0x55, 0x55, 0x5C}, - {0xFF, 0xFF, 0xFF}, - {0x96, 0x96, 0x96}, - }; - u8 driving_1v8[4][3] = { - {0xC4, 0xC4, 0xC4}, - {0x3C, 0x3C, 0x3C}, - {0xFE, 0xFE, 0xFE}, - {0xB3, 0xB3, 0xB3}, - }; - u8 (*driving)[3], drive_sel; - - if (voltage == OUTPUT_3V3) { - driving = driving_3v3; - drive_sel = pcr->sd30_drive_sel_3v3; - } else { - driving = driving_1v8; - drive_sel = pcr->sd30_drive_sel_1v8; - } - - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_CLK_DRIVE_SEL, - 0xFF, driving[drive_sel][0]); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_CMD_DRIVE_SEL, - 0xFF, driving[drive_sel][1]); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_DAT_DRIVE_SEL, - 0xFF, driving[drive_sel][2]); -} - -static void rtsx_base_fetch_vendor_settings(struct rtsx_pcr *pcr) -{ - u32 reg; - - rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, ®); - pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg); - - if (!rtsx_vendor_setting_valid(reg)) { - pcr_dbg(pcr, "skip fetch vendor setting\n"); - return; - } - - pcr->aspm_en = rtsx_reg_to_aspm(reg); - pcr->sd30_drive_sel_1v8 = rtsx_reg_to_sd30_drive_sel_1v8(reg); - pcr->card_drive_sel &= 0x3F; - pcr->card_drive_sel |= rtsx_reg_to_card_drive_sel(reg); - - rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG2, ®); - pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG2, reg); - pcr->sd30_drive_sel_3v3 = rtsx_reg_to_sd30_drive_sel_3v3(reg); - if (rtsx_reg_check_reverse_socket(reg)) - pcr->flags |= PCR_REVERSE_SOCKET; -} - -static void rtsx_base_force_power_down(struct rtsx_pcr *pcr, u8 pm_state) -{ - /* Set relink_time to 0 */ - rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 1, 0xFF, 0); - rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 2, 0xFF, 0); - rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 3, 0x01, 0); - - if (pm_state == HOST_ENTER_S3) - rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, - D3_DELINK_MODE_EN, D3_DELINK_MODE_EN); - - rtsx_pci_write_register(pcr, FPDCTL, 0x03, 0x03); -} - -static void rts5249_init_from_cfg(struct rtsx_pcr *pcr) -{ - struct rtsx_cr_option *option = &(pcr->option); - u32 lval; - - if (CHK_PCI_PID(pcr, PID_524A)) - rtsx_pci_read_config_dword(pcr, - PCR_ASPM_SETTING_REG1, &lval); - else - rtsx_pci_read_config_dword(pcr, - PCR_ASPM_SETTING_REG2, &lval); - - if (lval & ASPM_L1_1_EN_MASK) - rtsx_set_dev_flag(pcr, ASPM_L1_1_EN); - - if (lval & ASPM_L1_2_EN_MASK) - rtsx_set_dev_flag(pcr, ASPM_L1_2_EN); - - if (lval & PM_L1_1_EN_MASK) - rtsx_set_dev_flag(pcr, PM_L1_1_EN); - - if (lval & PM_L1_2_EN_MASK) - rtsx_set_dev_flag(pcr, PM_L1_2_EN); - - if (option->ltr_en) { - u16 val; - - pcie_capability_read_word(pcr->pci, PCI_EXP_DEVCTL2, &val); - if (val & PCI_EXP_DEVCTL2_LTR_EN) { - option->ltr_enabled = true; - option->ltr_active = true; - rtsx_set_ltr_latency(pcr, option->ltr_active_latency); - } else { - option->ltr_enabled = false; - } - } -} - -static int rts5249_init_from_hw(struct rtsx_pcr *pcr) -{ - struct rtsx_cr_option *option = &(pcr->option); - - if (rtsx_check_dev_flag(pcr, ASPM_L1_1_EN | ASPM_L1_2_EN - | PM_L1_1_EN | PM_L1_2_EN)) - option->force_clkreq_0 = false; - else - option->force_clkreq_0 = true; - - return 0; -} - -static int rts5249_extra_init_hw(struct rtsx_pcr *pcr) -{ - struct rtsx_cr_option *option = &(pcr->option); - - rts5249_init_from_cfg(pcr); - rts5249_init_from_hw(pcr); - - rtsx_pci_init_cmd(pcr); - - /* Rest L1SUB Config */ - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, L1SUB_CONFIG3, 0xFF, 0x00); - /* Configure GPIO as output */ - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, GPIO_CTL, 0x02, 0x02); - /* Reset ASPM state to default value */ - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, ASPM_FORCE_CTL, 0x3F, 0); - /* Switch LDO3318 source from DV33 to card_3v3 */ - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_PWR_SEL, 0x03, 0x00); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_PWR_SEL, 0x03, 0x01); - /* LED shine disabled, set initial shine cycle period */ - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, OLT_LED_CTL, 0x0F, 0x02); - /* Configure driving */ - rts5249_fill_driving(pcr, OUTPUT_3V3); - if (pcr->flags & PCR_REVERSE_SOCKET) - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PETXCFG, 0xB0, 0xB0); - else - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PETXCFG, 0xB0, 0x80); - - /* - * If u_force_clkreq_0 is enabled, CLKREQ# PIN will be forced - * to drive low, and we forcibly request clock. - */ - if (option->force_clkreq_0) - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PETXCFG, - FORCE_CLKREQ_DELINK_MASK, FORCE_CLKREQ_LOW); - else - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PETXCFG, - FORCE_CLKREQ_DELINK_MASK, FORCE_CLKREQ_HIGH); - - return rtsx_pci_send_cmd(pcr, CMD_TIMEOUT_DEF); -} - -static int rts5249_optimize_phy(struct rtsx_pcr *pcr) -{ - int err; - - err = rtsx_pci_write_register(pcr, PM_CTRL3, D3_DELINK_MODE_EN, 0x00); - if (err < 0) - return err; - - err = rtsx_pci_write_phy_register(pcr, PHY_REV, - PHY_REV_RESV | PHY_REV_RXIDLE_LATCHED | - PHY_REV_P1_EN | PHY_REV_RXIDLE_EN | - PHY_REV_CLKREQ_TX_EN | PHY_REV_RX_PWST | - PHY_REV_CLKREQ_DT_1_0 | PHY_REV_STOP_CLKRD | - PHY_REV_STOP_CLKWR); - if (err < 0) - return err; - - msleep(1); - - err = rtsx_pci_write_phy_register(pcr, PHY_BPCR, - PHY_BPCR_IBRXSEL | PHY_BPCR_IBTXSEL | - PHY_BPCR_IB_FILTER | PHY_BPCR_CMIRROR_EN); - if (err < 0) - return err; - - err = rtsx_pci_write_phy_register(pcr, PHY_PCR, - PHY_PCR_FORCE_CODE | PHY_PCR_OOBS_CALI_50 | - PHY_PCR_OOBS_VCM_08 | PHY_PCR_OOBS_SEN_90 | - PHY_PCR_RSSI_EN | PHY_PCR_RX10K); - if (err < 0) - return err; - - err = rtsx_pci_write_phy_register(pcr, PHY_RCR2, - PHY_RCR2_EMPHASE_EN | PHY_RCR2_NADJR | - PHY_RCR2_CDR_SR_2 | PHY_RCR2_FREQSEL_12 | - PHY_RCR2_CDR_SC_12P | PHY_RCR2_CALIB_LATE); - if (err < 0) - return err; - - err = rtsx_pci_write_phy_register(pcr, PHY_FLD4, - PHY_FLD4_FLDEN_SEL | PHY_FLD4_REQ_REF | - PHY_FLD4_RXAMP_OFF | PHY_FLD4_REQ_ADDA | - PHY_FLD4_BER_COUNT | PHY_FLD4_BER_TIMER | - PHY_FLD4_BER_CHK_EN); - if (err < 0) - return err; - err = rtsx_pci_write_phy_register(pcr, PHY_RDR, - PHY_RDR_RXDSEL_1_9 | PHY_SSC_AUTO_PWD); - if (err < 0) - return err; - err = rtsx_pci_write_phy_register(pcr, PHY_RCR1, - PHY_RCR1_ADP_TIME_4 | PHY_RCR1_VCO_COARSE); - if (err < 0) - return err; - err = rtsx_pci_write_phy_register(pcr, PHY_FLD3, - PHY_FLD3_TIMER_4 | PHY_FLD3_TIMER_6 | - PHY_FLD3_RXDELINK); - if (err < 0) - return err; - - return rtsx_pci_write_phy_register(pcr, PHY_TUNE, - PHY_TUNE_TUNEREF_1_0 | PHY_TUNE_VBGSEL_1252 | - PHY_TUNE_SDBUS_33 | PHY_TUNE_TUNED18 | - PHY_TUNE_TUNED12 | PHY_TUNE_TUNEA12); -} - -static int rtsx_base_turn_on_led(struct rtsx_pcr *pcr) -{ - return rtsx_pci_write_register(pcr, GPIO_CTL, 0x02, 0x02); -} - -static int rtsx_base_turn_off_led(struct rtsx_pcr *pcr) -{ - return rtsx_pci_write_register(pcr, GPIO_CTL, 0x02, 0x00); -} - -static int rtsx_base_enable_auto_blink(struct rtsx_pcr *pcr) -{ - return rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x08, 0x08); -} - -static int rtsx_base_disable_auto_blink(struct rtsx_pcr *pcr) -{ - return rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x08, 0x00); -} - -static int rtsx_base_card_power_on(struct rtsx_pcr *pcr, int card) -{ - int err; - - rtsx_pci_init_cmd(pcr); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, - SD_POWER_MASK, SD_VCC_PARTIAL_POWER_ON); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, - LDO3318_PWR_MASK, 0x02); - err = rtsx_pci_send_cmd(pcr, 100); - if (err < 0) - return err; - - msleep(5); - - rtsx_pci_init_cmd(pcr); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, - SD_POWER_MASK, SD_VCC_POWER_ON); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, - LDO3318_PWR_MASK, 0x06); - return rtsx_pci_send_cmd(pcr, 100); -} - -static int rtsx_base_card_power_off(struct rtsx_pcr *pcr, int card) -{ - rtsx_pci_init_cmd(pcr); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, - SD_POWER_MASK, SD_POWER_OFF); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, - LDO3318_PWR_MASK, 0x00); - return rtsx_pci_send_cmd(pcr, 100); -} - -static int rtsx_base_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) -{ - int err; - u16 append; - - switch (voltage) { - case OUTPUT_3V3: - err = rtsx_pci_update_phy(pcr, PHY_TUNE, PHY_TUNE_VOLTAGE_MASK, - PHY_TUNE_VOLTAGE_3V3); - if (err < 0) - return err; - break; - case OUTPUT_1V8: - append = PHY_TUNE_D18_1V8; - if (CHK_PCI_PID(pcr, 0x5249)) { - err = rtsx_pci_update_phy(pcr, PHY_BACR, - PHY_BACR_BASIC_MASK, 0); - if (err < 0) - return err; - append = PHY_TUNE_D18_1V7; - } - - err = rtsx_pci_update_phy(pcr, PHY_TUNE, PHY_TUNE_VOLTAGE_MASK, - append); - if (err < 0) - return err; - break; - default: - pcr_dbg(pcr, "unknown output voltage %d\n", voltage); - return -EINVAL; - } - - /* set pad drive */ - rtsx_pci_init_cmd(pcr); - rts5249_fill_driving(pcr, voltage); - return rtsx_pci_send_cmd(pcr, 100); -} - -static void rts5249_set_aspm(struct rtsx_pcr *pcr, bool enable) -{ - struct rtsx_cr_option *option = &pcr->option; - u8 val = 0; - - if (pcr->aspm_enabled == enable) - return; - - if (option->dev_aspm_mode == DEV_ASPM_DYNAMIC) { - if (enable) - val = pcr->aspm_en; - rtsx_pci_update_cfg_byte(pcr, - pcr->pcie_cap + PCI_EXP_LNKCTL, - ASPM_MASK_NEG, val); - } else if (option->dev_aspm_mode == DEV_ASPM_BACKDOOR) { - u8 mask = FORCE_ASPM_VAL_MASK | FORCE_ASPM_CTL0; - - if (!enable) - val = FORCE_ASPM_CTL0; - rtsx_pci_write_register(pcr, ASPM_FORCE_CTL, mask, val); - } - - pcr->aspm_enabled = enable; -} - -static const struct pcr_ops rts5249_pcr_ops = { - .fetch_vendor_settings = rtsx_base_fetch_vendor_settings, - .extra_init_hw = rts5249_extra_init_hw, - .optimize_phy = rts5249_optimize_phy, - .turn_on_led = rtsx_base_turn_on_led, - .turn_off_led = rtsx_base_turn_off_led, - .enable_auto_blink = rtsx_base_enable_auto_blink, - .disable_auto_blink = rtsx_base_disable_auto_blink, - .card_power_on = rtsx_base_card_power_on, - .card_power_off = rtsx_base_card_power_off, - .switch_output_voltage = rtsx_base_switch_output_voltage, - .force_power_down = rtsx_base_force_power_down, - .set_aspm = rts5249_set_aspm, -}; - -/* SD Pull Control Enable: - * SD_DAT[3:0] ==> pull up - * SD_CD ==> pull up - * SD_WP ==> pull up - * SD_CMD ==> pull up - * SD_CLK ==> pull down - */ -static const u32 rts5249_sd_pull_ctl_enable_tbl[] = { - RTSX_REG_PAIR(CARD_PULL_CTL1, 0x66), - RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA), - RTSX_REG_PAIR(CARD_PULL_CTL3, 0xE9), - RTSX_REG_PAIR(CARD_PULL_CTL4, 0xAA), - 0, -}; - -/* SD Pull Control Disable: - * SD_DAT[3:0] ==> pull down - * SD_CD ==> pull up - * SD_WP ==> pull down - * SD_CMD ==> pull down - * SD_CLK ==> pull down - */ -static const u32 rts5249_sd_pull_ctl_disable_tbl[] = { - RTSX_REG_PAIR(CARD_PULL_CTL1, 0x66), - RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55), - RTSX_REG_PAIR(CARD_PULL_CTL3, 0xD5), - RTSX_REG_PAIR(CARD_PULL_CTL4, 0x55), - 0, -}; - -/* MS Pull Control Enable: - * MS CD ==> pull up - * others ==> pull down - */ -static const u32 rts5249_ms_pull_ctl_enable_tbl[] = { - RTSX_REG_PAIR(CARD_PULL_CTL4, 0x55), - RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55), - RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15), - 0, -}; - -/* MS Pull Control Disable: - * MS CD ==> pull up - * others ==> pull down - */ -static const u32 rts5249_ms_pull_ctl_disable_tbl[] = { - RTSX_REG_PAIR(CARD_PULL_CTL4, 0x55), - RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55), - RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15), - 0, -}; - -void rts5249_init_params(struct rtsx_pcr *pcr) -{ - struct rtsx_cr_option *option = &(pcr->option); - - pcr->extra_caps = EXTRA_CAPS_SD_SDR50 | EXTRA_CAPS_SD_SDR104; - pcr->num_slots = 2; - pcr->ops = &rts5249_pcr_ops; - - pcr->flags = 0; - pcr->card_drive_sel = RTSX_CARD_DRIVE_DEFAULT; - pcr->sd30_drive_sel_1v8 = CFG_DRIVER_TYPE_B; - pcr->sd30_drive_sel_3v3 = CFG_DRIVER_TYPE_B; - pcr->aspm_en = ASPM_L1_EN; - pcr->tx_initial_phase = SET_CLOCK_PHASE(1, 29, 16); - pcr->rx_initial_phase = SET_CLOCK_PHASE(24, 6, 5); - - pcr->ic_version = rts5249_get_ic_version(pcr); - pcr->sd_pull_ctl_enable_tbl = rts5249_sd_pull_ctl_enable_tbl; - pcr->sd_pull_ctl_disable_tbl = rts5249_sd_pull_ctl_disable_tbl; - pcr->ms_pull_ctl_enable_tbl = rts5249_ms_pull_ctl_enable_tbl; - pcr->ms_pull_ctl_disable_tbl = rts5249_ms_pull_ctl_disable_tbl; - - pcr->reg_pm_ctrl3 = PM_CTRL3; - - option->dev_flags = (LTR_L1SS_PWR_GATE_CHECK_CARD_EN - | LTR_L1SS_PWR_GATE_EN); - option->ltr_en = true; - - /* Init latency of active, idle, L1OFF to 60us, 300us, 3ms */ - option->ltr_active_latency = LTR_ACTIVE_LATENCY_DEF; - option->ltr_idle_latency = LTR_IDLE_LATENCY_DEF; - option->ltr_l1off_latency = LTR_L1OFF_LATENCY_DEF; - option->dev_aspm_mode = DEV_ASPM_DYNAMIC; - option->l1_snooze_delay = L1_SNOOZE_DELAY_DEF; - option->ltr_l1off_sspwrgate = LTR_L1OFF_SSPWRGATE_5249_DEF; - option->ltr_l1off_snooze_sspwrgate = - LTR_L1OFF_SNOOZE_SSPWRGATE_5249_DEF; -} - -static int rts524a_write_phy(struct rtsx_pcr *pcr, u8 addr, u16 val) -{ - addr = addr & 0x80 ? (addr & 0x7F) | 0x40 : addr; - - return __rtsx_pci_write_phy_register(pcr, addr, val); -} - -static int rts524a_read_phy(struct rtsx_pcr *pcr, u8 addr, u16 *val) -{ - addr = addr & 0x80 ? (addr & 0x7F) | 0x40 : addr; - - return __rtsx_pci_read_phy_register(pcr, addr, val); -} - -static int rts524a_optimize_phy(struct rtsx_pcr *pcr) -{ - int err; - - err = rtsx_pci_write_register(pcr, RTS524A_PM_CTRL3, - D3_DELINK_MODE_EN, 0x00); - if (err < 0) - return err; - - rtsx_pci_write_phy_register(pcr, PHY_PCR, - PHY_PCR_FORCE_CODE | PHY_PCR_OOBS_CALI_50 | - PHY_PCR_OOBS_VCM_08 | PHY_PCR_OOBS_SEN_90 | PHY_PCR_RSSI_EN); - rtsx_pci_write_phy_register(pcr, PHY_SSCCR3, - PHY_SSCCR3_STEP_IN | PHY_SSCCR3_CHECK_DELAY); - - if (is_version(pcr, 0x524A, IC_VER_A)) { - rtsx_pci_write_phy_register(pcr, PHY_SSCCR3, - PHY_SSCCR3_STEP_IN | PHY_SSCCR3_CHECK_DELAY); - rtsx_pci_write_phy_register(pcr, PHY_SSCCR2, - PHY_SSCCR2_PLL_NCODE | PHY_SSCCR2_TIME0 | - PHY_SSCCR2_TIME2_WIDTH); - rtsx_pci_write_phy_register(pcr, PHY_ANA1A, - PHY_ANA1A_TXR_LOOPBACK | PHY_ANA1A_RXT_BIST | - PHY_ANA1A_TXR_BIST | PHY_ANA1A_REV); - rtsx_pci_write_phy_register(pcr, PHY_ANA1D, - PHY_ANA1D_DEBUG_ADDR); - rtsx_pci_write_phy_register(pcr, PHY_DIG1E, - PHY_DIG1E_REV | PHY_DIG1E_D0_X_D1 | - PHY_DIG1E_RX_ON_HOST | PHY_DIG1E_RCLK_REF_HOST | - PHY_DIG1E_RCLK_TX_EN_KEEP | - PHY_DIG1E_RCLK_TX_TERM_KEEP | - PHY_DIG1E_RCLK_RX_EIDLE_ON | PHY_DIG1E_TX_TERM_KEEP | - PHY_DIG1E_RX_TERM_KEEP | PHY_DIG1E_TX_EN_KEEP | - PHY_DIG1E_RX_EN_KEEP); - } - - rtsx_pci_write_phy_register(pcr, PHY_ANA08, - PHY_ANA08_RX_EQ_DCGAIN | PHY_ANA08_SEL_RX_EN | - PHY_ANA08_RX_EQ_VAL | PHY_ANA08_SCP | PHY_ANA08_SEL_IPI); - - return 0; -} - -static int rts524a_extra_init_hw(struct rtsx_pcr *pcr) -{ - rts5249_extra_init_hw(pcr); - - rtsx_pci_write_register(pcr, FUNC_FORCE_CTL, - FORCE_ASPM_L1_EN, FORCE_ASPM_L1_EN); - rtsx_pci_write_register(pcr, PM_EVENT_DEBUG, PME_DEBUG_0, PME_DEBUG_0); - rtsx_pci_write_register(pcr, LDO_VCC_CFG1, LDO_VCC_LMT_EN, - LDO_VCC_LMT_EN); - rtsx_pci_write_register(pcr, PCLK_CTL, PCLK_MODE_SEL, PCLK_MODE_SEL); - if (is_version(pcr, 0x524A, IC_VER_A)) { - rtsx_pci_write_register(pcr, LDO_DV18_CFG, - LDO_DV18_SR_MASK, LDO_DV18_SR_DF); - rtsx_pci_write_register(pcr, LDO_VCC_CFG1, - LDO_VCC_REF_TUNE_MASK, LDO_VCC_REF_1V2); - rtsx_pci_write_register(pcr, LDO_VIO_CFG, - LDO_VIO_REF_TUNE_MASK, LDO_VIO_REF_1V2); - rtsx_pci_write_register(pcr, LDO_VIO_CFG, - LDO_VIO_SR_MASK, LDO_VIO_SR_DF); - rtsx_pci_write_register(pcr, LDO_DV12S_CFG, - LDO_REF12_TUNE_MASK, LDO_REF12_TUNE_DF); - rtsx_pci_write_register(pcr, SD40_LDO_CTL1, - SD40_VIO_TUNE_MASK, SD40_VIO_TUNE_1V7); - } - - return 0; -} - -static void rts5250_set_l1off_cfg_sub_d0(struct rtsx_pcr *pcr, int active) -{ - struct rtsx_cr_option *option = &(pcr->option); - - u32 interrupt = rtsx_pci_readl(pcr, RTSX_BIPR); - int card_exist = (interrupt & SD_EXIST) | (interrupt & MS_EXIST); - int aspm_L1_1, aspm_L1_2; - u8 val = 0; - - aspm_L1_1 = rtsx_check_dev_flag(pcr, ASPM_L1_1_EN); - aspm_L1_2 = rtsx_check_dev_flag(pcr, ASPM_L1_2_EN); - - if (active) { - /* Run, latency: 60us */ - if (aspm_L1_1) - val = option->ltr_l1off_snooze_sspwrgate; - } else { - /* L1off, latency: 300us */ - if (aspm_L1_2) - val = option->ltr_l1off_sspwrgate; - } - - if (aspm_L1_1 || aspm_L1_2) { - if (rtsx_check_dev_flag(pcr, - LTR_L1SS_PWR_GATE_CHECK_CARD_EN)) { - if (card_exist) - val &= ~L1OFF_MBIAS2_EN_5250; - else - val |= L1OFF_MBIAS2_EN_5250; - } - } - rtsx_set_l1off_sub(pcr, val); -} - -static const struct pcr_ops rts524a_pcr_ops = { - .write_phy = rts524a_write_phy, - .read_phy = rts524a_read_phy, - .fetch_vendor_settings = rtsx_base_fetch_vendor_settings, - .extra_init_hw = rts524a_extra_init_hw, - .optimize_phy = rts524a_optimize_phy, - .turn_on_led = rtsx_base_turn_on_led, - .turn_off_led = rtsx_base_turn_off_led, - .enable_auto_blink = rtsx_base_enable_auto_blink, - .disable_auto_blink = rtsx_base_disable_auto_blink, - .card_power_on = rtsx_base_card_power_on, - .card_power_off = rtsx_base_card_power_off, - .switch_output_voltage = rtsx_base_switch_output_voltage, - .force_power_down = rtsx_base_force_power_down, - .set_l1off_cfg_sub_d0 = rts5250_set_l1off_cfg_sub_d0, - .set_aspm = rts5249_set_aspm, -}; - -void rts524a_init_params(struct rtsx_pcr *pcr) -{ - rts5249_init_params(pcr); - pcr->option.ltr_l1off_sspwrgate = LTR_L1OFF_SSPWRGATE_5250_DEF; - pcr->option.ltr_l1off_snooze_sspwrgate = - LTR_L1OFF_SNOOZE_SSPWRGATE_5250_DEF; - - pcr->reg_pm_ctrl3 = RTS524A_PM_CTRL3; - pcr->ops = &rts524a_pcr_ops; -} - -static int rts525a_card_power_on(struct rtsx_pcr *pcr, int card) -{ - rtsx_pci_write_register(pcr, LDO_VCC_CFG1, - LDO_VCC_TUNE_MASK, LDO_VCC_3V3); - return rtsx_base_card_power_on(pcr, card); -} - -static int rts525a_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) -{ - switch (voltage) { - case OUTPUT_3V3: - rtsx_pci_write_register(pcr, LDO_CONFIG2, - LDO_D3318_MASK, LDO_D3318_33V); - rtsx_pci_write_register(pcr, SD_PAD_CTL, SD_IO_USING_1V8, 0); - break; - case OUTPUT_1V8: - rtsx_pci_write_register(pcr, LDO_CONFIG2, - LDO_D3318_MASK, LDO_D3318_18V); - rtsx_pci_write_register(pcr, SD_PAD_CTL, SD_IO_USING_1V8, - SD_IO_USING_1V8); - break; - default: - return -EINVAL; - } - - rtsx_pci_init_cmd(pcr); - rts5249_fill_driving(pcr, voltage); - return rtsx_pci_send_cmd(pcr, 100); -} - -static int rts525a_optimize_phy(struct rtsx_pcr *pcr) -{ - int err; - - err = rtsx_pci_write_register(pcr, RTS524A_PM_CTRL3, - D3_DELINK_MODE_EN, 0x00); - if (err < 0) - return err; - - rtsx_pci_write_phy_register(pcr, _PHY_FLD0, - _PHY_FLD0_CLK_REQ_20C | _PHY_FLD0_RX_IDLE_EN | - _PHY_FLD0_BIT_ERR_RSTN | _PHY_FLD0_BER_COUNT | - _PHY_FLD0_BER_TIMER | _PHY_FLD0_CHECK_EN); - - rtsx_pci_write_phy_register(pcr, _PHY_ANA03, - _PHY_ANA03_TIMER_MAX | _PHY_ANA03_OOBS_DEB_EN | - _PHY_CMU_DEBUG_EN); - - if (is_version(pcr, 0x525A, IC_VER_A)) - rtsx_pci_write_phy_register(pcr, _PHY_REV0, - _PHY_REV0_FILTER_OUT | _PHY_REV0_CDR_BYPASS_PFD | - _PHY_REV0_CDR_RX_IDLE_BYPASS); - - return 0; -} - -static int rts525a_extra_init_hw(struct rtsx_pcr *pcr) -{ - rts5249_extra_init_hw(pcr); - - rtsx_pci_write_register(pcr, PCLK_CTL, PCLK_MODE_SEL, PCLK_MODE_SEL); - if (is_version(pcr, 0x525A, IC_VER_A)) { - rtsx_pci_write_register(pcr, L1SUB_CONFIG2, - L1SUB_AUTO_CFG, L1SUB_AUTO_CFG); - rtsx_pci_write_register(pcr, RREF_CFG, - RREF_VBGSEL_MASK, RREF_VBGSEL_1V25); - rtsx_pci_write_register(pcr, LDO_VIO_CFG, - LDO_VIO_TUNE_MASK, LDO_VIO_1V7); - rtsx_pci_write_register(pcr, LDO_DV12S_CFG, - LDO_D12_TUNE_MASK, LDO_D12_TUNE_DF); - rtsx_pci_write_register(pcr, LDO_AV12S_CFG, - LDO_AV12S_TUNE_MASK, LDO_AV12S_TUNE_DF); - rtsx_pci_write_register(pcr, LDO_VCC_CFG0, - LDO_VCC_LMTVTH_MASK, LDO_VCC_LMTVTH_2A); - rtsx_pci_write_register(pcr, OOBS_CONFIG, - OOBS_AUTOK_DIS | OOBS_VAL_MASK, 0x89); - } - - return 0; -} - -static const struct pcr_ops rts525a_pcr_ops = { - .fetch_vendor_settings = rtsx_base_fetch_vendor_settings, - .extra_init_hw = rts525a_extra_init_hw, - .optimize_phy = rts525a_optimize_phy, - .turn_on_led = rtsx_base_turn_on_led, - .turn_off_led = rtsx_base_turn_off_led, - .enable_auto_blink = rtsx_base_enable_auto_blink, - .disable_auto_blink = rtsx_base_disable_auto_blink, - .card_power_on = rts525a_card_power_on, - .card_power_off = rtsx_base_card_power_off, - .switch_output_voltage = rts525a_switch_output_voltage, - .force_power_down = rtsx_base_force_power_down, - .set_l1off_cfg_sub_d0 = rts5250_set_l1off_cfg_sub_d0, - .set_aspm = rts5249_set_aspm, -}; - -void rts525a_init_params(struct rtsx_pcr *pcr) -{ - rts5249_init_params(pcr); - pcr->option.ltr_l1off_sspwrgate = LTR_L1OFF_SSPWRGATE_5250_DEF; - pcr->option.ltr_l1off_snooze_sspwrgate = - LTR_L1OFF_SNOOZE_SSPWRGATE_5250_DEF; - - pcr->reg_pm_ctrl3 = RTS524A_PM_CTRL3; - pcr->ops = &rts525a_pcr_ops; -} - diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c deleted file mode 100644 index 590fb9aad77d..000000000000 --- a/drivers/mfd/rtsx_pcr.c +++ /dev/null @@ -1,1569 +0,0 @@ -/* Driver for Realtek PCI-Express card reader - * - * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - * - * Author: - * Wei WANG - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "rtsx_pcr.h" - -static bool msi_en = true; -module_param(msi_en, bool, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(msi_en, "Enable MSI"); - -static DEFINE_IDR(rtsx_pci_idr); -static DEFINE_SPINLOCK(rtsx_pci_lock); - -static struct mfd_cell rtsx_pcr_cells[] = { - [RTSX_SD_CARD] = { - .name = DRV_NAME_RTSX_PCI_SDMMC, - }, - [RTSX_MS_CARD] = { - .name = DRV_NAME_RTSX_PCI_MS, - }, -}; - -static const struct pci_device_id rtsx_pci_ids[] = { - { PCI_DEVICE(0x10EC, 0x5209), PCI_CLASS_OTHERS << 16, 0xFF0000 }, - { PCI_DEVICE(0x10EC, 0x5229), PCI_CLASS_OTHERS << 16, 0xFF0000 }, - { PCI_DEVICE(0x10EC, 0x5289), PCI_CLASS_OTHERS << 16, 0xFF0000 }, - { PCI_DEVICE(0x10EC, 0x5227), PCI_CLASS_OTHERS << 16, 0xFF0000 }, - { PCI_DEVICE(0x10EC, 0x522A), PCI_CLASS_OTHERS << 16, 0xFF0000 }, - { PCI_DEVICE(0x10EC, 0x5249), PCI_CLASS_OTHERS << 16, 0xFF0000 }, - { PCI_DEVICE(0x10EC, 0x5287), PCI_CLASS_OTHERS << 16, 0xFF0000 }, - { PCI_DEVICE(0x10EC, 0x5286), PCI_CLASS_OTHERS << 16, 0xFF0000 }, - { PCI_DEVICE(0x10EC, 0x524A), PCI_CLASS_OTHERS << 16, 0xFF0000 }, - { PCI_DEVICE(0x10EC, 0x525A), PCI_CLASS_OTHERS << 16, 0xFF0000 }, - { 0, } -}; - -MODULE_DEVICE_TABLE(pci, rtsx_pci_ids); - -static inline void rtsx_pci_enable_aspm(struct rtsx_pcr *pcr) -{ - rtsx_pci_update_cfg_byte(pcr, pcr->pcie_cap + PCI_EXP_LNKCTL, - 0xFC, pcr->aspm_en); -} - -static inline void rtsx_pci_disable_aspm(struct rtsx_pcr *pcr) -{ - rtsx_pci_update_cfg_byte(pcr, pcr->pcie_cap + PCI_EXP_LNKCTL, - 0xFC, 0); -} - -int rtsx_comm_set_ltr_latency(struct rtsx_pcr *pcr, u32 latency) -{ - rtsx_pci_write_register(pcr, MSGTXDATA0, - MASK_8_BIT_DEF, (u8) (latency & 0xFF)); - rtsx_pci_write_register(pcr, MSGTXDATA1, - MASK_8_BIT_DEF, (u8)((latency >> 8) & 0xFF)); - rtsx_pci_write_register(pcr, MSGTXDATA2, - MASK_8_BIT_DEF, (u8)((latency >> 16) & 0xFF)); - rtsx_pci_write_register(pcr, MSGTXDATA3, - MASK_8_BIT_DEF, (u8)((latency >> 24) & 0xFF)); - rtsx_pci_write_register(pcr, LTR_CTL, LTR_TX_EN_MASK | - LTR_LATENCY_MODE_MASK, LTR_TX_EN_1 | LTR_LATENCY_MODE_SW); - - return 0; -} - -int rtsx_set_ltr_latency(struct rtsx_pcr *pcr, u32 latency) -{ - if (pcr->ops->set_ltr_latency) - return pcr->ops->set_ltr_latency(pcr, latency); - else - return rtsx_comm_set_ltr_latency(pcr, latency); -} - -static void rtsx_comm_set_aspm(struct rtsx_pcr *pcr, bool enable) -{ - struct rtsx_cr_option *option = &pcr->option; - - if (pcr->aspm_enabled == enable) - return; - - if (option->dev_aspm_mode == DEV_ASPM_DYNAMIC) { - if (enable) - rtsx_pci_enable_aspm(pcr); - else - rtsx_pci_disable_aspm(pcr); - } else if (option->dev_aspm_mode == DEV_ASPM_BACKDOOR) { - u8 mask = FORCE_ASPM_VAL_MASK; - u8 val = 0; - - if (enable) - val = pcr->aspm_en; - rtsx_pci_write_register(pcr, ASPM_FORCE_CTL, mask, val); - } - - pcr->aspm_enabled = enable; -} - -static void rtsx_disable_aspm(struct rtsx_pcr *pcr) -{ - if (pcr->ops->set_aspm) - pcr->ops->set_aspm(pcr, false); - else - rtsx_comm_set_aspm(pcr, false); -} - -int rtsx_set_l1off_sub(struct rtsx_pcr *pcr, u8 val) -{ - rtsx_pci_write_register(pcr, L1SUB_CONFIG3, 0xFF, val); - - return 0; -} - -void rtsx_set_l1off_sub_cfg_d0(struct rtsx_pcr *pcr, int active) -{ - if (pcr->ops->set_l1off_cfg_sub_d0) - pcr->ops->set_l1off_cfg_sub_d0(pcr, active); -} - -static void rtsx_comm_pm_full_on(struct rtsx_pcr *pcr) -{ - struct rtsx_cr_option *option = &pcr->option; - - rtsx_disable_aspm(pcr); - - if (option->ltr_enabled) - rtsx_set_ltr_latency(pcr, option->ltr_active_latency); - - if (rtsx_check_dev_flag(pcr, LTR_L1SS_PWR_GATE_EN)) - rtsx_set_l1off_sub_cfg_d0(pcr, 1); -} - -void rtsx_pm_full_on(struct rtsx_pcr *pcr) -{ - if (pcr->ops->full_on) - pcr->ops->full_on(pcr); - else - rtsx_comm_pm_full_on(pcr); -} - -void rtsx_pci_start_run(struct rtsx_pcr *pcr) -{ - /* If pci device removed, don't queue idle work any more */ - if (pcr->remove_pci) - return; - - if (pcr->state != PDEV_STAT_RUN) { - pcr->state = PDEV_STAT_RUN; - if (pcr->ops->enable_auto_blink) - pcr->ops->enable_auto_blink(pcr); - rtsx_pm_full_on(pcr); - } - - mod_delayed_work(system_wq, &pcr->idle_work, msecs_to_jiffies(200)); -} -EXPORT_SYMBOL_GPL(rtsx_pci_start_run); - -int rtsx_pci_write_register(struct rtsx_pcr *pcr, u16 addr, u8 mask, u8 data) -{ - int i; - u32 val = HAIMR_WRITE_START; - - val |= (u32)(addr & 0x3FFF) << 16; - val |= (u32)mask << 8; - val |= (u32)data; - - rtsx_pci_writel(pcr, RTSX_HAIMR, val); - - for (i = 0; i < MAX_RW_REG_CNT; i++) { - val = rtsx_pci_readl(pcr, RTSX_HAIMR); - if ((val & HAIMR_TRANS_END) == 0) { - if (data != (u8)val) - return -EIO; - return 0; - } - } - - return -ETIMEDOUT; -} -EXPORT_SYMBOL_GPL(rtsx_pci_write_register); - -int rtsx_pci_read_register(struct rtsx_pcr *pcr, u16 addr, u8 *data) -{ - u32 val = HAIMR_READ_START; - int i; - - val |= (u32)(addr & 0x3FFF) << 16; - rtsx_pci_writel(pcr, RTSX_HAIMR, val); - - for (i = 0; i < MAX_RW_REG_CNT; i++) { - val = rtsx_pci_readl(pcr, RTSX_HAIMR); - if ((val & HAIMR_TRANS_END) == 0) - break; - } - - if (i >= MAX_RW_REG_CNT) - return -ETIMEDOUT; - - if (data) - *data = (u8)(val & 0xFF); - - return 0; -} -EXPORT_SYMBOL_GPL(rtsx_pci_read_register); - -int __rtsx_pci_write_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 val) -{ - int err, i, finished = 0; - u8 tmp; - - rtsx_pci_init_cmd(pcr); - - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PHYDATA0, 0xFF, (u8)val); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PHYDATA1, 0xFF, (u8)(val >> 8)); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PHYADDR, 0xFF, addr); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PHYRWCTL, 0xFF, 0x81); - - err = rtsx_pci_send_cmd(pcr, 100); - if (err < 0) - return err; - - for (i = 0; i < 100000; i++) { - err = rtsx_pci_read_register(pcr, PHYRWCTL, &tmp); - if (err < 0) - return err; - - if (!(tmp & 0x80)) { - finished = 1; - break; - } - } - - if (!finished) - return -ETIMEDOUT; - - return 0; -} - -int rtsx_pci_write_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 val) -{ - if (pcr->ops->write_phy) - return pcr->ops->write_phy(pcr, addr, val); - - return __rtsx_pci_write_phy_register(pcr, addr, val); -} -EXPORT_SYMBOL_GPL(rtsx_pci_write_phy_register); - -int __rtsx_pci_read_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 *val) -{ - int err, i, finished = 0; - u16 data; - u8 *ptr, tmp; - - rtsx_pci_init_cmd(pcr); - - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PHYADDR, 0xFF, addr); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PHYRWCTL, 0xFF, 0x80); - - err = rtsx_pci_send_cmd(pcr, 100); - if (err < 0) - return err; - - for (i = 0; i < 100000; i++) { - err = rtsx_pci_read_register(pcr, PHYRWCTL, &tmp); - if (err < 0) - return err; - - if (!(tmp & 0x80)) { - finished = 1; - break; - } - } - - if (!finished) - return -ETIMEDOUT; - - rtsx_pci_init_cmd(pcr); - - rtsx_pci_add_cmd(pcr, READ_REG_CMD, PHYDATA0, 0, 0); - rtsx_pci_add_cmd(pcr, READ_REG_CMD, PHYDATA1, 0, 0); - - err = rtsx_pci_send_cmd(pcr, 100); - if (err < 0) - return err; - - ptr = rtsx_pci_get_cmd_data(pcr); - data = ((u16)ptr[1] << 8) | ptr[0]; - - if (val) - *val = data; - - return 0; -} - -int rtsx_pci_read_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 *val) -{ - if (pcr->ops->read_phy) - return pcr->ops->read_phy(pcr, addr, val); - - return __rtsx_pci_read_phy_register(pcr, addr, val); -} -EXPORT_SYMBOL_GPL(rtsx_pci_read_phy_register); - -void rtsx_pci_stop_cmd(struct rtsx_pcr *pcr) -{ - rtsx_pci_writel(pcr, RTSX_HCBCTLR, STOP_CMD); - rtsx_pci_writel(pcr, RTSX_HDBCTLR, STOP_DMA); - - rtsx_pci_write_register(pcr, DMACTL, 0x80, 0x80); - rtsx_pci_write_register(pcr, RBCTL, 0x80, 0x80); -} -EXPORT_SYMBOL_GPL(rtsx_pci_stop_cmd); - -void rtsx_pci_add_cmd(struct rtsx_pcr *pcr, - u8 cmd_type, u16 reg_addr, u8 mask, u8 data) -{ - unsigned long flags; - u32 val = 0; - u32 *ptr = (u32 *)(pcr->host_cmds_ptr); - - val |= (u32)(cmd_type & 0x03) << 30; - val |= (u32)(reg_addr & 0x3FFF) << 16; - val |= (u32)mask << 8; - val |= (u32)data; - - spin_lock_irqsave(&pcr->lock, flags); - ptr += pcr->ci; - if (pcr->ci < (HOST_CMDS_BUF_LEN / 4)) { - put_unaligned_le32(val, ptr); - ptr++; - pcr->ci++; - } - spin_unlock_irqrestore(&pcr->lock, flags); -} -EXPORT_SYMBOL_GPL(rtsx_pci_add_cmd); - -void rtsx_pci_send_cmd_no_wait(struct rtsx_pcr *pcr) -{ - u32 val = 1 << 31; - - rtsx_pci_writel(pcr, RTSX_HCBAR, pcr->host_cmds_addr); - - val |= (u32)(pcr->ci * 4) & 0x00FFFFFF; - /* Hardware Auto Response */ - val |= 0x40000000; - rtsx_pci_writel(pcr, RTSX_HCBCTLR, val); -} -EXPORT_SYMBOL_GPL(rtsx_pci_send_cmd_no_wait); - -int rtsx_pci_send_cmd(struct rtsx_pcr *pcr, int timeout) -{ - struct completion trans_done; - u32 val = 1 << 31; - long timeleft; - unsigned long flags; - int err = 0; - - spin_lock_irqsave(&pcr->lock, flags); - - /* set up data structures for the wakeup system */ - pcr->done = &trans_done; - pcr->trans_result = TRANS_NOT_READY; - init_completion(&trans_done); - - rtsx_pci_writel(pcr, RTSX_HCBAR, pcr->host_cmds_addr); - - val |= (u32)(pcr->ci * 4) & 0x00FFFFFF; - /* Hardware Auto Response */ - val |= 0x40000000; - rtsx_pci_writel(pcr, RTSX_HCBCTLR, val); - - spin_unlock_irqrestore(&pcr->lock, flags); - - /* Wait for TRANS_OK_INT */ - timeleft = wait_for_completion_interruptible_timeout( - &trans_done, msecs_to_jiffies(timeout)); - if (timeleft <= 0) { - pcr_dbg(pcr, "Timeout (%s %d)\n", __func__, __LINE__); - err = -ETIMEDOUT; - goto finish_send_cmd; - } - - spin_lock_irqsave(&pcr->lock, flags); - if (pcr->trans_result == TRANS_RESULT_FAIL) - err = -EINVAL; - else if (pcr->trans_result == TRANS_RESULT_OK) - err = 0; - else if (pcr->trans_result == TRANS_NO_DEVICE) - err = -ENODEV; - spin_unlock_irqrestore(&pcr->lock, flags); - -finish_send_cmd: - spin_lock_irqsave(&pcr->lock, flags); - pcr->done = NULL; - spin_unlock_irqrestore(&pcr->lock, flags); - - if ((err < 0) && (err != -ENODEV)) - rtsx_pci_stop_cmd(pcr); - - if (pcr->finish_me) - complete(pcr->finish_me); - - return err; -} -EXPORT_SYMBOL_GPL(rtsx_pci_send_cmd); - -static void rtsx_pci_add_sg_tbl(struct rtsx_pcr *pcr, - dma_addr_t addr, unsigned int len, int end) -{ - u64 *ptr = (u64 *)(pcr->host_sg_tbl_ptr) + pcr->sgi; - u64 val; - u8 option = SG_VALID | SG_TRANS_DATA; - - pcr_dbg(pcr, "DMA addr: 0x%x, Len: 0x%x\n", (unsigned int)addr, len); - - if (end) - option |= SG_END; - val = ((u64)addr << 32) | ((u64)len << 12) | option; - - put_unaligned_le64(val, ptr); - pcr->sgi++; -} - -int rtsx_pci_transfer_data(struct rtsx_pcr *pcr, struct scatterlist *sglist, - int num_sg, bool read, int timeout) -{ - int err = 0, count; - - pcr_dbg(pcr, "--> %s: num_sg = %d\n", __func__, num_sg); - count = rtsx_pci_dma_map_sg(pcr, sglist, num_sg, read); - if (count < 1) - return -EINVAL; - pcr_dbg(pcr, "DMA mapping count: %d\n", count); - - err = rtsx_pci_dma_transfer(pcr, sglist, count, read, timeout); - - rtsx_pci_dma_unmap_sg(pcr, sglist, num_sg, read); - - return err; -} -EXPORT_SYMBOL_GPL(rtsx_pci_transfer_data); - -int rtsx_pci_dma_map_sg(struct rtsx_pcr *pcr, struct scatterlist *sglist, - int num_sg, bool read) -{ - enum dma_data_direction dir = read ? DMA_FROM_DEVICE : DMA_TO_DEVICE; - - if (pcr->remove_pci) - return -EINVAL; - - if ((sglist == NULL) || (num_sg <= 0)) - return -EINVAL; - - return dma_map_sg(&(pcr->pci->dev), sglist, num_sg, dir); -} -EXPORT_SYMBOL_GPL(rtsx_pci_dma_map_sg); - -void rtsx_pci_dma_unmap_sg(struct rtsx_pcr *pcr, struct scatterlist *sglist, - int num_sg, bool read) -{ - enum dma_data_direction dir = read ? DMA_FROM_DEVICE : DMA_TO_DEVICE; - - dma_unmap_sg(&(pcr->pci->dev), sglist, num_sg, dir); -} -EXPORT_SYMBOL_GPL(rtsx_pci_dma_unmap_sg); - -int rtsx_pci_dma_transfer(struct rtsx_pcr *pcr, struct scatterlist *sglist, - int count, bool read, int timeout) -{ - struct completion trans_done; - struct scatterlist *sg; - dma_addr_t addr; - long timeleft; - unsigned long flags; - unsigned int len; - int i, err = 0; - u32 val; - u8 dir = read ? DEVICE_TO_HOST : HOST_TO_DEVICE; - - if (pcr->remove_pci) - return -ENODEV; - - if ((sglist == NULL) || (count < 1)) - return -EINVAL; - - val = ((u32)(dir & 0x01) << 29) | TRIG_DMA | ADMA_MODE; - pcr->sgi = 0; - for_each_sg(sglist, sg, count, i) { - addr = sg_dma_address(sg); - len = sg_dma_len(sg); - rtsx_pci_add_sg_tbl(pcr, addr, len, i == count - 1); - } - - spin_lock_irqsave(&pcr->lock, flags); - - pcr->done = &trans_done; - pcr->trans_result = TRANS_NOT_READY; - init_completion(&trans_done); - rtsx_pci_writel(pcr, RTSX_HDBAR, pcr->host_sg_tbl_addr); - rtsx_pci_writel(pcr, RTSX_HDBCTLR, val); - - spin_unlock_irqrestore(&pcr->lock, flags); - - timeleft = wait_for_completion_interruptible_timeout( - &trans_done, msecs_to_jiffies(timeout)); - if (timeleft <= 0) { - pcr_dbg(pcr, "Timeout (%s %d)\n", __func__, __LINE__); - err = -ETIMEDOUT; - goto out; - } - - spin_lock_irqsave(&pcr->lock, flags); - if (pcr->trans_result == TRANS_RESULT_FAIL) { - err = -EILSEQ; - if (pcr->dma_error_count < RTS_MAX_TIMES_FREQ_REDUCTION) - pcr->dma_error_count++; - } - - else if (pcr->trans_result == TRANS_NO_DEVICE) - err = -ENODEV; - spin_unlock_irqrestore(&pcr->lock, flags); - -out: - spin_lock_irqsave(&pcr->lock, flags); - pcr->done = NULL; - spin_unlock_irqrestore(&pcr->lock, flags); - - if ((err < 0) && (err != -ENODEV)) - rtsx_pci_stop_cmd(pcr); - - if (pcr->finish_me) - complete(pcr->finish_me); - - return err; -} -EXPORT_SYMBOL_GPL(rtsx_pci_dma_transfer); - -int rtsx_pci_read_ppbuf(struct rtsx_pcr *pcr, u8 *buf, int buf_len) -{ - int err; - int i, j; - u16 reg; - u8 *ptr; - - if (buf_len > 512) - buf_len = 512; - - ptr = buf; - reg = PPBUF_BASE2; - for (i = 0; i < buf_len / 256; i++) { - rtsx_pci_init_cmd(pcr); - - for (j = 0; j < 256; j++) - rtsx_pci_add_cmd(pcr, READ_REG_CMD, reg++, 0, 0); - - err = rtsx_pci_send_cmd(pcr, 250); - if (err < 0) - return err; - - memcpy(ptr, rtsx_pci_get_cmd_data(pcr), 256); - ptr += 256; - } - - if (buf_len % 256) { - rtsx_pci_init_cmd(pcr); - - for (j = 0; j < buf_len % 256; j++) - rtsx_pci_add_cmd(pcr, READ_REG_CMD, reg++, 0, 0); - - err = rtsx_pci_send_cmd(pcr, 250); - if (err < 0) - return err; - } - - memcpy(ptr, rtsx_pci_get_cmd_data(pcr), buf_len % 256); - - return 0; -} -EXPORT_SYMBOL_GPL(rtsx_pci_read_ppbuf); - -int rtsx_pci_write_ppbuf(struct rtsx_pcr *pcr, u8 *buf, int buf_len) -{ - int err; - int i, j; - u16 reg; - u8 *ptr; - - if (buf_len > 512) - buf_len = 512; - - ptr = buf; - reg = PPBUF_BASE2; - for (i = 0; i < buf_len / 256; i++) { - rtsx_pci_init_cmd(pcr); - - for (j = 0; j < 256; j++) { - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, - reg++, 0xFF, *ptr); - ptr++; - } - - err = rtsx_pci_send_cmd(pcr, 250); - if (err < 0) - return err; - } - - if (buf_len % 256) { - rtsx_pci_init_cmd(pcr); - - for (j = 0; j < buf_len % 256; j++) { - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, - reg++, 0xFF, *ptr); - ptr++; - } - - err = rtsx_pci_send_cmd(pcr, 250); - if (err < 0) - return err; - } - - return 0; -} -EXPORT_SYMBOL_GPL(rtsx_pci_write_ppbuf); - -static int rtsx_pci_set_pull_ctl(struct rtsx_pcr *pcr, const u32 *tbl) -{ - rtsx_pci_init_cmd(pcr); - - while (*tbl & 0xFFFF0000) { - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, - (u16)(*tbl >> 16), 0xFF, (u8)(*tbl)); - tbl++; - } - - return rtsx_pci_send_cmd(pcr, 100); -} - -int rtsx_pci_card_pull_ctl_enable(struct rtsx_pcr *pcr, int card) -{ - const u32 *tbl; - - if (card == RTSX_SD_CARD) - tbl = pcr->sd_pull_ctl_enable_tbl; - else if (card == RTSX_MS_CARD) - tbl = pcr->ms_pull_ctl_enable_tbl; - else - return -EINVAL; - - return rtsx_pci_set_pull_ctl(pcr, tbl); -} -EXPORT_SYMBOL_GPL(rtsx_pci_card_pull_ctl_enable); - -int rtsx_pci_card_pull_ctl_disable(struct rtsx_pcr *pcr, int card) -{ - const u32 *tbl; - - if (card == RTSX_SD_CARD) - tbl = pcr->sd_pull_ctl_disable_tbl; - else if (card == RTSX_MS_CARD) - tbl = pcr->ms_pull_ctl_disable_tbl; - else - return -EINVAL; - - - return rtsx_pci_set_pull_ctl(pcr, tbl); -} -EXPORT_SYMBOL_GPL(rtsx_pci_card_pull_ctl_disable); - -static void rtsx_pci_enable_bus_int(struct rtsx_pcr *pcr) -{ - pcr->bier = TRANS_OK_INT_EN | TRANS_FAIL_INT_EN | SD_INT_EN; - - if (pcr->num_slots > 1) - pcr->bier |= MS_INT_EN; - - /* Enable Bus Interrupt */ - rtsx_pci_writel(pcr, RTSX_BIER, pcr->bier); - - pcr_dbg(pcr, "RTSX_BIER: 0x%08x\n", pcr->bier); -} - -static inline u8 double_ssc_depth(u8 depth) -{ - return ((depth > 1) ? (depth - 1) : depth); -} - -static u8 revise_ssc_depth(u8 ssc_depth, u8 div) -{ - if (div > CLK_DIV_1) { - if (ssc_depth > (div - 1)) - ssc_depth -= (div - 1); - else - ssc_depth = SSC_DEPTH_4M; - } - - return ssc_depth; -} - -int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock, - u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk) -{ - int err, clk; - u8 n, clk_divider, mcu_cnt, div; - static const u8 depth[] = { - [RTSX_SSC_DEPTH_4M] = SSC_DEPTH_4M, - [RTSX_SSC_DEPTH_2M] = SSC_DEPTH_2M, - [RTSX_SSC_DEPTH_1M] = SSC_DEPTH_1M, - [RTSX_SSC_DEPTH_500K] = SSC_DEPTH_500K, - [RTSX_SSC_DEPTH_250K] = SSC_DEPTH_250K, - }; - - if (initial_mode) { - /* We use 250k(around) here, in initial stage */ - clk_divider = SD_CLK_DIVIDE_128; - card_clock = 30000000; - } else { - clk_divider = SD_CLK_DIVIDE_0; - } - err = rtsx_pci_write_register(pcr, SD_CFG1, - SD_CLK_DIVIDE_MASK, clk_divider); - if (err < 0) - return err; - - /* Reduce card clock by 20MHz each time a DMA transfer error occurs */ - if (card_clock == UHS_SDR104_MAX_DTR && - pcr->dma_error_count && - PCI_PID(pcr) == RTS5227_DEVICE_ID) - card_clock = UHS_SDR104_MAX_DTR - - (pcr->dma_error_count * 20000000); - - card_clock /= 1000000; - pcr_dbg(pcr, "Switch card clock to %dMHz\n", card_clock); - - clk = card_clock; - if (!initial_mode && double_clk) - clk = card_clock * 2; - pcr_dbg(pcr, "Internal SSC clock: %dMHz (cur_clock = %d)\n", - clk, pcr->cur_clock); - - if (clk == pcr->cur_clock) - return 0; - - if (pcr->ops->conv_clk_and_div_n) - n = (u8)pcr->ops->conv_clk_and_div_n(clk, CLK_TO_DIV_N); - else - n = (u8)(clk - 2); - if ((clk <= 2) || (n > MAX_DIV_N_PCR)) - return -EINVAL; - - mcu_cnt = (u8)(125/clk + 3); - if (mcu_cnt > 15) - mcu_cnt = 15; - - /* Make sure that the SSC clock div_n is not less than MIN_DIV_N_PCR */ - div = CLK_DIV_1; - while ((n < MIN_DIV_N_PCR) && (div < CLK_DIV_8)) { - if (pcr->ops->conv_clk_and_div_n) { - int dbl_clk = pcr->ops->conv_clk_and_div_n(n, - DIV_N_TO_CLK) * 2; - n = (u8)pcr->ops->conv_clk_and_div_n(dbl_clk, - CLK_TO_DIV_N); - } else { - n = (n + 2) * 2 - 2; - } - div++; - } - pcr_dbg(pcr, "n = %d, div = %d\n", n, div); - - ssc_depth = depth[ssc_depth]; - if (double_clk) - ssc_depth = double_ssc_depth(ssc_depth); - - ssc_depth = revise_ssc_depth(ssc_depth, div); - pcr_dbg(pcr, "ssc_depth = %d\n", ssc_depth); - - rtsx_pci_init_cmd(pcr); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, - CLK_LOW_FREQ, CLK_LOW_FREQ); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_DIV, - 0xFF, (div << 4) | mcu_cnt); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, 0); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL2, - SSC_DEPTH_MASK, ssc_depth); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_DIV_N_0, 0xFF, n); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, SSC_RSTB); - if (vpclk) { - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK0_CTL, - PHASE_NOT_RESET, 0); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK0_CTL, - PHASE_NOT_RESET, PHASE_NOT_RESET); - } - - err = rtsx_pci_send_cmd(pcr, 2000); - if (err < 0) - return err; - - /* Wait SSC clock stable */ - udelay(10); - err = rtsx_pci_write_register(pcr, CLK_CTL, CLK_LOW_FREQ, 0); - if (err < 0) - return err; - - pcr->cur_clock = clk; - return 0; -} -EXPORT_SYMBOL_GPL(rtsx_pci_switch_clock); - -int rtsx_pci_card_power_on(struct rtsx_pcr *pcr, int card) -{ - if (pcr->ops->card_power_on) - return pcr->ops->card_power_on(pcr, card); - - return 0; -} -EXPORT_SYMBOL_GPL(rtsx_pci_card_power_on); - -int rtsx_pci_card_power_off(struct rtsx_pcr *pcr, int card) -{ - if (pcr->ops->card_power_off) - return pcr->ops->card_power_off(pcr, card); - - return 0; -} -EXPORT_SYMBOL_GPL(rtsx_pci_card_power_off); - -int rtsx_pci_card_exclusive_check(struct rtsx_pcr *pcr, int card) -{ - static const unsigned int cd_mask[] = { - [RTSX_SD_CARD] = SD_EXIST, - [RTSX_MS_CARD] = MS_EXIST - }; - - if (!(pcr->flags & PCR_MS_PMOS)) { - /* When using single PMOS, accessing card is not permitted - * if the existing card is not the designated one. - */ - if (pcr->card_exist & (~cd_mask[card])) - return -EIO; - } - - return 0; -} -EXPORT_SYMBOL_GPL(rtsx_pci_card_exclusive_check); - -int rtsx_pci_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) -{ - if (pcr->ops->switch_output_voltage) - return pcr->ops->switch_output_voltage(pcr, voltage); - - return 0; -} -EXPORT_SYMBOL_GPL(rtsx_pci_switch_output_voltage); - -unsigned int rtsx_pci_card_exist(struct rtsx_pcr *pcr) -{ - unsigned int val; - - val = rtsx_pci_readl(pcr, RTSX_BIPR); - if (pcr->ops->cd_deglitch) - val = pcr->ops->cd_deglitch(pcr); - - return val; -} -EXPORT_SYMBOL_GPL(rtsx_pci_card_exist); - -void rtsx_pci_complete_unfinished_transfer(struct rtsx_pcr *pcr) -{ - struct completion finish; - - pcr->finish_me = &finish; - init_completion(&finish); - - if (pcr->done) - complete(pcr->done); - - if (!pcr->remove_pci) - rtsx_pci_stop_cmd(pcr); - - wait_for_completion_interruptible_timeout(&finish, - msecs_to_jiffies(2)); - pcr->finish_me = NULL; -} -EXPORT_SYMBOL_GPL(rtsx_pci_complete_unfinished_transfer); - -static void rtsx_pci_card_detect(struct work_struct *work) -{ - struct delayed_work *dwork; - struct rtsx_pcr *pcr; - unsigned long flags; - unsigned int card_detect = 0, card_inserted, card_removed; - u32 irq_status; - - dwork = to_delayed_work(work); - pcr = container_of(dwork, struct rtsx_pcr, carddet_work); - - pcr_dbg(pcr, "--> %s\n", __func__); - - mutex_lock(&pcr->pcr_mutex); - spin_lock_irqsave(&pcr->lock, flags); - - irq_status = rtsx_pci_readl(pcr, RTSX_BIPR); - pcr_dbg(pcr, "irq_status: 0x%08x\n", irq_status); - - irq_status &= CARD_EXIST; - card_inserted = pcr->card_inserted & irq_status; - card_removed = pcr->card_removed; - pcr->card_inserted = 0; - pcr->card_removed = 0; - - spin_unlock_irqrestore(&pcr->lock, flags); - - if (card_inserted || card_removed) { - pcr_dbg(pcr, "card_inserted: 0x%x, card_removed: 0x%x\n", - card_inserted, card_removed); - - if (pcr->ops->cd_deglitch) - card_inserted = pcr->ops->cd_deglitch(pcr); - - card_detect = card_inserted | card_removed; - - pcr->card_exist |= card_inserted; - pcr->card_exist &= ~card_removed; - } - - mutex_unlock(&pcr->pcr_mutex); - - if ((card_detect & SD_EXIST) && pcr->slots[RTSX_SD_CARD].card_event) - pcr->slots[RTSX_SD_CARD].card_event( - pcr->slots[RTSX_SD_CARD].p_dev); - if ((card_detect & MS_EXIST) && pcr->slots[RTSX_MS_CARD].card_event) - pcr->slots[RTSX_MS_CARD].card_event( - pcr->slots[RTSX_MS_CARD].p_dev); -} - -static irqreturn_t rtsx_pci_isr(int irq, void *dev_id) -{ - struct rtsx_pcr *pcr = dev_id; - u32 int_reg; - - if (!pcr) - return IRQ_NONE; - - spin_lock(&pcr->lock); - - int_reg = rtsx_pci_readl(pcr, RTSX_BIPR); - /* Clear interrupt flag */ - rtsx_pci_writel(pcr, RTSX_BIPR, int_reg); - if ((int_reg & pcr->bier) == 0) { - spin_unlock(&pcr->lock); - return IRQ_NONE; - } - if (int_reg == 0xFFFFFFFF) { - spin_unlock(&pcr->lock); - return IRQ_HANDLED; - } - - int_reg &= (pcr->bier | 0x7FFFFF); - - if (int_reg & SD_INT) { - if (int_reg & SD_EXIST) { - pcr->card_inserted |= SD_EXIST; - } else { - pcr->card_removed |= SD_EXIST; - pcr->card_inserted &= ~SD_EXIST; - } - pcr->dma_error_count = 0; - } - - if (int_reg & MS_INT) { - if (int_reg & MS_EXIST) { - pcr->card_inserted |= MS_EXIST; - } else { - pcr->card_removed |= MS_EXIST; - pcr->card_inserted &= ~MS_EXIST; - } - } - - if (int_reg & (NEED_COMPLETE_INT | DELINK_INT)) { - if (int_reg & (TRANS_FAIL_INT | DELINK_INT)) { - pcr->trans_result = TRANS_RESULT_FAIL; - if (pcr->done) - complete(pcr->done); - } else if (int_reg & TRANS_OK_INT) { - pcr->trans_result = TRANS_RESULT_OK; - if (pcr->done) - complete(pcr->done); - } - } - - if (pcr->card_inserted || pcr->card_removed) - schedule_delayed_work(&pcr->carddet_work, - msecs_to_jiffies(200)); - - spin_unlock(&pcr->lock); - return IRQ_HANDLED; -} - -static int rtsx_pci_acquire_irq(struct rtsx_pcr *pcr) -{ - pcr_dbg(pcr, "%s: pcr->msi_en = %d, pci->irq = %d\n", - __func__, pcr->msi_en, pcr->pci->irq); - - if (request_irq(pcr->pci->irq, rtsx_pci_isr, - pcr->msi_en ? 0 : IRQF_SHARED, - DRV_NAME_RTSX_PCI, pcr)) { - dev_err(&(pcr->pci->dev), - "rtsx_sdmmc: unable to grab IRQ %d, disabling device\n", - pcr->pci->irq); - return -1; - } - - pcr->irq = pcr->pci->irq; - pci_intx(pcr->pci, !pcr->msi_en); - - return 0; -} - -static void rtsx_enable_aspm(struct rtsx_pcr *pcr) -{ - if (pcr->ops->set_aspm) - pcr->ops->set_aspm(pcr, true); - else - rtsx_comm_set_aspm(pcr, true); -} - -static void rtsx_comm_pm_power_saving(struct rtsx_pcr *pcr) -{ - struct rtsx_cr_option *option = &pcr->option; - - if (option->ltr_enabled) { - u32 latency = option->ltr_l1off_latency; - - if (rtsx_check_dev_flag(pcr, L1_SNOOZE_TEST_EN)) - mdelay(option->l1_snooze_delay); - - rtsx_set_ltr_latency(pcr, latency); - } - - if (rtsx_check_dev_flag(pcr, LTR_L1SS_PWR_GATE_EN)) - rtsx_set_l1off_sub_cfg_d0(pcr, 0); - - rtsx_enable_aspm(pcr); -} - -void rtsx_pm_power_saving(struct rtsx_pcr *pcr) -{ - if (pcr->ops->power_saving) - pcr->ops->power_saving(pcr); - else - rtsx_comm_pm_power_saving(pcr); -} - -static void rtsx_pci_idle_work(struct work_struct *work) -{ - struct delayed_work *dwork = to_delayed_work(work); - struct rtsx_pcr *pcr = container_of(dwork, struct rtsx_pcr, idle_work); - - pcr_dbg(pcr, "--> %s\n", __func__); - - mutex_lock(&pcr->pcr_mutex); - - pcr->state = PDEV_STAT_IDLE; - - if (pcr->ops->disable_auto_blink) - pcr->ops->disable_auto_blink(pcr); - if (pcr->ops->turn_off_led) - pcr->ops->turn_off_led(pcr); - - rtsx_pm_power_saving(pcr); - - mutex_unlock(&pcr->pcr_mutex); -} - -#ifdef CONFIG_PM -static void rtsx_pci_power_off(struct rtsx_pcr *pcr, u8 pm_state) -{ - if (pcr->ops->turn_off_led) - pcr->ops->turn_off_led(pcr); - - rtsx_pci_writel(pcr, RTSX_BIER, 0); - pcr->bier = 0; - - rtsx_pci_write_register(pcr, PETXCFG, 0x08, 0x08); - rtsx_pci_write_register(pcr, HOST_SLEEP_STATE, 0x03, pm_state); - - if (pcr->ops->force_power_down) - pcr->ops->force_power_down(pcr, pm_state); -} -#endif - -static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) -{ - int err; - - pcr->pcie_cap = pci_find_capability(pcr->pci, PCI_CAP_ID_EXP); - rtsx_pci_writel(pcr, RTSX_HCBAR, pcr->host_cmds_addr); - - rtsx_pci_enable_bus_int(pcr); - - /* Power on SSC */ - err = rtsx_pci_write_register(pcr, FPDCTL, SSC_POWER_DOWN, 0); - if (err < 0) - return err; - - /* Wait SSC power stable */ - udelay(200); - - rtsx_pci_disable_aspm(pcr); - if (pcr->ops->optimize_phy) { - err = pcr->ops->optimize_phy(pcr); - if (err < 0) - return err; - } - - rtsx_pci_init_cmd(pcr); - - /* Set mcu_cnt to 7 to ensure data can be sampled properly */ - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_DIV, 0x07, 0x07); - - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, HOST_SLEEP_STATE, 0x03, 0x00); - /* Disable card clock */ - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_EN, 0x1E, 0); - /* Reset delink mode */ - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CHANGE_LINK_STATE, 0x0A, 0); - /* Card driving select */ - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DRIVE_SEL, - 0xFF, pcr->card_drive_sel); - /* Enable SSC Clock */ - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL1, - 0xFF, SSC_8X_EN | SSC_SEL_4M); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL2, 0xFF, 0x12); - /* Disable cd_pwr_save */ - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CHANGE_LINK_STATE, 0x16, 0x10); - /* Clear Link Ready Interrupt */ - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, IRQSTAT0, - LINK_RDY_INT, LINK_RDY_INT); - /* Enlarge the estimation window of PERST# glitch - * to reduce the chance of invalid card interrupt - */ - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PERST_GLITCH_WIDTH, 0xFF, 0x80); - /* Update RC oscillator to 400k - * bit[0] F_HIGH: for RC oscillator, Rst_value is 1'b1 - * 1: 2M 0: 400k - */ - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, RCCTL, 0x01, 0x00); - /* Set interrupt write clear - * bit 1: U_elbi_if_rd_clr_en - * 1: Enable ELBI interrupt[31:22] & [7:0] flag read clear - * 0: ELBI interrupt flag[31:22] & [7:0] only can be write clear - */ - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, NFTS_TX_CTRL, 0x02, 0); - - err = rtsx_pci_send_cmd(pcr, 100); - if (err < 0) - return err; - - switch (PCI_PID(pcr)) { - case PID_5250: - case PID_524A: - case PID_525A: - rtsx_pci_write_register(pcr, PM_CLK_FORCE_CTL, 1, 1); - break; - default: - break; - } - - /* Enable clk_request_n to enable clock power management */ - rtsx_pci_write_config_byte(pcr, pcr->pcie_cap + PCI_EXP_LNKCTL + 1, 1); - /* Enter L1 when host tx idle */ - rtsx_pci_write_config_byte(pcr, 0x70F, 0x5B); - - if (pcr->ops->extra_init_hw) { - err = pcr->ops->extra_init_hw(pcr); - if (err < 0) - return err; - } - - /* No CD interrupt if probing driver with card inserted. - * So we need to initialize pcr->card_exist here. - */ - if (pcr->ops->cd_deglitch) - pcr->card_exist = pcr->ops->cd_deglitch(pcr); - else - pcr->card_exist = rtsx_pci_readl(pcr, RTSX_BIPR) & CARD_EXIST; - - return 0; -} - -static int rtsx_pci_init_chip(struct rtsx_pcr *pcr) -{ - int err; - - spin_lock_init(&pcr->lock); - mutex_init(&pcr->pcr_mutex); - - switch (PCI_PID(pcr)) { - default: - case 0x5209: - rts5209_init_params(pcr); - break; - - case 0x5229: - rts5229_init_params(pcr); - break; - - case 0x5289: - rtl8411_init_params(pcr); - break; - - case 0x5227: - rts5227_init_params(pcr); - break; - - case 0x522A: - rts522a_init_params(pcr); - break; - - case 0x5249: - rts5249_init_params(pcr); - break; - - case 0x524A: - rts524a_init_params(pcr); - break; - - case 0x525A: - rts525a_init_params(pcr); - break; - - case 0x5287: - rtl8411b_init_params(pcr); - break; - - case 0x5286: - rtl8402_init_params(pcr); - break; - } - - pcr_dbg(pcr, "PID: 0x%04x, IC version: 0x%02x\n", - PCI_PID(pcr), pcr->ic_version); - - pcr->slots = kcalloc(pcr->num_slots, sizeof(struct rtsx_slot), - GFP_KERNEL); - if (!pcr->slots) - return -ENOMEM; - - if (pcr->ops->fetch_vendor_settings) - pcr->ops->fetch_vendor_settings(pcr); - - pcr_dbg(pcr, "pcr->aspm_en = 0x%x\n", pcr->aspm_en); - pcr_dbg(pcr, "pcr->sd30_drive_sel_1v8 = 0x%x\n", - pcr->sd30_drive_sel_1v8); - pcr_dbg(pcr, "pcr->sd30_drive_sel_3v3 = 0x%x\n", - pcr->sd30_drive_sel_3v3); - pcr_dbg(pcr, "pcr->card_drive_sel = 0x%x\n", - pcr->card_drive_sel); - pcr_dbg(pcr, "pcr->flags = 0x%x\n", pcr->flags); - - pcr->state = PDEV_STAT_IDLE; - err = rtsx_pci_init_hw(pcr); - if (err < 0) { - kfree(pcr->slots); - return err; - } - - return 0; -} - -static int rtsx_pci_probe(struct pci_dev *pcidev, - const struct pci_device_id *id) -{ - struct rtsx_pcr *pcr; - struct pcr_handle *handle; - u32 base, len; - int ret, i, bar = 0; - - dev_dbg(&(pcidev->dev), - ": Realtek PCI-E Card Reader found at %s [%04x:%04x] (rev %x)\n", - pci_name(pcidev), (int)pcidev->vendor, (int)pcidev->device, - (int)pcidev->revision); - - ret = pci_set_dma_mask(pcidev, DMA_BIT_MASK(32)); - if (ret < 0) - return ret; - - ret = pci_enable_device(pcidev); - if (ret) - return ret; - - ret = pci_request_regions(pcidev, DRV_NAME_RTSX_PCI); - if (ret) - goto disable; - - pcr = kzalloc(sizeof(*pcr), GFP_KERNEL); - if (!pcr) { - ret = -ENOMEM; - goto release_pci; - } - - handle = kzalloc(sizeof(*handle), GFP_KERNEL); - if (!handle) { - ret = -ENOMEM; - goto free_pcr; - } - handle->pcr = pcr; - - idr_preload(GFP_KERNEL); - spin_lock(&rtsx_pci_lock); - ret = idr_alloc(&rtsx_pci_idr, pcr, 0, 0, GFP_NOWAIT); - if (ret >= 0) - pcr->id = ret; - spin_unlock(&rtsx_pci_lock); - idr_preload_end(); - if (ret < 0) - goto free_handle; - - pcr->pci = pcidev; - dev_set_drvdata(&pcidev->dev, handle); - - if (CHK_PCI_PID(pcr, 0x525A)) - bar = 1; - len = pci_resource_len(pcidev, bar); - base = pci_resource_start(pcidev, bar); - pcr->remap_addr = ioremap_nocache(base, len); - if (!pcr->remap_addr) { - ret = -ENOMEM; - goto free_handle; - } - - pcr->rtsx_resv_buf = dma_alloc_coherent(&(pcidev->dev), - RTSX_RESV_BUF_LEN, &(pcr->rtsx_resv_buf_addr), - GFP_KERNEL); - if (pcr->rtsx_resv_buf == NULL) { - ret = -ENXIO; - goto unmap; - } - pcr->host_cmds_ptr = pcr->rtsx_resv_buf; - pcr->host_cmds_addr = pcr->rtsx_resv_buf_addr; - pcr->host_sg_tbl_ptr = pcr->rtsx_resv_buf + HOST_CMDS_BUF_LEN; - pcr->host_sg_tbl_addr = pcr->rtsx_resv_buf_addr + HOST_CMDS_BUF_LEN; - - pcr->card_inserted = 0; - pcr->card_removed = 0; - INIT_DELAYED_WORK(&pcr->carddet_work, rtsx_pci_card_detect); - INIT_DELAYED_WORK(&pcr->idle_work, rtsx_pci_idle_work); - - pcr->msi_en = msi_en; - if (pcr->msi_en) { - ret = pci_enable_msi(pcidev); - if (ret) - pcr->msi_en = false; - } - - ret = rtsx_pci_acquire_irq(pcr); - if (ret < 0) - goto disable_msi; - - pci_set_master(pcidev); - synchronize_irq(pcr->irq); - - ret = rtsx_pci_init_chip(pcr); - if (ret < 0) - goto disable_irq; - - for (i = 0; i < ARRAY_SIZE(rtsx_pcr_cells); i++) { - rtsx_pcr_cells[i].platform_data = handle; - rtsx_pcr_cells[i].pdata_size = sizeof(*handle); - } - ret = mfd_add_devices(&pcidev->dev, pcr->id, rtsx_pcr_cells, - ARRAY_SIZE(rtsx_pcr_cells), NULL, 0, NULL); - if (ret < 0) - goto disable_irq; - - schedule_delayed_work(&pcr->idle_work, msecs_to_jiffies(200)); - - return 0; - -disable_irq: - free_irq(pcr->irq, (void *)pcr); -disable_msi: - if (pcr->msi_en) - pci_disable_msi(pcr->pci); - dma_free_coherent(&(pcr->pci->dev), RTSX_RESV_BUF_LEN, - pcr->rtsx_resv_buf, pcr->rtsx_resv_buf_addr); -unmap: - iounmap(pcr->remap_addr); -free_handle: - kfree(handle); -free_pcr: - kfree(pcr); -release_pci: - pci_release_regions(pcidev); -disable: - pci_disable_device(pcidev); - - return ret; -} - -static void rtsx_pci_remove(struct pci_dev *pcidev) -{ - struct pcr_handle *handle = pci_get_drvdata(pcidev); - struct rtsx_pcr *pcr = handle->pcr; - - pcr->remove_pci = true; - - /* Disable interrupts at the pcr level */ - spin_lock_irq(&pcr->lock); - rtsx_pci_writel(pcr, RTSX_BIER, 0); - pcr->bier = 0; - spin_unlock_irq(&pcr->lock); - - cancel_delayed_work_sync(&pcr->carddet_work); - cancel_delayed_work_sync(&pcr->idle_work); - - mfd_remove_devices(&pcidev->dev); - - dma_free_coherent(&(pcr->pci->dev), RTSX_RESV_BUF_LEN, - pcr->rtsx_resv_buf, pcr->rtsx_resv_buf_addr); - free_irq(pcr->irq, (void *)pcr); - if (pcr->msi_en) - pci_disable_msi(pcr->pci); - iounmap(pcr->remap_addr); - - pci_release_regions(pcidev); - pci_disable_device(pcidev); - - spin_lock(&rtsx_pci_lock); - idr_remove(&rtsx_pci_idr, pcr->id); - spin_unlock(&rtsx_pci_lock); - - kfree(pcr->slots); - kfree(pcr); - kfree(handle); - - dev_dbg(&(pcidev->dev), - ": Realtek PCI-E Card Reader at %s [%04x:%04x] has been removed\n", - pci_name(pcidev), (int)pcidev->vendor, (int)pcidev->device); -} - -#ifdef CONFIG_PM - -static int rtsx_pci_suspend(struct pci_dev *pcidev, pm_message_t state) -{ - struct pcr_handle *handle; - struct rtsx_pcr *pcr; - - dev_dbg(&(pcidev->dev), "--> %s\n", __func__); - - handle = pci_get_drvdata(pcidev); - pcr = handle->pcr; - - cancel_delayed_work(&pcr->carddet_work); - cancel_delayed_work(&pcr->idle_work); - - mutex_lock(&pcr->pcr_mutex); - - rtsx_pci_power_off(pcr, HOST_ENTER_S3); - - pci_save_state(pcidev); - pci_enable_wake(pcidev, pci_choose_state(pcidev, state), 0); - pci_disable_device(pcidev); - pci_set_power_state(pcidev, pci_choose_state(pcidev, state)); - - mutex_unlock(&pcr->pcr_mutex); - return 0; -} - -static int rtsx_pci_resume(struct pci_dev *pcidev) -{ - struct pcr_handle *handle; - struct rtsx_pcr *pcr; - int ret = 0; - - dev_dbg(&(pcidev->dev), "--> %s\n", __func__); - - handle = pci_get_drvdata(pcidev); - pcr = handle->pcr; - - mutex_lock(&pcr->pcr_mutex); - - pci_set_power_state(pcidev, PCI_D0); - pci_restore_state(pcidev); - ret = pci_enable_device(pcidev); - if (ret) - goto out; - pci_set_master(pcidev); - - ret = rtsx_pci_write_register(pcr, HOST_SLEEP_STATE, 0x03, 0x00); - if (ret) - goto out; - - ret = rtsx_pci_init_hw(pcr); - if (ret) - goto out; - - schedule_delayed_work(&pcr->idle_work, msecs_to_jiffies(200)); - -out: - mutex_unlock(&pcr->pcr_mutex); - return ret; -} - -static void rtsx_pci_shutdown(struct pci_dev *pcidev) -{ - struct pcr_handle *handle; - struct rtsx_pcr *pcr; - - dev_dbg(&(pcidev->dev), "--> %s\n", __func__); - - handle = pci_get_drvdata(pcidev); - pcr = handle->pcr; - rtsx_pci_power_off(pcr, HOST_ENTER_S1); - - pci_disable_device(pcidev); -} - -#else /* CONFIG_PM */ - -#define rtsx_pci_suspend NULL -#define rtsx_pci_resume NULL -#define rtsx_pci_shutdown NULL - -#endif /* CONFIG_PM */ - -static struct pci_driver rtsx_pci_driver = { - .name = DRV_NAME_RTSX_PCI, - .id_table = rtsx_pci_ids, - .probe = rtsx_pci_probe, - .remove = rtsx_pci_remove, - .suspend = rtsx_pci_suspend, - .resume = rtsx_pci_resume, - .shutdown = rtsx_pci_shutdown, -}; -module_pci_driver(rtsx_pci_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Wei WANG "); -MODULE_DESCRIPTION("Realtek PCI-E Card Reader Driver"); diff --git a/drivers/mfd/rtsx_pcr.h b/drivers/mfd/rtsx_pcr.h deleted file mode 100644 index ec784e04fe20..000000000000 --- a/drivers/mfd/rtsx_pcr.h +++ /dev/null @@ -1,103 +0,0 @@ -/* Driver for Realtek PCI-Express card reader - * - * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - * - * Author: - * Wei WANG - */ - -#ifndef __RTSX_PCR_H -#define __RTSX_PCR_H - -#include - -#define MIN_DIV_N_PCR 80 -#define MAX_DIV_N_PCR 208 - -#define RTS522A_PM_CTRL3 0xFF7E - -#define RTS524A_PME_FORCE_CTL 0xFF78 -#define RTS524A_PM_CTRL3 0xFF7E - -#define LTR_ACTIVE_LATENCY_DEF 0x883C -#define LTR_IDLE_LATENCY_DEF 0x892C -#define LTR_L1OFF_LATENCY_DEF 0x9003 -#define L1_SNOOZE_DELAY_DEF 1 -#define LTR_L1OFF_SSPWRGATE_5249_DEF 0xAF -#define LTR_L1OFF_SSPWRGATE_5250_DEF 0xFF -#define LTR_L1OFF_SNOOZE_SSPWRGATE_5249_DEF 0xAC -#define LTR_L1OFF_SNOOZE_SSPWRGATE_5250_DEF 0xF8 -#define CMD_TIMEOUT_DEF 100 -#define ASPM_MASK_NEG 0xFC -#define MASK_8_BIT_DEF 0xFF - -int __rtsx_pci_write_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 val); -int __rtsx_pci_read_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 *val); - -void rts5209_init_params(struct rtsx_pcr *pcr); -void rts5229_init_params(struct rtsx_pcr *pcr); -void rtl8411_init_params(struct rtsx_pcr *pcr); -void rtl8402_init_params(struct rtsx_pcr *pcr); -void rts5227_init_params(struct rtsx_pcr *pcr); -void rts522a_init_params(struct rtsx_pcr *pcr); -void rts5249_init_params(struct rtsx_pcr *pcr); -void rts524a_init_params(struct rtsx_pcr *pcr); -void rts525a_init_params(struct rtsx_pcr *pcr); -void rtl8411b_init_params(struct rtsx_pcr *pcr); - -static inline u8 map_sd_drive(int idx) -{ - u8 sd_drive[4] = { - 0x01, /* Type D */ - 0x02, /* Type C */ - 0x05, /* Type A */ - 0x03 /* Type B */ - }; - - return sd_drive[idx]; -} - -#define rtsx_vendor_setting_valid(reg) (!((reg) & 0x1000000)) -#define rts5209_vendor_setting1_valid(reg) (!((reg) & 0x80)) -#define rts5209_vendor_setting2_valid(reg) ((reg) & 0x80) - -#define rtsx_reg_to_aspm(reg) (((reg) >> 28) & 0x03) -#define rtsx_reg_to_sd30_drive_sel_1v8(reg) (((reg) >> 26) & 0x03) -#define rtsx_reg_to_sd30_drive_sel_3v3(reg) (((reg) >> 5) & 0x03) -#define rtsx_reg_to_card_drive_sel(reg) ((((reg) >> 25) & 0x01) << 6) -#define rtsx_reg_check_reverse_socket(reg) ((reg) & 0x4000) -#define rts5209_reg_to_aspm(reg) (((reg) >> 5) & 0x03) -#define rts5209_reg_check_ms_pmos(reg) (!((reg) & 0x08)) -#define rts5209_reg_to_sd30_drive_sel_1v8(reg) (((reg) >> 3) & 0x07) -#define rts5209_reg_to_sd30_drive_sel_3v3(reg) ((reg) & 0x07) -#define rts5209_reg_to_card_drive_sel(reg) ((reg) >> 8) -#define rtl8411_reg_to_sd30_drive_sel_3v3(reg) (((reg) >> 5) & 0x07) -#define rtl8411b_reg_to_sd30_drive_sel_3v3(reg) ((reg) & 0x03) - -#define set_pull_ctrl_tables(pcr, __device) \ -do { \ - pcr->sd_pull_ctl_enable_tbl = __device##_sd_pull_ctl_enable_tbl; \ - pcr->sd_pull_ctl_disable_tbl = __device##_sd_pull_ctl_disable_tbl; \ - pcr->ms_pull_ctl_enable_tbl = __device##_ms_pull_ctl_enable_tbl; \ - pcr->ms_pull_ctl_disable_tbl = __device##_ms_pull_ctl_disable_tbl; \ -} while (0) - -/* generic operations */ -int rtsx_gops_pm_reset(struct rtsx_pcr *pcr); -int rtsx_set_ltr_latency(struct rtsx_pcr *pcr, u32 latency); -int rtsx_set_l1off_sub(struct rtsx_pcr *pcr, u8 val); - -#endif diff --git a/drivers/mfd/rtsx_usb.c b/drivers/mfd/rtsx_usb.c deleted file mode 100644 index 59d61b04c197..000000000000 --- a/drivers/mfd/rtsx_usb.c +++ /dev/null @@ -1,791 +0,0 @@ -/* Driver for Realtek USB card reader - * - * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - * - * Author: - * Roger Tseng - */ -#include -#include -#include -#include -#include -#include -#include - -static int polling_pipe = 1; -module_param(polling_pipe, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(polling_pipe, "polling pipe (0: ctl, 1: bulk)"); - -static const struct mfd_cell rtsx_usb_cells[] = { - [RTSX_USB_SD_CARD] = { - .name = "rtsx_usb_sdmmc", - .pdata_size = 0, - }, - [RTSX_USB_MS_CARD] = { - .name = "rtsx_usb_ms", - .pdata_size = 0, - }, -}; - -static void rtsx_usb_sg_timed_out(struct timer_list *t) -{ - struct rtsx_ucr *ucr = from_timer(ucr, t, sg_timer); - - dev_dbg(&ucr->pusb_intf->dev, "%s: sg transfer timed out", __func__); - usb_sg_cancel(&ucr->current_sg); -} - -static int rtsx_usb_bulk_transfer_sglist(struct rtsx_ucr *ucr, - unsigned int pipe, struct scatterlist *sg, int num_sg, - unsigned int length, unsigned int *act_len, int timeout) -{ - int ret; - - dev_dbg(&ucr->pusb_intf->dev, "%s: xfer %u bytes, %d entries\n", - __func__, length, num_sg); - ret = usb_sg_init(&ucr->current_sg, ucr->pusb_dev, pipe, 0, - sg, num_sg, length, GFP_NOIO); - if (ret) - return ret; - - ucr->sg_timer.expires = jiffies + msecs_to_jiffies(timeout); - add_timer(&ucr->sg_timer); - usb_sg_wait(&ucr->current_sg); - if (!del_timer_sync(&ucr->sg_timer)) - ret = -ETIMEDOUT; - else - ret = ucr->current_sg.status; - - if (act_len) - *act_len = ucr->current_sg.bytes; - - return ret; -} - -int rtsx_usb_transfer_data(struct rtsx_ucr *ucr, unsigned int pipe, - void *buf, unsigned int len, int num_sg, - unsigned int *act_len, int timeout) -{ - if (timeout < 600) - timeout = 600; - - if (num_sg) - return rtsx_usb_bulk_transfer_sglist(ucr, pipe, - (struct scatterlist *)buf, num_sg, len, act_len, - timeout); - else - return usb_bulk_msg(ucr->pusb_dev, pipe, buf, len, act_len, - timeout); -} -EXPORT_SYMBOL_GPL(rtsx_usb_transfer_data); - -static inline void rtsx_usb_seq_cmd_hdr(struct rtsx_ucr *ucr, - u16 addr, u16 len, u8 seq_type) -{ - rtsx_usb_cmd_hdr_tag(ucr); - - ucr->cmd_buf[PACKET_TYPE] = seq_type; - ucr->cmd_buf[5] = (u8)(len >> 8); - ucr->cmd_buf[6] = (u8)len; - ucr->cmd_buf[8] = (u8)(addr >> 8); - ucr->cmd_buf[9] = (u8)addr; - - if (seq_type == SEQ_WRITE) - ucr->cmd_buf[STAGE_FLAG] = 0; - else - ucr->cmd_buf[STAGE_FLAG] = STAGE_R; -} - -static int rtsx_usb_seq_write_register(struct rtsx_ucr *ucr, - u16 addr, u16 len, u8 *data) -{ - u16 cmd_len = ALIGN(SEQ_WRITE_DATA_OFFSET + len, 4); - - if (!data) - return -EINVAL; - - if (cmd_len > IOBUF_SIZE) - return -EINVAL; - - rtsx_usb_seq_cmd_hdr(ucr, addr, len, SEQ_WRITE); - memcpy(ucr->cmd_buf + SEQ_WRITE_DATA_OFFSET, data, len); - - return rtsx_usb_transfer_data(ucr, - usb_sndbulkpipe(ucr->pusb_dev, EP_BULK_OUT), - ucr->cmd_buf, cmd_len, 0, NULL, 100); -} - -static int rtsx_usb_seq_read_register(struct rtsx_ucr *ucr, - u16 addr, u16 len, u8 *data) -{ - int i, ret; - u16 rsp_len = round_down(len, 4); - u16 res_len = len - rsp_len; - - if (!data) - return -EINVAL; - - /* 4-byte aligned part */ - if (rsp_len) { - rtsx_usb_seq_cmd_hdr(ucr, addr, len, SEQ_READ); - ret = rtsx_usb_transfer_data(ucr, - usb_sndbulkpipe(ucr->pusb_dev, EP_BULK_OUT), - ucr->cmd_buf, 12, 0, NULL, 100); - if (ret) - return ret; - - ret = rtsx_usb_transfer_data(ucr, - usb_rcvbulkpipe(ucr->pusb_dev, EP_BULK_IN), - data, rsp_len, 0, NULL, 100); - if (ret) - return ret; - } - - /* unaligned part */ - for (i = 0; i < res_len; i++) { - ret = rtsx_usb_read_register(ucr, addr + rsp_len + i, - data + rsp_len + i); - if (ret) - return ret; - } - - return 0; -} - -int rtsx_usb_read_ppbuf(struct rtsx_ucr *ucr, u8 *buf, int buf_len) -{ - return rtsx_usb_seq_read_register(ucr, PPBUF_BASE2, (u16)buf_len, buf); -} -EXPORT_SYMBOL_GPL(rtsx_usb_read_ppbuf); - -int rtsx_usb_write_ppbuf(struct rtsx_ucr *ucr, u8 *buf, int buf_len) -{ - return rtsx_usb_seq_write_register(ucr, PPBUF_BASE2, (u16)buf_len, buf); -} -EXPORT_SYMBOL_GPL(rtsx_usb_write_ppbuf); - -int rtsx_usb_ep0_write_register(struct rtsx_ucr *ucr, u16 addr, - u8 mask, u8 data) -{ - u16 value, index; - - addr |= EP0_WRITE_REG_CMD << EP0_OP_SHIFT; - value = swab16(addr); - index = mask | data << 8; - - return usb_control_msg(ucr->pusb_dev, - usb_sndctrlpipe(ucr->pusb_dev, 0), RTSX_USB_REQ_REG_OP, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, index, NULL, 0, 100); -} -EXPORT_SYMBOL_GPL(rtsx_usb_ep0_write_register); - -int rtsx_usb_ep0_read_register(struct rtsx_ucr *ucr, u16 addr, u8 *data) -{ - u16 value; - u8 *buf; - int ret; - - if (!data) - return -EINVAL; - - buf = kzalloc(sizeof(u8), GFP_KERNEL); - if (!buf) - return -ENOMEM; - - addr |= EP0_READ_REG_CMD << EP0_OP_SHIFT; - value = swab16(addr); - - ret = usb_control_msg(ucr->pusb_dev, - usb_rcvctrlpipe(ucr->pusb_dev, 0), RTSX_USB_REQ_REG_OP, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, 0, buf, 1, 100); - *data = *buf; - - kfree(buf); - return ret; -} -EXPORT_SYMBOL_GPL(rtsx_usb_ep0_read_register); - -void rtsx_usb_add_cmd(struct rtsx_ucr *ucr, u8 cmd_type, u16 reg_addr, - u8 mask, u8 data) -{ - int i; - - if (ucr->cmd_idx < (IOBUF_SIZE - CMD_OFFSET) / 4) { - i = CMD_OFFSET + ucr->cmd_idx * 4; - - ucr->cmd_buf[i++] = ((cmd_type & 0x03) << 6) | - (u8)((reg_addr >> 8) & 0x3F); - ucr->cmd_buf[i++] = (u8)reg_addr; - ucr->cmd_buf[i++] = mask; - ucr->cmd_buf[i++] = data; - - ucr->cmd_idx++; - } -} -EXPORT_SYMBOL_GPL(rtsx_usb_add_cmd); - -int rtsx_usb_send_cmd(struct rtsx_ucr *ucr, u8 flag, int timeout) -{ - int ret; - - ucr->cmd_buf[CNT_H] = (u8)(ucr->cmd_idx >> 8); - ucr->cmd_buf[CNT_L] = (u8)(ucr->cmd_idx); - ucr->cmd_buf[STAGE_FLAG] = flag; - - ret = rtsx_usb_transfer_data(ucr, - usb_sndbulkpipe(ucr->pusb_dev, EP_BULK_OUT), - ucr->cmd_buf, ucr->cmd_idx * 4 + CMD_OFFSET, - 0, NULL, timeout); - if (ret) { - rtsx_usb_clear_fsm_err(ucr); - return ret; - } - - return 0; -} -EXPORT_SYMBOL_GPL(rtsx_usb_send_cmd); - -int rtsx_usb_get_rsp(struct rtsx_ucr *ucr, int rsp_len, int timeout) -{ - if (rsp_len <= 0) - return -EINVAL; - - rsp_len = ALIGN(rsp_len, 4); - - return rtsx_usb_transfer_data(ucr, - usb_rcvbulkpipe(ucr->pusb_dev, EP_BULK_IN), - ucr->rsp_buf, rsp_len, 0, NULL, timeout); -} -EXPORT_SYMBOL_GPL(rtsx_usb_get_rsp); - -static int rtsx_usb_get_status_with_bulk(struct rtsx_ucr *ucr, u16 *status) -{ - int ret; - - rtsx_usb_init_cmd(ucr); - rtsx_usb_add_cmd(ucr, READ_REG_CMD, CARD_EXIST, 0x00, 0x00); - rtsx_usb_add_cmd(ucr, READ_REG_CMD, OCPSTAT, 0x00, 0x00); - ret = rtsx_usb_send_cmd(ucr, MODE_CR, 100); - if (ret) - return ret; - - ret = rtsx_usb_get_rsp(ucr, 2, 100); - if (ret) - return ret; - - *status = ((ucr->rsp_buf[0] >> 2) & 0x0f) | - ((ucr->rsp_buf[1] & 0x03) << 4); - - return 0; -} - -int rtsx_usb_get_card_status(struct rtsx_ucr *ucr, u16 *status) -{ - int ret; - u16 *buf; - - if (!status) - return -EINVAL; - - if (polling_pipe == 0) { - buf = kzalloc(sizeof(u16), GFP_KERNEL); - if (!buf) - return -ENOMEM; - - ret = usb_control_msg(ucr->pusb_dev, - usb_rcvctrlpipe(ucr->pusb_dev, 0), - RTSX_USB_REQ_POLL, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, 0, buf, 2, 100); - *status = *buf; - - kfree(buf); - } else { - ret = rtsx_usb_get_status_with_bulk(ucr, status); - } - - /* usb_control_msg may return positive when success */ - if (ret < 0) - return ret; - - return 0; -} -EXPORT_SYMBOL_GPL(rtsx_usb_get_card_status); - -static int rtsx_usb_write_phy_register(struct rtsx_ucr *ucr, u8 addr, u8 val) -{ - dev_dbg(&ucr->pusb_intf->dev, "Write 0x%x to phy register 0x%x\n", - val, addr); - - rtsx_usb_init_cmd(ucr); - - rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VSTAIN, 0xFF, val); - rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VCONTROL, 0xFF, addr & 0x0F); - rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00); - rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00); - rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x01); - rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VCONTROL, - 0xFF, (addr >> 4) & 0x0F); - rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00); - rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x00); - rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, HS_VLOADM, 0xFF, 0x01); - - return rtsx_usb_send_cmd(ucr, MODE_C, 100); -} - -int rtsx_usb_write_register(struct rtsx_ucr *ucr, u16 addr, u8 mask, u8 data) -{ - rtsx_usb_init_cmd(ucr); - rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, addr, mask, data); - return rtsx_usb_send_cmd(ucr, MODE_C, 100); -} -EXPORT_SYMBOL_GPL(rtsx_usb_write_register); - -int rtsx_usb_read_register(struct rtsx_ucr *ucr, u16 addr, u8 *data) -{ - int ret; - - if (data != NULL) - *data = 0; - - rtsx_usb_init_cmd(ucr); - rtsx_usb_add_cmd(ucr, READ_REG_CMD, addr, 0, 0); - ret = rtsx_usb_send_cmd(ucr, MODE_CR, 100); - if (ret) - return ret; - - ret = rtsx_usb_get_rsp(ucr, 1, 100); - if (ret) - return ret; - - if (data != NULL) - *data = ucr->rsp_buf[0]; - - return 0; -} -EXPORT_SYMBOL_GPL(rtsx_usb_read_register); - -static inline u8 double_ssc_depth(u8 depth) -{ - return (depth > 1) ? (depth - 1) : depth; -} - -static u8 revise_ssc_depth(u8 ssc_depth, u8 div) -{ - if (div > CLK_DIV_1) { - if (ssc_depth > div - 1) - ssc_depth -= (div - 1); - else - ssc_depth = SSC_DEPTH_2M; - } - - return ssc_depth; -} - -int rtsx_usb_switch_clock(struct rtsx_ucr *ucr, unsigned int card_clock, - u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk) -{ - int ret; - u8 n, clk_divider, mcu_cnt, div; - - if (!card_clock) { - ucr->cur_clk = 0; - return 0; - } - - if (initial_mode) { - /* We use 250k(around) here, in initial stage */ - clk_divider = SD_CLK_DIVIDE_128; - card_clock = 30000000; - } else { - clk_divider = SD_CLK_DIVIDE_0; - } - - ret = rtsx_usb_write_register(ucr, SD_CFG1, - SD_CLK_DIVIDE_MASK, clk_divider); - if (ret < 0) - return ret; - - card_clock /= 1000000; - dev_dbg(&ucr->pusb_intf->dev, - "Switch card clock to %dMHz\n", card_clock); - - if (!initial_mode && double_clk) - card_clock *= 2; - dev_dbg(&ucr->pusb_intf->dev, - "Internal SSC clock: %dMHz (cur_clk = %d)\n", - card_clock, ucr->cur_clk); - - if (card_clock == ucr->cur_clk) - return 0; - - /* Converting clock value into internal settings: n and div */ - n = card_clock - 2; - if ((card_clock <= 2) || (n > MAX_DIV_N)) - return -EINVAL; - - mcu_cnt = 60/card_clock + 3; - if (mcu_cnt > 15) - mcu_cnt = 15; - - /* Make sure that the SSC clock div_n is not less than MIN_DIV_N */ - - div = CLK_DIV_1; - while (n < MIN_DIV_N && div < CLK_DIV_4) { - n = (n + 2) * 2 - 2; - div++; - } - dev_dbg(&ucr->pusb_intf->dev, "n = %d, div = %d\n", n, div); - - if (double_clk) - ssc_depth = double_ssc_depth(ssc_depth); - - ssc_depth = revise_ssc_depth(ssc_depth, div); - dev_dbg(&ucr->pusb_intf->dev, "ssc_depth = %d\n", ssc_depth); - - rtsx_usb_init_cmd(ucr); - rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CLK_DIV, CLK_CHANGE, CLK_CHANGE); - rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CLK_DIV, - 0x3F, (div << 4) | mcu_cnt); - rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, 0); - rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_CTL2, - SSC_DEPTH_MASK, ssc_depth); - rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_DIV_N_0, 0xFF, n); - rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, SSC_RSTB); - if (vpclk) { - rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_VPCLK0_CTL, - PHASE_NOT_RESET, 0); - rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_VPCLK0_CTL, - PHASE_NOT_RESET, PHASE_NOT_RESET); - } - - ret = rtsx_usb_send_cmd(ucr, MODE_C, 2000); - if (ret < 0) - return ret; - - ret = rtsx_usb_write_register(ucr, SSC_CTL1, 0xff, - SSC_RSTB | SSC_8X_EN | SSC_SEL_4M); - if (ret < 0) - return ret; - - /* Wait SSC clock stable */ - usleep_range(100, 1000); - - ret = rtsx_usb_write_register(ucr, CLK_DIV, CLK_CHANGE, 0); - if (ret < 0) - return ret; - - ucr->cur_clk = card_clock; - - return 0; -} -EXPORT_SYMBOL_GPL(rtsx_usb_switch_clock); - -int rtsx_usb_card_exclusive_check(struct rtsx_ucr *ucr, int card) -{ - int ret; - u16 val; - u16 cd_mask[] = { - [RTSX_USB_SD_CARD] = (CD_MASK & ~SD_CD), - [RTSX_USB_MS_CARD] = (CD_MASK & ~MS_CD) - }; - - ret = rtsx_usb_get_card_status(ucr, &val); - /* - * If get status fails, return 0 (ok) for the exclusive check - * and let the flow fail at somewhere else. - */ - if (ret) - return 0; - - if (val & cd_mask[card]) - return -EIO; - - return 0; -} -EXPORT_SYMBOL_GPL(rtsx_usb_card_exclusive_check); - -static int rtsx_usb_reset_chip(struct rtsx_ucr *ucr) -{ - int ret; - u8 val; - - rtsx_usb_init_cmd(ucr); - - if (CHECK_PKG(ucr, LQFP48)) { - rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL, - LDO3318_PWR_MASK, LDO_SUSPEND); - rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL, - FORCE_LDO_POWERB, FORCE_LDO_POWERB); - rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1, - 0x30, 0x10); - rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, - 0x03, 0x01); - rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, - 0x0C, 0x04); - } - - rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SYS_DUMMY0, NYET_MSAK, NYET_EN); - rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CD_DEGLITCH_WIDTH, 0xFF, 0x08); - rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, - CD_DEGLITCH_EN, XD_CD_DEGLITCH_EN, 0x0); - rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD30_DRIVE_SEL, - SD30_DRIVE_MASK, DRIVER_TYPE_D); - rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, - CARD_DRIVE_SEL, SD20_DRIVE_MASK, 0x0); - rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, LDO_POWER_CFG, 0xE0, 0x0); - - if (ucr->is_rts5179) - rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, - CARD_PULL_CTL5, 0x03, 0x01); - - rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_DMA1_CTL, - EXTEND_DMA1_ASYNC_SIGNAL, EXTEND_DMA1_ASYNC_SIGNAL); - rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_INT_PEND, - XD_INT | MS_INT | SD_INT, - XD_INT | MS_INT | SD_INT); - - ret = rtsx_usb_send_cmd(ucr, MODE_C, 100); - if (ret) - return ret; - - /* config non-crystal mode */ - rtsx_usb_read_register(ucr, CFG_MODE, &val); - if ((val & XTAL_FREE) || ((val & CLK_MODE_MASK) == CLK_MODE_NON_XTAL)) { - ret = rtsx_usb_write_phy_register(ucr, 0xC2, 0x7C); - if (ret) - return ret; - } - - return 0; -} - -static int rtsx_usb_init_chip(struct rtsx_ucr *ucr) -{ - int ret; - u8 val; - - rtsx_usb_clear_fsm_err(ucr); - - /* power on SSC */ - ret = rtsx_usb_write_register(ucr, - FPDCTL, SSC_POWER_MASK, SSC_POWER_ON); - if (ret) - return ret; - - usleep_range(100, 1000); - ret = rtsx_usb_write_register(ucr, CLK_DIV, CLK_CHANGE, 0x00); - if (ret) - return ret; - - /* determine IC version */ - ret = rtsx_usb_read_register(ucr, HW_VERSION, &val); - if (ret) - return ret; - - ucr->ic_version = val & HW_VER_MASK; - - /* determine package */ - ret = rtsx_usb_read_register(ucr, CARD_SHARE_MODE, &val); - if (ret) - return ret; - - if (val & CARD_SHARE_LQFP_SEL) { - ucr->package = LQFP48; - dev_dbg(&ucr->pusb_intf->dev, "Package: LQFP48\n"); - } else { - ucr->package = QFN24; - dev_dbg(&ucr->pusb_intf->dev, "Package: QFN24\n"); - } - - /* determine IC variations */ - rtsx_usb_read_register(ucr, CFG_MODE_1, &val); - if (val & RTS5179) { - ucr->is_rts5179 = true; - dev_dbg(&ucr->pusb_intf->dev, "Device is rts5179\n"); - } else { - ucr->is_rts5179 = false; - } - - return rtsx_usb_reset_chip(ucr); -} - -static int rtsx_usb_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *usb_dev = interface_to_usbdev(intf); - struct rtsx_ucr *ucr; - int ret; - - dev_dbg(&intf->dev, - ": Realtek USB Card Reader found at bus %03d address %03d\n", - usb_dev->bus->busnum, usb_dev->devnum); - - ucr = devm_kzalloc(&intf->dev, sizeof(*ucr), GFP_KERNEL); - if (!ucr) - return -ENOMEM; - - ucr->pusb_dev = usb_dev; - - ucr->iobuf = usb_alloc_coherent(ucr->pusb_dev, IOBUF_SIZE, - GFP_KERNEL, &ucr->iobuf_dma); - if (!ucr->iobuf) - return -ENOMEM; - - usb_set_intfdata(intf, ucr); - - ucr->vendor_id = id->idVendor; - ucr->product_id = id->idProduct; - ucr->cmd_buf = ucr->rsp_buf = ucr->iobuf; - - mutex_init(&ucr->dev_mutex); - - ucr->pusb_intf = intf; - - /* initialize */ - ret = rtsx_usb_init_chip(ucr); - if (ret) - goto out_init_fail; - - /* initialize USB SG transfer timer */ - timer_setup(&ucr->sg_timer, rtsx_usb_sg_timed_out, 0); - - ret = mfd_add_hotplug_devices(&intf->dev, rtsx_usb_cells, - ARRAY_SIZE(rtsx_usb_cells)); - if (ret) - goto out_init_fail; - -#ifdef CONFIG_PM - intf->needs_remote_wakeup = 1; - usb_enable_autosuspend(usb_dev); -#endif - - return 0; - -out_init_fail: - usb_free_coherent(ucr->pusb_dev, IOBUF_SIZE, ucr->iobuf, - ucr->iobuf_dma); - return ret; -} - -static void rtsx_usb_disconnect(struct usb_interface *intf) -{ - struct rtsx_ucr *ucr = (struct rtsx_ucr *)usb_get_intfdata(intf); - - dev_dbg(&intf->dev, "%s called\n", __func__); - - mfd_remove_devices(&intf->dev); - - usb_set_intfdata(ucr->pusb_intf, NULL); - usb_free_coherent(ucr->pusb_dev, IOBUF_SIZE, ucr->iobuf, - ucr->iobuf_dma); -} - -#ifdef CONFIG_PM -static int rtsx_usb_suspend(struct usb_interface *intf, pm_message_t message) -{ - struct rtsx_ucr *ucr = - (struct rtsx_ucr *)usb_get_intfdata(intf); - u16 val = 0; - - dev_dbg(&intf->dev, "%s called with pm message 0x%04x\n", - __func__, message.event); - - if (PMSG_IS_AUTO(message)) { - if (mutex_trylock(&ucr->dev_mutex)) { - rtsx_usb_get_card_status(ucr, &val); - mutex_unlock(&ucr->dev_mutex); - - /* Defer the autosuspend if card exists */ - if (val & (SD_CD | MS_CD)) - return -EAGAIN; - } else { - /* There is an ongoing operation*/ - return -EAGAIN; - } - } - - return 0; -} - -static int rtsx_usb_resume(struct usb_interface *intf) -{ - return 0; -} - -static int rtsx_usb_reset_resume(struct usb_interface *intf) -{ - struct rtsx_ucr *ucr = - (struct rtsx_ucr *)usb_get_intfdata(intf); - - rtsx_usb_reset_chip(ucr); - return 0; -} - -#else /* CONFIG_PM */ - -#define rtsx_usb_suspend NULL -#define rtsx_usb_resume NULL -#define rtsx_usb_reset_resume NULL - -#endif /* CONFIG_PM */ - - -static int rtsx_usb_pre_reset(struct usb_interface *intf) -{ - struct rtsx_ucr *ucr = (struct rtsx_ucr *)usb_get_intfdata(intf); - - mutex_lock(&ucr->dev_mutex); - return 0; -} - -static int rtsx_usb_post_reset(struct usb_interface *intf) -{ - struct rtsx_ucr *ucr = (struct rtsx_ucr *)usb_get_intfdata(intf); - - mutex_unlock(&ucr->dev_mutex); - return 0; -} - -static struct usb_device_id rtsx_usb_usb_ids[] = { - { USB_DEVICE(0x0BDA, 0x0129) }, - { USB_DEVICE(0x0BDA, 0x0139) }, - { USB_DEVICE(0x0BDA, 0x0140) }, - { } -}; -MODULE_DEVICE_TABLE(usb, rtsx_usb_usb_ids); - -static struct usb_driver rtsx_usb_driver = { - .name = "rtsx_usb", - .probe = rtsx_usb_probe, - .disconnect = rtsx_usb_disconnect, - .suspend = rtsx_usb_suspend, - .resume = rtsx_usb_resume, - .reset_resume = rtsx_usb_reset_resume, - .pre_reset = rtsx_usb_pre_reset, - .post_reset = rtsx_usb_post_reset, - .id_table = rtsx_usb_usb_ids, - .supports_autosuspend = 1, - .soft_unbind = 1, -}; - -module_usb_driver(rtsx_usb_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Roger Tseng "); -MODULE_DESCRIPTION("Realtek USB Card Reader Driver"); -- cgit v1.2.3 From ea01a31b90581a94cdeef7fda9e4522f15ef64f2 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Mon, 20 Nov 2017 17:15:25 +0100 Subject: cros_ec: Split cros_ec_devs module This patch splits the cros_ec_devs module in two parts with a cros_ec_dev module responsible for handling MFD devices registration and a cros_ec_ctl module responsible for handling the various user-space interfaces. For consistency purpose, the driver name for the cros_ec_dev module is now cros-ec-dev instead of cros-ec-ctl. In the next commit, the new cros_ec_dev module will be moved to the MFD subtree so mfd_add_devices() calls are not done from outside MFD. Signed-off-by: Thierry Escande Reviewed-by: Gwendal Grignou Tested-by: Guenter Roeck Signed-off-by: Lee Jones --- drivers/mfd/cros_ec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/mfd') diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c index b0ca5a4c841e..d61024141e2b 100644 --- a/drivers/mfd/cros_ec.c +++ b/drivers/mfd/cros_ec.c @@ -40,13 +40,13 @@ static struct cros_ec_platform pd_p = { }; static const struct mfd_cell ec_cell = { - .name = "cros-ec-ctl", + .name = "cros-ec-dev", .platform_data = &ec_p, .pdata_size = sizeof(ec_p), }; static const struct mfd_cell ec_pd_cell = { - .name = "cros-ec-ctl", + .name = "cros-ec-dev", .platform_data = &pd_p, .pdata_size = sizeof(pd_p), }; -- cgit v1.2.3 From 5e0115581bbc367c7958bf5ab8c511b808558533 Mon Sep 17 00:00:00 2001 From: Thierry Escande Date: Mon, 20 Nov 2017 17:15:26 +0100 Subject: cros_ec: Move cros_ec_dev module to drivers/mfd The cros_ec_dev module is responsible for registering the MFD devices attached to the ChromeOS EC. This patch moves this module to drivers/mfd so calls to mfd_add_devices() are not done from outside the MFD subtree anymore. Signed-off-by: Thierry Escande Reviewed-by: Gwendal Grignou Tested-by: Guenter Roeck Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 10 + drivers/mfd/Makefile | 1 + drivers/mfd/cros_ec_dev.c | 552 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/mfd/cros_ec_dev.h | 52 +++++ 4 files changed, 615 insertions(+) create mode 100644 drivers/mfd/cros_ec_dev.c create mode 100644 drivers/mfd/cros_ec_dev.h (limited to 'drivers/mfd') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 1d20a800e967..538a2ae8bd25 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -222,6 +222,16 @@ config MFD_CROS_EC_SPI response time cannot be guaranteed, we support ignoring 'pre-amble' bytes before the response actually starts. +config MFD_CROS_EC_CHARDEV + tristate "Chrome OS Embedded Controller userspace device interface" + depends on MFD_CROS_EC + select CROS_EC_CTL + ---help--- + This driver adds support to talk with the ChromeOS EC from userspace. + + If you have a supported Chromebook, choose Y or M here. + The module will be called cros_ec_dev. + config MFD_ASIC3 bool "Compaq ASIC3" depends on GPIOLIB && ARM diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index d9474ade32e6..fcd8af88110e 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -17,6 +17,7 @@ cros_ec_core-$(CONFIG_ACPI) += cros_ec_acpi_gpe.o obj-$(CONFIG_MFD_CROS_EC) += cros_ec_core.o obj-$(CONFIG_MFD_CROS_EC_I2C) += cros_ec_i2c.o obj-$(CONFIG_MFD_CROS_EC_SPI) += cros_ec_spi.o +obj-$(CONFIG_MFD_CROS_EC_CHARDEV) += cros_ec_dev.o obj-$(CONFIG_MFD_EXYNOS_LPASS) += exynos-lpass.o rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c new file mode 100644 index 000000000000..e4fafdd96e5e --- /dev/null +++ b/drivers/mfd/cros_ec_dev.c @@ -0,0 +1,552 @@ +/* + * cros_ec_dev - expose the Chrome OS Embedded Controller to user-space + * + * Copyright (C) 2014 Google, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "cros_ec_dev.h" + +#define DRV_NAME "cros-ec-dev" + +/* Device variables */ +#define CROS_MAX_DEV 128 +static int ec_major; + +static const struct attribute_group *cros_ec_groups[] = { + &cros_ec_attr_group, + &cros_ec_lightbar_attr_group, + &cros_ec_vbc_attr_group, + NULL, +}; + +static struct class cros_class = { + .owner = THIS_MODULE, + .name = "chromeos", + .dev_groups = cros_ec_groups, +}; + +/* Basic communication */ +static int ec_get_version(struct cros_ec_dev *ec, char *str, int maxlen) +{ + struct ec_response_get_version *resp; + static const char * const current_image_name[] = { + "unknown", "read-only", "read-write", "invalid", + }; + struct cros_ec_command *msg; + int ret; + + msg = kmalloc(sizeof(*msg) + sizeof(*resp), GFP_KERNEL); + if (!msg) + return -ENOMEM; + + msg->version = 0; + msg->command = EC_CMD_GET_VERSION + ec->cmd_offset; + msg->insize = sizeof(*resp); + msg->outsize = 0; + + ret = cros_ec_cmd_xfer(ec->ec_dev, msg); + if (ret < 0) + goto exit; + + if (msg->result != EC_RES_SUCCESS) { + snprintf(str, maxlen, + "%s\nUnknown EC version: EC returned %d\n", + CROS_EC_DEV_VERSION, msg->result); + ret = -EINVAL; + goto exit; + } + + resp = (struct ec_response_get_version *)msg->data; + if (resp->current_image >= ARRAY_SIZE(current_image_name)) + resp->current_image = 3; /* invalid */ + + snprintf(str, maxlen, "%s\n%s\n%s\n%s\n", CROS_EC_DEV_VERSION, + resp->version_string_ro, resp->version_string_rw, + current_image_name[resp->current_image]); + + ret = 0; +exit: + kfree(msg); + return ret; +} + +static int cros_ec_check_features(struct cros_ec_dev *ec, int feature) +{ + struct cros_ec_command *msg; + int ret; + + if (ec->features[0] == -1U && ec->features[1] == -1U) { + /* features bitmap not read yet */ + + msg = kmalloc(sizeof(*msg) + sizeof(ec->features), GFP_KERNEL); + if (!msg) + return -ENOMEM; + + msg->version = 0; + msg->command = EC_CMD_GET_FEATURES + ec->cmd_offset; + msg->insize = sizeof(ec->features); + msg->outsize = 0; + + ret = cros_ec_cmd_xfer(ec->ec_dev, msg); + if (ret < 0 || msg->result != EC_RES_SUCCESS) { + dev_warn(ec->dev, "cannot get EC features: %d/%d\n", + ret, msg->result); + memset(ec->features, 0, sizeof(ec->features)); + } + + memcpy(ec->features, msg->data, sizeof(ec->features)); + + dev_dbg(ec->dev, "EC features %08x %08x\n", + ec->features[0], ec->features[1]); + + kfree(msg); + } + + return ec->features[feature / 32] & EC_FEATURE_MASK_0(feature); +} + +/* Device file ops */ +static int ec_device_open(struct inode *inode, struct file *filp) +{ + struct cros_ec_dev *ec = container_of(inode->i_cdev, + struct cros_ec_dev, cdev); + filp->private_data = ec; + nonseekable_open(inode, filp); + return 0; +} + +static int ec_device_release(struct inode *inode, struct file *filp) +{ + return 0; +} + +static ssize_t ec_device_read(struct file *filp, char __user *buffer, + size_t length, loff_t *offset) +{ + struct cros_ec_dev *ec = filp->private_data; + char msg[sizeof(struct ec_response_get_version) + + sizeof(CROS_EC_DEV_VERSION)]; + size_t count; + int ret; + + if (*offset != 0) + return 0; + + ret = ec_get_version(ec, msg, sizeof(msg)); + if (ret) + return ret; + + count = min(length, strlen(msg)); + + if (copy_to_user(buffer, msg, count)) + return -EFAULT; + + *offset = count; + return count; +} + +/* Ioctls */ +static long ec_device_ioctl_xcmd(struct cros_ec_dev *ec, void __user *arg) +{ + long ret; + struct cros_ec_command u_cmd; + struct cros_ec_command *s_cmd; + + if (copy_from_user(&u_cmd, arg, sizeof(u_cmd))) + return -EFAULT; + + if ((u_cmd.outsize > EC_MAX_MSG_BYTES) || + (u_cmd.insize > EC_MAX_MSG_BYTES)) + return -EINVAL; + + s_cmd = kmalloc(sizeof(*s_cmd) + max(u_cmd.outsize, u_cmd.insize), + GFP_KERNEL); + if (!s_cmd) + return -ENOMEM; + + if (copy_from_user(s_cmd, arg, sizeof(*s_cmd) + u_cmd.outsize)) { + ret = -EFAULT; + goto exit; + } + + if (u_cmd.outsize != s_cmd->outsize || + u_cmd.insize != s_cmd->insize) { + ret = -EINVAL; + goto exit; + } + + s_cmd->command += ec->cmd_offset; + ret = cros_ec_cmd_xfer(ec->ec_dev, s_cmd); + /* Only copy data to userland if data was received. */ + if (ret < 0) + goto exit; + + if (copy_to_user(arg, s_cmd, sizeof(*s_cmd) + s_cmd->insize)) + ret = -EFAULT; +exit: + kfree(s_cmd); + return ret; +} + +static long ec_device_ioctl_readmem(struct cros_ec_dev *ec, void __user *arg) +{ + struct cros_ec_device *ec_dev = ec->ec_dev; + struct cros_ec_readmem s_mem = { }; + long num; + + /* Not every platform supports direct reads */ + if (!ec_dev->cmd_readmem) + return -ENOTTY; + + if (copy_from_user(&s_mem, arg, sizeof(s_mem))) + return -EFAULT; + + num = ec_dev->cmd_readmem(ec_dev, s_mem.offset, s_mem.bytes, + s_mem.buffer); + if (num <= 0) + return num; + + if (copy_to_user((void __user *)arg, &s_mem, sizeof(s_mem))) + return -EFAULT; + + return 0; +} + +static long ec_device_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct cros_ec_dev *ec = filp->private_data; + + if (_IOC_TYPE(cmd) != CROS_EC_DEV_IOC) + return -ENOTTY; + + switch (cmd) { + case CROS_EC_DEV_IOCXCMD: + return ec_device_ioctl_xcmd(ec, (void __user *)arg); + case CROS_EC_DEV_IOCRDMEM: + return ec_device_ioctl_readmem(ec, (void __user *)arg); + } + + return -ENOTTY; +} + +/* Module initialization */ +static const struct file_operations fops = { + .open = ec_device_open, + .release = ec_device_release, + .read = ec_device_read, + .unlocked_ioctl = ec_device_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = ec_device_ioctl, +#endif +}; + +static void __remove(struct device *dev) +{ + struct cros_ec_dev *ec = container_of(dev, struct cros_ec_dev, + class_dev); + kfree(ec); +} + +static void cros_ec_sensors_register(struct cros_ec_dev *ec) +{ + /* + * Issue a command to get the number of sensor reported. + * Build an array of sensors driver and register them all. + */ + int ret, i, id, sensor_num; + struct mfd_cell *sensor_cells; + struct cros_ec_sensor_platform *sensor_platforms; + int sensor_type[MOTIONSENSE_TYPE_MAX]; + struct ec_params_motion_sense *params; + struct ec_response_motion_sense *resp; + struct cros_ec_command *msg; + + msg = kzalloc(sizeof(struct cros_ec_command) + + max(sizeof(*params), sizeof(*resp)), GFP_KERNEL); + if (msg == NULL) + return; + + msg->version = 2; + msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset; + msg->outsize = sizeof(*params); + msg->insize = sizeof(*resp); + + params = (struct ec_params_motion_sense *)msg->data; + params->cmd = MOTIONSENSE_CMD_DUMP; + + ret = cros_ec_cmd_xfer(ec->ec_dev, msg); + if (ret < 0 || msg->result != EC_RES_SUCCESS) { + dev_warn(ec->dev, "cannot get EC sensor information: %d/%d\n", + ret, msg->result); + goto error; + } + + resp = (struct ec_response_motion_sense *)msg->data; + sensor_num = resp->dump.sensor_count; + /* Allocate 2 extra sensors in case lid angle or FIFO are needed */ + sensor_cells = kzalloc(sizeof(struct mfd_cell) * (sensor_num + 2), + GFP_KERNEL); + if (sensor_cells == NULL) + goto error; + + sensor_platforms = kzalloc(sizeof(struct cros_ec_sensor_platform) * + (sensor_num + 1), GFP_KERNEL); + if (sensor_platforms == NULL) + goto error_platforms; + + memset(sensor_type, 0, sizeof(sensor_type)); + id = 0; + for (i = 0; i < sensor_num; i++) { + params->cmd = MOTIONSENSE_CMD_INFO; + params->info.sensor_num = i; + ret = cros_ec_cmd_xfer(ec->ec_dev, msg); + if (ret < 0 || msg->result != EC_RES_SUCCESS) { + dev_warn(ec->dev, "no info for EC sensor %d : %d/%d\n", + i, ret, msg->result); + continue; + } + switch (resp->info.type) { + case MOTIONSENSE_TYPE_ACCEL: + sensor_cells[id].name = "cros-ec-accel"; + break; + case MOTIONSENSE_TYPE_BARO: + sensor_cells[id].name = "cros-ec-baro"; + break; + case MOTIONSENSE_TYPE_GYRO: + sensor_cells[id].name = "cros-ec-gyro"; + break; + case MOTIONSENSE_TYPE_MAG: + sensor_cells[id].name = "cros-ec-mag"; + break; + case MOTIONSENSE_TYPE_PROX: + sensor_cells[id].name = "cros-ec-prox"; + break; + case MOTIONSENSE_TYPE_LIGHT: + sensor_cells[id].name = "cros-ec-light"; + break; + case MOTIONSENSE_TYPE_ACTIVITY: + sensor_cells[id].name = "cros-ec-activity"; + break; + default: + dev_warn(ec->dev, "unknown type %d\n", resp->info.type); + continue; + } + sensor_platforms[id].sensor_num = i; + sensor_cells[id].id = sensor_type[resp->info.type]; + sensor_cells[id].platform_data = &sensor_platforms[id]; + sensor_cells[id].pdata_size = + sizeof(struct cros_ec_sensor_platform); + + sensor_type[resp->info.type]++; + id++; + } + if (sensor_type[MOTIONSENSE_TYPE_ACCEL] >= 2) { + sensor_platforms[id].sensor_num = sensor_num; + + sensor_cells[id].name = "cros-ec-angle"; + sensor_cells[id].id = 0; + sensor_cells[id].platform_data = &sensor_platforms[id]; + sensor_cells[id].pdata_size = + sizeof(struct cros_ec_sensor_platform); + id++; + } + if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) { + sensor_cells[id].name = "cros-ec-ring"; + id++; + } + + ret = mfd_add_devices(ec->dev, 0, sensor_cells, id, + NULL, 0, NULL); + if (ret) + dev_err(ec->dev, "failed to add EC sensors\n"); + + kfree(sensor_platforms); +error_platforms: + kfree(sensor_cells); +error: + kfree(msg); +} + +static int ec_device_probe(struct platform_device *pdev) +{ + int retval = -ENOMEM; + struct device *dev = &pdev->dev; + struct cros_ec_platform *ec_platform = dev_get_platdata(dev); + struct cros_ec_dev *ec = kzalloc(sizeof(*ec), GFP_KERNEL); + + if (!ec) + return retval; + + dev_set_drvdata(dev, ec); + ec->ec_dev = dev_get_drvdata(dev->parent); + ec->dev = dev; + ec->cmd_offset = ec_platform->cmd_offset; + ec->features[0] = -1U; /* Not cached yet */ + ec->features[1] = -1U; /* Not cached yet */ + device_initialize(&ec->class_dev); + cdev_init(&ec->cdev, &fops); + + /* + * Add the class device + * Link to the character device for creating the /dev entry + * in devtmpfs. + */ + ec->class_dev.devt = MKDEV(ec_major, pdev->id); + ec->class_dev.class = &cros_class; + ec->class_dev.parent = dev; + ec->class_dev.release = __remove; + + retval = dev_set_name(&ec->class_dev, "%s", ec_platform->ec_name); + if (retval) { + dev_err(dev, "dev_set_name failed => %d\n", retval); + goto failed; + } + + retval = cdev_device_add(&ec->cdev, &ec->class_dev); + if (retval) { + dev_err(dev, "cdev_device_add failed => %d\n", retval); + goto failed; + } + + if (cros_ec_debugfs_init(ec)) + dev_warn(dev, "failed to create debugfs directory\n"); + + /* check whether this EC is a sensor hub. */ + if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE)) + cros_ec_sensors_register(ec); + + /* Take control of the lightbar from the EC. */ + lb_manual_suspend_ctrl(ec, 1); + + return 0; + +failed: + put_device(&ec->class_dev); + return retval; +} + +static int ec_device_remove(struct platform_device *pdev) +{ + struct cros_ec_dev *ec = dev_get_drvdata(&pdev->dev); + + /* Let the EC take over the lightbar again. */ + lb_manual_suspend_ctrl(ec, 0); + + cros_ec_debugfs_remove(ec); + + cdev_del(&ec->cdev); + device_unregister(&ec->class_dev); + return 0; +} + +static const struct platform_device_id cros_ec_id[] = { + { DRV_NAME, 0 }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(platform, cros_ec_id); + +static __maybe_unused int ec_device_suspend(struct device *dev) +{ + struct cros_ec_dev *ec = dev_get_drvdata(dev); + + lb_suspend(ec); + + return 0; +} + +static __maybe_unused int ec_device_resume(struct device *dev) +{ + struct cros_ec_dev *ec = dev_get_drvdata(dev); + + lb_resume(ec); + + return 0; +} + +static const struct dev_pm_ops cros_ec_dev_pm_ops = { +#ifdef CONFIG_PM_SLEEP + .suspend = ec_device_suspend, + .resume = ec_device_resume, +#endif +}; + +static struct platform_driver cros_ec_dev_driver = { + .driver = { + .name = DRV_NAME, + .pm = &cros_ec_dev_pm_ops, + }, + .probe = ec_device_probe, + .remove = ec_device_remove, +}; + +static int __init cros_ec_dev_init(void) +{ + int ret; + dev_t dev = 0; + + ret = class_register(&cros_class); + if (ret) { + pr_err(CROS_EC_DEV_NAME ": failed to register device class\n"); + return ret; + } + + /* Get a range of minor numbers (starting with 0) to work with */ + ret = alloc_chrdev_region(&dev, 0, CROS_MAX_DEV, CROS_EC_DEV_NAME); + if (ret < 0) { + pr_err(CROS_EC_DEV_NAME ": alloc_chrdev_region() failed\n"); + goto failed_chrdevreg; + } + ec_major = MAJOR(dev); + + /* Register the driver */ + ret = platform_driver_register(&cros_ec_dev_driver); + if (ret < 0) { + pr_warn(CROS_EC_DEV_NAME ": can't register driver: %d\n", ret); + goto failed_devreg; + } + return 0; + +failed_devreg: + unregister_chrdev_region(MKDEV(ec_major, 0), CROS_MAX_DEV); +failed_chrdevreg: + class_unregister(&cros_class); + return ret; +} + +static void __exit cros_ec_dev_exit(void) +{ + platform_driver_unregister(&cros_ec_dev_driver); + unregister_chrdev(ec_major, CROS_EC_DEV_NAME); + class_unregister(&cros_class); +} + +module_init(cros_ec_dev_init); +module_exit(cros_ec_dev_exit); + +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_AUTHOR("Bill Richardson "); +MODULE_DESCRIPTION("Userspace interface to the Chrome OS Embedded Controller"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/cros_ec_dev.h b/drivers/mfd/cros_ec_dev.h new file mode 100644 index 000000000000..45e9453608c5 --- /dev/null +++ b/drivers/mfd/cros_ec_dev.h @@ -0,0 +1,52 @@ +/* + * cros_ec_dev - expose the Chrome OS Embedded Controller to userspace + * + * Copyright (C) 2014 Google, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _CROS_EC_DEV_H_ +#define _CROS_EC_DEV_H_ + +#include +#include +#include + +#define CROS_EC_DEV_VERSION "1.0.0" + +/* + * @offset: within EC_LPC_ADDR_MEMMAP region + * @bytes: number of bytes to read. zero means "read a string" (including '\0') + * (at most only EC_MEMMAP_SIZE bytes can be read) + * @buffer: where to store the result + * ioctl returns the number of bytes read, negative on error + */ +struct cros_ec_readmem { + uint32_t offset; + uint32_t bytes; + uint8_t buffer[EC_MEMMAP_SIZE]; +}; + +#define CROS_EC_DEV_IOC 0xEC +#define CROS_EC_DEV_IOCXCMD _IOWR(CROS_EC_DEV_IOC, 0, struct cros_ec_command) +#define CROS_EC_DEV_IOCRDMEM _IOWR(CROS_EC_DEV_IOC, 1, struct cros_ec_readmem) + +/* Lightbar utilities */ +extern bool ec_has_lightbar(struct cros_ec_dev *ec); +extern int lb_manual_suspend_ctrl(struct cros_ec_dev *ec, uint8_t enable); +extern int lb_suspend(struct cros_ec_dev *ec); +extern int lb_resume(struct cros_ec_dev *ec); + +#endif /* _CROS_EC_DEV_H_ */ -- cgit v1.2.3 From 538ee27290fa277f82159f61da1c5f95f6d631e2 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Wed, 20 Dec 2017 22:51:16 -0800 Subject: mfd: Add driver for RAVE Supervisory Processor Add a driver for RAVE Supervisory Processor, an MCU implementing various bits of housekeeping functionality (watchdoging, backlight control, LED control, etc) on RAVE family of products by Zodiac Inflight Innovations. This driver implementes core MFD/serdev device as well as communication subroutines necessary for commanding the device. Signed-off-by: Andrey Smirnov Acked-by: Philippe Ombredanne Acked-by: Pavel Machek Reviewed-by: Guenter Roeck Reviewed-by: Andy Shevchenko Tested-by: Chris Healy Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 8 + drivers/mfd/Makefile | 2 + drivers/mfd/rave-sp.c | 710 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 720 insertions(+) create mode 100644 drivers/mfd/rave-sp.c (limited to 'drivers/mfd') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 1d20a800e967..ec90d408bfa9 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1859,5 +1859,13 @@ config MFD_VEXPRESS_SYSREG System Registers are the platform configuration block on the ARM Ltd. Versatile Express board. +config RAVE_SP_CORE + tristate "RAVE SP MCU core driver" + depends on SERIAL_DEV_BUS + select CRC_CCITT + help + Select this to get support for the Supervisory Processor + device found on several devices in RAVE line of hardware. + endmenu endif diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index d9474ade32e6..61abc297b97c 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -230,3 +230,5 @@ obj-$(CONFIG_MFD_STM32_LPTIMER) += stm32-lptimer.o obj-$(CONFIG_MFD_STM32_TIMERS) += stm32-timers.o obj-$(CONFIG_MFD_MXS_LRADC) += mxs-lradc.o obj-$(CONFIG_MFD_SC27XX_PMIC) += sprd-sc27xx-spi.o +obj-$(CONFIG_RAVE_SP_CORE) += rave-sp.o + diff --git a/drivers/mfd/rave-sp.c b/drivers/mfd/rave-sp.c new file mode 100644 index 000000000000..5c858e784a89 --- /dev/null +++ b/drivers/mfd/rave-sp.c @@ -0,0 +1,710 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/* + * Multifunction core driver for Zodiac Inflight Innovations RAVE + * Supervisory Processor(SP) MCU that is connected via dedicated UART + * port + * + * Copyright (C) 2017 Zodiac Inflight Innovations + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * UART protocol using following entities: + * - message to MCU => ACK response + * - event from MCU => event ACK + * + * Frame structure: + * + * Where: + * - STX - is start of transmission character + * - ETX - end of transmission + * - DATA - payload + * - CHECKSUM - checksum calculated on + * + * If or contain one of control characters, then it is + * escaped using control code. Added does not participate in + * checksum calculation. + */ +#define RAVE_SP_STX 0x02 +#define RAVE_SP_ETX 0x03 +#define RAVE_SP_DLE 0x10 + +#define RAVE_SP_MAX_DATA_SIZE 64 +#define RAVE_SP_CHECKSUM_SIZE 2 /* Worst case scenario on RDU2 */ +/* + * We don't store STX, ETX and unescaped bytes, so Rx is only + * DATA + CSUM + */ +#define RAVE_SP_RX_BUFFER_SIZE \ + (RAVE_SP_MAX_DATA_SIZE + RAVE_SP_CHECKSUM_SIZE) + +#define RAVE_SP_STX_ETX_SIZE 2 +/* + * For Tx we have to have space for everything, STX, EXT and + * potentially stuffed DATA + CSUM data + csum + */ +#define RAVE_SP_TX_BUFFER_SIZE \ + (RAVE_SP_STX_ETX_SIZE + 2 * RAVE_SP_RX_BUFFER_SIZE) + +#define RAVE_SP_BOOT_SOURCE_GET 0 +#define RAVE_SP_BOOT_SOURCE_SET 1 + +#define RAVE_SP_RDU2_BOARD_TYPE_RMB 0 +#define RAVE_SP_RDU2_BOARD_TYPE_DEB 1 + +#define RAVE_SP_BOOT_SOURCE_SD 0 +#define RAVE_SP_BOOT_SOURCE_EMMC 1 +#define RAVE_SP_BOOT_SOURCE_NOR 2 + +/** + * enum rave_sp_deframer_state - Possible state for de-framer + * + * @RAVE_SP_EXPECT_SOF: Scanning input for start-of-frame marker + * @RAVE_SP_EXPECT_DATA: Got start of frame marker, collecting frame + * @RAVE_SP_EXPECT_ESCAPED_DATA: Got escape character, collecting escaped byte + */ +enum rave_sp_deframer_state { + RAVE_SP_EXPECT_SOF, + RAVE_SP_EXPECT_DATA, + RAVE_SP_EXPECT_ESCAPED_DATA, +}; + +/** + * struct rave_sp_deframer - Device protocol deframer + * + * @state: Current state of the deframer + * @data: Buffer used to collect deframed data + * @length: Number of bytes de-framed so far + */ +struct rave_sp_deframer { + enum rave_sp_deframer_state state; + unsigned char data[RAVE_SP_RX_BUFFER_SIZE]; + size_t length; +}; + +/** + * struct rave_sp_reply - Reply as per RAVE device protocol + * + * @length: Expected reply length + * @data: Buffer to store reply payload in + * @code: Expected reply code + * @ackid: Expected reply ACK ID + * @completion: Successful reply reception completion + */ +struct rave_sp_reply { + size_t length; + void *data; + u8 code; + u8 ackid; + struct completion received; +}; + +/** + * struct rave_sp_checksum - Variant specific checksum implementation details + * + * @length: Caculated checksum length + * @subroutine: Utilized checksum algorithm implementation + */ +struct rave_sp_checksum { + size_t length; + void (*subroutine)(const u8 *, size_t, u8 *); +}; + +/** + * struct rave_sp_variant_cmds - Variant specific command routines + * + * @translate: Generic to variant specific command mapping routine + * + */ +struct rave_sp_variant_cmds { + int (*translate)(enum rave_sp_command); +}; + +/** + * struct rave_sp_variant - RAVE supervisory processor core variant + * + * @checksum: Variant specific checksum implementation + * @cmd: Variant specific command pointer table + * + */ +struct rave_sp_variant { + const struct rave_sp_checksum *checksum; + struct rave_sp_variant_cmds cmd; +}; + +/** + * struct rave_sp - RAVE supervisory processor core + * + * @serdev: Pointer to underlying serdev + * @deframer: Stored state of the protocol deframer + * @ackid: ACK ID used in last reply sent to the device + * @bus_lock: Lock to serialize access to the device + * @reply_lock: Lock protecting @reply + * @reply: Pointer to memory to store reply payload + * + * @variant: Device variant specific information + * @event_notifier_list: Input event notification chain + * + */ +struct rave_sp { + struct serdev_device *serdev; + struct rave_sp_deframer deframer; + atomic_t ackid; + struct mutex bus_lock; + struct mutex reply_lock; + struct rave_sp_reply *reply; + + const struct rave_sp_variant *variant; + struct blocking_notifier_head event_notifier_list; +}; + +static bool rave_sp_id_is_event(u8 code) +{ + return (code & 0xF0) == RAVE_SP_EVNT_BASE; +} + +static void rave_sp_unregister_event_notifier(struct device *dev, void *res) +{ + struct rave_sp *sp = dev_get_drvdata(dev->parent); + struct notifier_block *nb = *(struct notifier_block **)res; + struct blocking_notifier_head *bnh = &sp->event_notifier_list; + + WARN_ON(blocking_notifier_chain_unregister(bnh, nb)); +} + +int devm_rave_sp_register_event_notifier(struct device *dev, + struct notifier_block *nb) +{ + struct rave_sp *sp = dev_get_drvdata(dev->parent); + struct notifier_block **rcnb; + int ret; + + rcnb = devres_alloc(rave_sp_unregister_event_notifier, + sizeof(*rcnb), GFP_KERNEL); + if (!rcnb) + return -ENOMEM; + + ret = blocking_notifier_chain_register(&sp->event_notifier_list, nb); + if (!ret) { + *rcnb = nb; + devres_add(dev, rcnb); + } else { + devres_free(rcnb); + } + + return ret; +} +EXPORT_SYMBOL_GPL(devm_rave_sp_register_event_notifier); + +static void csum_8b2c(const u8 *buf, size_t size, u8 *crc) +{ + *crc = *buf++; + size--; + + while (size--) + *crc += *buf++; + + *crc = 1 + ~(*crc); +} + +static void csum_ccitt(const u8 *buf, size_t size, u8 *crc) +{ + const u16 calculated = crc_ccitt_false(0xffff, buf, size); + + /* + * While the rest of the wire protocol is little-endian, + * CCITT-16 CRC in RDU2 device is sent out in big-endian order. + */ + put_unaligned_be16(calculated, crc); +} + +static void *stuff(unsigned char *dest, const unsigned char *src, size_t n) +{ + while (n--) { + const unsigned char byte = *src++; + + switch (byte) { + case RAVE_SP_STX: + case RAVE_SP_ETX: + case RAVE_SP_DLE: + *dest++ = RAVE_SP_DLE; + /* FALLTHROUGH */ + default: + *dest++ = byte; + } + } + + return dest; +} + +static int rave_sp_write(struct rave_sp *sp, const u8 *data, u8 data_size) +{ + const size_t checksum_length = sp->variant->checksum->length; + unsigned char frame[RAVE_SP_TX_BUFFER_SIZE]; + unsigned char crc[RAVE_SP_CHECKSUM_SIZE]; + unsigned char *dest = frame; + size_t length; + + if (WARN_ON(checksum_length > sizeof(crc))) + return -ENOMEM; + + if (WARN_ON(data_size > sizeof(frame))) + return -ENOMEM; + + sp->variant->checksum->subroutine(data, data_size, crc); + + *dest++ = RAVE_SP_STX; + dest = stuff(dest, data, data_size); + dest = stuff(dest, crc, checksum_length); + *dest++ = RAVE_SP_ETX; + + length = dest - frame; + + print_hex_dump(KERN_DEBUG, "rave-sp tx: ", DUMP_PREFIX_NONE, + 16, 1, frame, length, false); + + return serdev_device_write(sp->serdev, frame, length, HZ); +} + +static u8 rave_sp_reply_code(u8 command) +{ + /* + * There isn't a single rule that describes command code -> + * ACK code transformation, but, going through various + * versions of ICDs, there appear to be three distinct groups + * that can be described by simple transformation. + */ + switch (command) { + case 0xA0 ... 0xBE: + /* + * Commands implemented by firmware found in RDU1 and + * older devices all seem to obey the following rule + */ + return command + 0x20; + case 0xE0 ... 0xEF: + /* + * Events emitted by all versions of the firmare use + * least significant bit to get an ACK code + */ + return command | 0x01; + default: + /* + * Commands implemented by firmware found in RDU2 are + * similar to "old" commands, but they use slightly + * different offset + */ + return command + 0x40; + } +} + +int rave_sp_exec(struct rave_sp *sp, + void *__data, size_t data_size, + void *reply_data, size_t reply_data_size) +{ + struct rave_sp_reply reply = { + .data = reply_data, + .length = reply_data_size, + .received = COMPLETION_INITIALIZER_ONSTACK(reply.received), + }; + unsigned char *data = __data; + int command, ret = 0; + u8 ackid; + + command = sp->variant->cmd.translate(data[0]); + if (command < 0) + return command; + + ackid = atomic_inc_return(&sp->ackid); + reply.ackid = ackid; + reply.code = rave_sp_reply_code((u8)command), + + mutex_lock(&sp->bus_lock); + + mutex_lock(&sp->reply_lock); + sp->reply = &reply; + mutex_unlock(&sp->reply_lock); + + data[0] = command; + data[1] = ackid; + + rave_sp_write(sp, data, data_size); + + if (!wait_for_completion_timeout(&reply.received, HZ)) { + dev_err(&sp->serdev->dev, "Command timeout\n"); + ret = -ETIMEDOUT; + + mutex_lock(&sp->reply_lock); + sp->reply = NULL; + mutex_unlock(&sp->reply_lock); + } + + mutex_unlock(&sp->bus_lock); + return ret; +} +EXPORT_SYMBOL_GPL(rave_sp_exec); + +static void rave_sp_receive_event(struct rave_sp *sp, + const unsigned char *data, size_t length) +{ + u8 cmd[] = { + [0] = rave_sp_reply_code(data[0]), + [1] = data[1], + }; + + rave_sp_write(sp, cmd, sizeof(cmd)); + + blocking_notifier_call_chain(&sp->event_notifier_list, + rave_sp_action_pack(data[0], data[2]), + NULL); +} + +static void rave_sp_receive_reply(struct rave_sp *sp, + const unsigned char *data, size_t length) +{ + struct device *dev = &sp->serdev->dev; + struct rave_sp_reply *reply; + const size_t payload_length = length - 2; + + mutex_lock(&sp->reply_lock); + reply = sp->reply; + + if (reply) { + if (reply->code == data[0] && reply->ackid == data[1] && + payload_length >= reply->length) { + /* + * We are relying on memcpy(dst, src, 0) to be a no-op + * when handling commands that have a no-payload reply + */ + memcpy(reply->data, &data[2], reply->length); + complete(&reply->received); + sp->reply = NULL; + } else { + dev_err(dev, "Ignoring incorrect reply\n"); + dev_dbg(dev, "Code: expected = 0x%08x received = 0x%08x\n", + reply->code, data[0]); + dev_dbg(dev, "ACK ID: expected = 0x%08x received = 0x%08x\n", + reply->ackid, data[1]); + dev_dbg(dev, "Length: expected = %zu received = %zu\n", + reply->length, payload_length); + } + } + + mutex_unlock(&sp->reply_lock); +} + +static void rave_sp_receive_frame(struct rave_sp *sp, + const unsigned char *data, + size_t length) +{ + const size_t checksum_length = sp->variant->checksum->length; + const size_t payload_length = length - checksum_length; + const u8 *crc_reported = &data[payload_length]; + struct device *dev = &sp->serdev->dev; + u8 crc_calculated[checksum_length]; + + print_hex_dump(KERN_DEBUG, "rave-sp rx: ", DUMP_PREFIX_NONE, + 16, 1, data, length, false); + + if (unlikely(length <= checksum_length)) { + dev_warn(dev, "Dropping short frame\n"); + return; + } + + sp->variant->checksum->subroutine(data, payload_length, + crc_calculated); + + if (memcmp(crc_calculated, crc_reported, checksum_length)) { + dev_warn(dev, "Dropping bad frame\n"); + return; + } + + if (rave_sp_id_is_event(data[0])) + rave_sp_receive_event(sp, data, length); + else + rave_sp_receive_reply(sp, data, length); +} + +static int rave_sp_receive_buf(struct serdev_device *serdev, + const unsigned char *buf, size_t size) +{ + struct device *dev = &serdev->dev; + struct rave_sp *sp = dev_get_drvdata(dev); + struct rave_sp_deframer *deframer = &sp->deframer; + const unsigned char *src = buf; + const unsigned char *end = buf + size; + + while (src < end) { + const unsigned char byte = *src++; + + switch (deframer->state) { + case RAVE_SP_EXPECT_SOF: + if (byte == RAVE_SP_STX) + deframer->state = RAVE_SP_EXPECT_DATA; + break; + + case RAVE_SP_EXPECT_DATA: + /* + * Treat special byte values first + */ + switch (byte) { + case RAVE_SP_ETX: + rave_sp_receive_frame(sp, + deframer->data, + deframer->length); + /* + * Once we extracted a complete frame + * out of a stream, we call it done + * and proceed to bailing out while + * resetting the framer to initial + * state, regardless if we've consumed + * all of the stream or not. + */ + goto reset_framer; + case RAVE_SP_STX: + dev_warn(dev, "Bad frame: STX before ETX\n"); + /* + * If we encounter second "start of + * the frame" marker before seeing + * corresponding "end of frame", we + * reset the framer and ignore both: + * frame started by first SOF and + * frame started by current SOF. + * + * NOTE: The above means that only the + * frame started by third SOF, sent + * after this one will have a chance + * to get throught. + */ + goto reset_framer; + case RAVE_SP_DLE: + deframer->state = RAVE_SP_EXPECT_ESCAPED_DATA; + /* + * If we encounter escape sequence we + * need to skip it and collect the + * byte that follows. We do it by + * forcing the next iteration of the + * encompassing while loop. + */ + continue; + } + /* + * For the rest of the bytes, that are not + * speical snoflakes, we do the same thing + * that we do to escaped data - collect it in + * deframer buffer + */ + + /* FALLTHROUGH */ + + case RAVE_SP_EXPECT_ESCAPED_DATA: + deframer->data[deframer->length++] = byte; + + if (deframer->length == sizeof(deframer->data)) { + dev_warn(dev, "Bad frame: Too long\n"); + /* + * If the amount of data we've + * accumulated for current frame so + * far starts to exceed the capacity + * of deframer's buffer, there's + * nothing else we can do but to + * discard that data and start + * assemblying a new frame again + */ + goto reset_framer; + } + + /* + * We've extracted out special byte, now we + * can go back to regular data collecting + */ + deframer->state = RAVE_SP_EXPECT_DATA; + break; + } + } + + /* + * The only way to get out of the above loop and end up here + * is throught consuming all of the supplied data, so here we + * report that we processed it all. + */ + return size; + +reset_framer: + /* + * NOTE: A number of codepaths that will drop us here will do + * so before consuming all 'size' bytes of the data passed by + * serdev layer. We rely on the fact that serdev layer will + * re-execute this handler with the remainder of the Rx bytes + * once we report actual number of bytes that we processed. + */ + deframer->state = RAVE_SP_EXPECT_SOF; + deframer->length = 0; + + return src - buf; +} + +static int rave_sp_rdu1_cmd_translate(enum rave_sp_command command) +{ + if (command >= RAVE_SP_CMD_STATUS && + command <= RAVE_SP_CMD_CONTROL_EVENTS) + return command; + + return -EINVAL; +} + +static int rave_sp_rdu2_cmd_translate(enum rave_sp_command command) +{ + if (command >= RAVE_SP_CMD_GET_FIRMWARE_VERSION && + command <= RAVE_SP_CMD_GET_GPIO_STATE) + return command; + + if (command == RAVE_SP_CMD_REQ_COPPER_REV) { + /* + * As per RDU2 ICD 3.4.47 CMD_GET_COPPER_REV code is + * different from that for RDU1 and it is set to 0x28. + */ + return 0x28; + } + + return rave_sp_rdu1_cmd_translate(command); +} + +static int rave_sp_default_cmd_translate(enum rave_sp_command command) +{ + /* + * All of the following command codes were taken from "Table : + * Communications Protocol Message Types" in section 3.3 + * "MESSAGE TYPES" of Rave PIC24 ICD. + */ + switch (command) { + case RAVE_SP_CMD_GET_FIRMWARE_VERSION: + return 0x11; + case RAVE_SP_CMD_GET_BOOTLOADER_VERSION: + return 0x12; + case RAVE_SP_CMD_BOOT_SOURCE: + return 0x14; + case RAVE_SP_CMD_SW_WDT: + return 0x1C; + case RAVE_SP_CMD_RESET: + return 0x1E; + case RAVE_SP_CMD_RESET_REASON: + return 0x1F; + default: + return -EINVAL; + } +} + +static const struct rave_sp_checksum rave_sp_checksum_8b2c = { + .length = 1, + .subroutine = csum_8b2c, +}; + +static const struct rave_sp_checksum rave_sp_checksum_ccitt = { + .length = 2, + .subroutine = csum_ccitt, +}; + +static const struct rave_sp_variant rave_sp_legacy = { + .checksum = &rave_sp_checksum_8b2c, + .cmd = { + .translate = rave_sp_default_cmd_translate, + }, +}; + +static const struct rave_sp_variant rave_sp_rdu1 = { + .checksum = &rave_sp_checksum_8b2c, + .cmd = { + .translate = rave_sp_rdu1_cmd_translate, + }, +}; + +static const struct rave_sp_variant rave_sp_rdu2 = { + .checksum = &rave_sp_checksum_ccitt, + .cmd = { + .translate = rave_sp_rdu2_cmd_translate, + }, +}; + +static const struct of_device_id rave_sp_dt_ids[] = { + { .compatible = "zii,rave-sp-niu", .data = &rave_sp_legacy }, + { .compatible = "zii,rave-sp-mezz", .data = &rave_sp_legacy }, + { .compatible = "zii,rave-sp-esb", .data = &rave_sp_legacy }, + { .compatible = "zii,rave-sp-rdu1", .data = &rave_sp_rdu1 }, + { .compatible = "zii,rave-sp-rdu2", .data = &rave_sp_rdu2 }, + { /* sentinel */ } +}; + +static const struct serdev_device_ops rave_sp_serdev_device_ops = { + .receive_buf = rave_sp_receive_buf, + .write_wakeup = serdev_device_write_wakeup, +}; + +static int rave_sp_probe(struct serdev_device *serdev) +{ + struct device *dev = &serdev->dev; + struct rave_sp *sp; + u32 baud; + int ret; + + if (of_property_read_u32(dev->of_node, "current-speed", &baud)) { + dev_err(dev, + "'current-speed' is not specified in device node\n"); + return -EINVAL; + } + + sp = devm_kzalloc(dev, sizeof(*sp), GFP_KERNEL); + if (!sp) + return -ENOMEM; + + sp->serdev = serdev; + dev_set_drvdata(dev, sp); + + sp->variant = of_device_get_match_data(dev); + if (!sp->variant) + return -ENODEV; + + mutex_init(&sp->bus_lock); + mutex_init(&sp->reply_lock); + BLOCKING_INIT_NOTIFIER_HEAD(&sp->event_notifier_list); + + serdev_device_set_client_ops(serdev, &rave_sp_serdev_device_ops); + ret = devm_serdev_device_open(dev, serdev); + if (ret) + return ret; + + serdev_device_set_baudrate(serdev, baud); + + return devm_of_platform_populate(dev); +} + +MODULE_DEVICE_TABLE(of, rave_sp_dt_ids); + +static struct serdev_device_driver rave_sp_drv = { + .probe = rave_sp_probe, + .driver = { + .name = "rave-sp", + .of_match_table = rave_sp_dt_ids, + }, +}; +module_serdev_device_driver(rave_sp_drv); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Andrey Vostrikov "); +MODULE_AUTHOR("Nikita Yushchenko "); +MODULE_AUTHOR("Andrey Smirnov "); +MODULE_DESCRIPTION("RAVE SP core driver"); -- cgit v1.2.3