mmc:sdhci-bayhub:fix Qualcomm sd host 7180 SD card compatibility issue
Subsystems (3)
PATCH v3 1/1 mmc:sdhci-bayhub:fix Qualcomm sd host 7180 SD card compatibility issue
Improve the signal integrity for long SD bus trace by using SC7180+GGC SD host redriver chip
1.GGC is a SD bus signal re-timing IC that has been paired with the SC7180 sometimes.
2.The key points are initialized GGC chip during SD initialization and use GGC special tuning flow to re-timing SD bus signal. 3.GGC resource is initialized for GGC chip during Qualcomm host probe:
3.1 GGC structure initialization
3.2 GGC GPIO resource apply
3.3 Implement GGC chip special initialization function with "host->mmc_host_ops.init_card" callback.
3.4 Reload host->mmc->detect with GGC chip special initiation flow.
3.5 Reload the host->mmc_host_ops.execute_tuning with GGC chip special tuning flow.
4.The function of the patch is already verified on Chrome OS, and Google request us to submit the patch to Linux for them future use. 5.GGC can work with any other standard SDHCI controller to improve SD signal SI and Timing.
6.GGC has cooperated with Intel/Qualcomm/MTK/SPRD sd host already and work well.
---
changes in V3:
1.add static declare for the internal functions.
2.remove some unused function base on V2.
changes in V2:
1.rebase the patch on kernel version 5.16.
2.remove some unused variables and functions base on V1. 3.add some annotation in code to distinguish bayhub GGC patch.
changes in V1:
1.copy Qualcomm driver base from sdhci-msm.c to sdhci-bayhub.c 2.implement the BH201 chip initialization function at sdhci-bayhub.c 3.implement the BH201 chip power control function at sdhci-bayhub.c 4.implement the BH201 chip tuning function at sdhci-bayhub.c 5.re-implement mmc_rescan to match BH201 mode switch flow at sdhci-bayhub.c
--- .../devicetree/bindings/mmc/sdhci-msm.txt | 1 + drivers/mmc/host/Makefile | 1 + drivers/mmc/host/sdhci-bayhub.c | 6761 +++++++++++++++++ 3 files changed, 6763 insertions(+) create mode 100644 drivers/mmc/host/sdhci-bayhub.c
Documentation/devicetree/bindings/mmc/sdhci-msm.txt +1 -0
@@ -24,6 +24,7 @@ Required properties: "qcom,sdm845-sdhci", "qcom,sdhci-msm-v5" "qcom,sdx55-sdhci", "qcom,sdhci-msm-v5"; "qcom,sm8250-sdhci", "qcom,sdhci-msm-v5" + "qcom,sc7180-ggc-sdhci", "qcom,sdhci-msm-bayhub-v5" NOTE that some old device tree files may be floating around that only have the string "qcom,sdhci-msm-v4" without the SoC compatible string but doing that should be considered a deprecated practice.
drivers/mmc/host/Makefile +1 -0
@@ -92,6 +92,7 @@ obj-$(CONFIG_MMC_SDHCI_OF_SPARX5) += sdhci-of-sparx5.o obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o obj-$(CONFIG_MMC_SDHCI_IPROC) += sdhci-iproc.o obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o +obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-bayhub.o obj-$(CONFIG_MMC_SDHCI_ST) += sdhci-st.o obj-$(CONFIG_MMC_SDHCI_MICROCHIP_PIC32) += sdhci-pic32.o obj-$(CONFIG_MMC_SDHCI_BRCMSTB) += sdhci-brcmstb.o
drivers/mmc/host/sdhci-bayhub.c new file +6761 -0
2,986 lines not on this page · raw message
The rest of this diff is not shown. View the raw message.
base-commit: c9e6606c7fe92b50a02ce51dda82586ebdf99b48
Signature · 1 line
2.32.0
Part of the diff is not shown here. View the raw message.
Quoted 4 lines · Chevron Li
Improve the signal integrity for long SD bus trace by using SC7180+GGC SD host redriver chip
1.GGC is a SD bus signal re-timing IC that has been paired with the SC7180 sometimes.
2.The key points are initialized GGC chip during SD initialization and use GGC special tuning flow to re-timing SD bus signal. 3.GGC resource is initialized for GGC chip during Qualcomm host probe:
Quoted 8 lines
3.1 GGC structure initialization
3.2 GGC GPIO resource apply
3.3 Implement GGC chip special initialization function with "host->mmc_host_ops.init_card" callback.
3.4 Reloadhost->mmc->detectwith GGC chip special initiation flow.
3.5 Reload thehost->mmc_host_ops.execute_tuningwith GGC chip special tuning flow.
4.The function of the patch is already verified on Chrome OS, and Google request us to submit the patch to Linux for them future use. 5.GGC can work with any other standard SDHCI controller to improve SD signal SI and Timing.
6.GGC has cooperated with Intel/Qualcomm/MTK/SPRD sd host already and work well.
Hi Chevron,
I think the forking of sdhci-msm into a new driver, in order to support the redriver, is a showstopper for this. Further more, lumping that and all your modifications into a single change makes it impossible to review your changes to the file.
You also missed the relevant MMC/SDHCI maintainers and mailing lists when sending this, so please use ./scripts/get_maintainer.pl to get the appropriate recipients list.
Regards,
Bjorn
Quoted 6,821 lines
Signed-off-by Chevron Li <[email protected]>---
changes in V3:
1.add static declare for the internal functions.
2.remove some unused function base on V2.changes in V2:
1.rebase the patch on kernel version 5.16.
2.remove some unused variables and functions base on V1. 3.add some annotation in code to distinguish bayhub GGC patch.changes in V1:
1.copy Qualcomm driver base fromsdhci-msm.ctosdhci-bayhub.c2.implement the BH201 chip initialization function atsdhci-bayhub.c3.implement the BH201 chip power control function atsdhci-bayhub.c4.implement the BH201 chip tuning function atsdhci-bayhub.c5.re-implementmmc_rescanto match BH201 mode switch flow atsdhci-bayhub.c--- .../devicetree/bindings/mmc/sdhci-msm.txt | 1 + drivers/mmc/host/Makefile | 1 + drivers/mmc/host/sdhci-bayhub.c | 6761 +++++++++++++++++ 3 files changed, 6763 insertions(+) create mode 100644 drivers/mmc/host/sdhci-bayhub.cDocumentation/devicetree/bindings/mmc/sdhci-msm.txt +1
-0@@ -24,6 +24,7 @@ Required properties: "qcom,sdm845-sdhci", "qcom,sdhci-msm-v5" "qcom,sdx55-sdhci", "qcom,sdhci-msm-v5"; "qcom,sm8250-sdhci", "qcom,sdhci-msm-v5" + "qcom,sc7180-ggc-sdhci", "qcom,sdhci-msm-bayhub-v5" NOTE that some old device tree files may be floating around that only have the string "qcom,sdhci-msm-v4" without the SoC compatible string but doing that should be considered a deprecated practice.drivers/mmc/host/Makefile +1
-0@@ -92,6 +92,7 @@ obj-$(CONFIG_MMC_SDHCI_OF_SPARX5) += sdhci-of-sparx5.o obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o obj-$(CONFIG_MMC_SDHCI_IPROC) += sdhci-iproc.o obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o +obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-bayhub.o obj-$(CONFIG_MMC_SDHCI_ST) += sdhci-st.o obj-$(CONFIG_MMC_SDHCI_MICROCHIP_PIC32) += sdhci-pic32.o obj-$(CONFIG_MMC_SDHCI_BRCMSTB) += sdhci-brcmstb.odrivers/mmc/host/sdhci-bayhub.c new file +6761
-0@@ -0,0 +1,6761 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* +* Bayhub Technologies, Inc. BH201 SDHCI bridge IC for +* VENDOR SDHCI platform driver source file +* +* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. +*/ + +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/delay.h> +#include <linux/mmc/mmc.h> +#include <linux/pm_runtime.h> +#include <linux/pm_opp.h> +#include <linux/slab.h> +#include <linux/iopoll.h> +#include <linux/regulator/consumer.h> +#include <linux/interconnect.h> +#include <linux/pinctrl/consumer.h> +#include <linux/mmc/host.h> +#include <linux/mmc/card.h> +#include <linux/mmc/sd.h> +#include <linux/io.h> +#include <linux/of_gpio.h> +#include <linux/scatterlist.h> + +#include "../core/sd_ops.h" +#include "../core/sdio_ops.h" +#include "../core/mmc_ops.h" +#include "../core/sd.h" +#include "../core/bus.h" +#include "../core/host.h" +#include "../core/card.h" +#include "../core/pwrseq.h" +#include "../core/core.h" + +#include "sdhci-pltfm.h" +#include "cqhci.h" + +#define SD_FNC_AM_SDR50 0x2 +#define SD_FNC_AM_SDR104 0x3 +#define BIT_PASS_MASK (0x7ff) +#define SDR104_MANUAL_INJECT 0x3ff +#define SDR50_MANUAL_INJECT 0x77f + +#define TUNING_RING_IDX (x) ((x) % TUNING_PHASE_SIZE) +#define GET_IDX_VALUE (tb, x) (tb & (1 << (x))) +#define GENERATE_IDX_VALUE (x) (1 << (x)) +#define GET_TUNING_RING_IDX_VALUE (tb, x) \ + (tb & (1 << TUNING_RING_IDX(x))) +#define GENERATE_TUNING_RING_IDX_VALUE (x) \ + (1 << TUNING_RING_IDX(x)) +#define MAX_CFG_BIT_VAL (383) + +/* +* The code porting from sdhci-msm.c part-1 start +* We need to porting the code from sdhci-msm.c for below reasons: +* 1.GGC is a SD bus signal re-timing IC, has no vendor ID, need to work with sd host IC together +* 2.Qualcomm engineer doesn’t allow us to add code into their source code file directly +*/ +#define CORE_MCI_VERSION 0x50 +#define CORE_VERSION_MAJOR_SHIFT 28 +#define CORE_VERSION_MAJOR_MASK (0xf << CORE_VERSION_MAJOR_SHIFT) +#define CORE_VERSION_MINOR_MASK 0xff + +#define CORE_MCI_GENERICS 0x70 +#define SWITCHABLE_SIGNALING_VOLTAGE BIT(29) + +#define HC_MODE_EN 0x1 +#define CORE_POWER 0x0 +#define CORE_SW_RST BIT(7) +#define FF_CLK_SW_RST_DIS BIT(13) + +#define CORE_PWRCTL_BUS_OFF BIT(0) +#define CORE_PWRCTL_BUS_ON BIT(1) +#define CORE_PWRCTL_IO_LOW BIT(2) +#define CORE_PWRCTL_IO_HIGH BIT(3) +#define CORE_PWRCTL_BUS_SUCCESS BIT(0) +#define CORE_PWRCTL_BUS_FAIL BIT(1) +#define CORE_PWRCTL_IO_SUCCESS BIT(2) +#define CORE_PWRCTL_IO_FAIL BIT(3) +#define REQ_BUS_OFF BIT(0) +#define REQ_BUS_ON BIT(1) +#define REQ_IO_LOW BIT(2) +#define REQ_IO_HIGH BIT(3) +#define INT_MASK 0xf +#define MAX_PHASES 16 +#define CORE_DLL_LOCK BIT(7) +#define CORE_DDR_DLL_LOCK BIT(11) +#define CORE_DLL_EN BIT(16) +#define CORE_CDR_EN BIT(17) +#define CORE_CK_OUT_EN BIT(18) +#define CORE_CDR_EXT_EN BIT(19) +#define CORE_DLL_PDN BIT(29) +#define CORE_DLL_RST BIT(30) +#define CORE_CMD_DAT_TRACK_SEL BIT(0) + +#define CORE_DDR_CAL_EN BIT(0) +#define CORE_FLL_CYCLE_CNT BIT(18) +#define CORE_DLL_CLOCK_DISABLE BIT(21) + +#define DLL_USR_CTL_POR_VAL 0x10800 +#define ENABLE_DLL_LOCK_STATUS BIT(26) +#define FINE_TUNE_MODE_EN BIT(27) +#define BIAS_OK_SIGNAL BIT(29) + +#define DLL_CONFIG_3_LOW_FREQ_VAL 0x08 +#define DLL_CONFIG_3_HIGH_FREQ_VAL 0x10 + +#define CORE_VENDOR_SPEC_POR_VAL 0xa9c +#define CORE_CLK_PWRSAVE BIT(1) +#define CORE_HC_MCLK_SEL_DFLT (2 << 8) +#define CORE_HC_MCLK_SEL_HS400 (3 << 8) +#define CORE_HC_MCLK_SEL_MASK (3 << 8) +#define CORE_IO_PAD_PWR_SWITCH_EN BIT(15) +#define CORE_IO_PAD_PWR_SWITCH BIT(16) +#define CORE_HC_SELECT_IN_EN BIT(18) +#define CORE_HC_SELECT_IN_HS400 (6 << 19) +#define CORE_HC_SELECT_IN_MASK (7 << 19) + +#define CORE_3_0V_SUPPORT BIT(25) +#define CORE_1_8V_SUPPORT BIT(26) +#define CORE_VOLT_SUPPORT (CORE_3_0V_SUPPORT | CORE_1_8V_SUPPORT) + +#define CORE_CSR_CDC_CTLR_CFG0 0x130 +#define CORE_SW_TRIG_FULL_CALIB BIT(16) +#define CORE_HW_AUTOCAL_ENA BIT(17) + +#define CORE_CSR_CDC_CTLR_CFG1 0x134 +#define CORE_CSR_CDC_CAL_TIMER_CFG0 0x138 +#define CORE_TIMER_ENA BIT(16) + +#define CORE_CSR_CDC_CAL_TIMER_CFG1 0x13C +#define CORE_CSR_CDC_REFCOUNT_CFG 0x140 +#define CORE_CSR_CDC_COARSE_CAL_CFG 0x144 +#define CORE_CDC_OFFSET_CFG 0x14C +#define CORE_CSR_CDC_DELAY_CFG 0x150 +#define CORE_CDC_SLAVE_DDA_CFG 0x160 +#define CORE_CSR_CDC_STATUS0 0x164 +#define CORE_CALIBRATION_DONE BIT(0) + +#define CORE_CDC_ERROR_CODE_MASK 0x7000000 + +#define CORE_CSR_CDC_GEN_CFG 0x178 +#define CORE_CDC_SWITCH_BYPASS_OFF BIT(0) +#define CORE_CDC_SWITCH_RC_EN BIT(1) + +#define CORE_CDC_T4_DLY_SEL BIT(0) +#define CORE_CMDIN_RCLK_EN BIT(1) +#define CORE_START_CDC_TRAFFIC BIT(6) + +#define CORE_PWRSAVE_DLL BIT(3) + +#define DDR_CONFIG_POR_VAL 0x80040873 + + +#define INVALID_TUNING_PHASE -1 +#define SDHCI_MSM_MIN_CLOCK 400000 +#define CORE_FREQ_100MHZ (100 * 1000 * 1000) + +#define CDR_SELEXT_SHIFT 20 +#define CDR_SELEXT_MASK (0xf << CDR_SELEXT_SHIFT) +#define CMUX_SHIFT_PHASE_SHIFT 24 +#define CMUX_SHIFT_PHASE_MASK (7 << CMUX_SHIFT_PHASE_SHIFT) + +#define MSM_MMC_AUTOSUSPEND_DELAY_MS 50 + +/* Timeout value to avoid infinite waiting for pwr_irq */ +#define MSM_PWR_IRQ_TIMEOUT_MS 5000 + +/* Max load for eMMC Vdd-io supply */ +#define MMC_VQMMC_MAX_LOAD_UA 325000 + +#define msm_bayhub_host_readl (msm_bayhub_host, host, offset) \ + msm_bayhub_host->var_ops->msm_bayhub_readl_relaxed(host, offset) + +#define msm_bayhub_host_writel (msm_bayhub_host, val, host, offset) \ + msm_bayhub_host->var_ops->msm_bayhub_writel_relaxed(val, host, offset) + +/* CQHCI vendor specific registers */ +#define CQHCI_VENDOR_CFG1 0xA00 +#define CQHCI_VENDOR_DIS_RST_ON_CQ_EN (0x3 << 13) +/* The code porting from sdhci-msm.c part-1 end */ + +/* +* Bayhub patch part-1 start +* This part define the GGC used structure +* The GGC chip can work with standard SD host controller +* GGC is the chip IC name and it's a SD bus signal re-timing IC +* Aim at high-speed SD mode(ex.SDR104) and long wire of SD signals +*/ +#define TUNING_PHASE_SIZE 11 +static const u32 ggc_cfg_data[16 ] = { +0x07000000 ,0x07364022 ,0x01015412 ,0x01062400 , +0x10400076 ,0x00025432 ,0x01046076 ,0x62011000 , +0x30503106 ,0x64141711 ,0x10057513 ,0x00336200 , +0x00020006 ,0x40000400 ,0x12200310 ,0x3A314177 +}; + +struct ggc_bus_mode_cfg { +u32 tx_selb_tb[TUNING_PHASE_SIZE ]; +u32 all_selb_tb[TUNING_PHASE_SIZE ]; +u32 tx_selb_failed_history; +int bus_mode; +int default_sela; +int default_selb; +u32 default_delaycode; +u32 dll_voltage_unlock_cnt[4 ]; +u32 max_delaycode; +u32 min_delaycode; +u32 delaycode_narrowdown_index; +u32 fail_phase; +}; + +enum tuning_state { +NO_TUNING =0 , +OUTPUT_PASS_TYPE =1 , +SET_PHASE_FAIL_TYPE =2 , +TUNING_FAIL_TYPE =3 , +READ_STATUS_FAIL_TYPE =4 , +TUNING_CMD7_TIMEOUT =5 , +RETUNING_CASE =6 , +}; + + +struct ggc_reg_op { +u32 ofs; +u32 mask; +u32 value; +}; + +static const char *const op_dbg_str[] = { +"no tuning" , +"pass" , +"set_phase_fail" , +"tuning fail" , +"read status fail" , +"tuning CMD7 timeout" , +"retuning case" +}; + +struct ggc_adaptor { +struct ggc_bus_mode_cfg sdr50; +struct ggc_bus_mode_cfg sdr104; +struct ggc_bus_mode_cfg *cur_bus_mode; + +struct ggc_reg_op pha_stas_rx_low32; +struct ggc_reg_op pha_stas_rx_high32; +struct ggc_reg_op pha_stas_tx_low32; +struct ggc_reg_op pha_stas_tx_high32; +struct ggc_reg_op dll_sela_after_mask; +struct ggc_reg_op dll_selb_after_mask; + +struct ggc_reg_op dll_delay_100m_backup; +struct ggc_reg_op dll_delay_200m_backup; + +struct ggc_reg_op dll_sela_100m_cfg; +struct ggc_reg_op dll_sela_200m_cfg; +struct ggc_reg_op dll_selb_100m_cfg; +struct ggc_reg_op dll_selb_200m_cfg; +struct ggc_reg_op dll_selb_100m_cfg_en; +struct ggc_reg_op dll_selb_200m_cfg_en; +struct ggc_reg_op internl_tuning_en_100m; +struct ggc_reg_op internl_tuning_en_200m; +struct ggc_reg_op cmd19_cnt_cfg; + +struct ggc_reg_op inject_failure_for_tuning_enable_cfg; +struct ggc_reg_op inject_failure_for_200m_tuning_cfg; +struct ggc_reg_op inject_failure_for_100m_tuning_cfg; + +int def_sela_100m; +int def_sela_200m; +int def_selb_100m; +int def_selb_200m; + +u32 gg_reg_cur[16 ]; +u8 cur_read_buf[512 ]; + +bool dll_unlock_reinit_flg; +u8 driver_strength_reinit_flg; +bool tuning_cmd7_timeout_reinit_flg; +u32 tuning_cmd7_timeout_reinit_cnt; +u32 ggc_cur_sela; +bool selx_tuning_done_flag; +u32 ggc_cmd_tx_selb_failed_range; +int ggc_sw_selb_tuning_first_selb; +enum tuning_state ggc_sela_tuning_result[11 ]; +int dll_voltage_scan_map[4 ]; +int cur_dll_voltage_idx; +int sdr50_notuning_sela_inject_flag; +int sdr50_notuning_crc_error_flag; +u32 sdr50_notuning_sela_rx_inject; +u32 bh201_sdr50_sela_sw_inject; +u32 bh201_sdr50_selb_hw_inject; +u32 bh201_sdr104_selb_hw_inject; +u32 bh201_drive_strength; +bool tuning_in_progress; +int pwr_gpio;/* External power enable pin for Redriver IC */ +int det_gpio; +}; +/* Bayhub patch part-1 end */ + +/* The code porting from sdhci-msm.c part-2 start */ +struct sdhci_msm_bayhub_offset { +u32 core_hc_mode; +u32 core_mci_data_cnt; +u32 core_mci_status; +u32 core_mci_fifo_cnt; +u32 core_mci_version; +u32 core_generics; +u32 core_testbus_config; +u32 core_testbus_sel2_bit; +u32 core_testbus_ena; +u32 core_testbus_sel2; +u32 core_pwrctl_status; +u32 core_pwrctl_mask; +u32 core_pwrctl_clear; +u32 core_pwrctl_ctl; +u32 core_sdcc_debug_reg; +u32 core_dll_config; +u32 core_dll_status; +u32 core_vendor_spec; +u32 core_vendor_spec_adma_err_addr0; +u32 core_vendor_spec_adma_err_addr1; +u32 core_vendor_spec_func2; +u32 core_vendor_spec_capabilities0; +u32 core_ddr_200_cfg; +u32 core_vendor_spec3; +u32 core_dll_config_2; +u32 core_dll_config_3; +u32 core_ddr_config_old;/* Applicable to sdcc minor ver < 0x49 */ +u32 core_ddr_config; +u32 core_dll_usr_ctl;/* Present on SDCC5.1 onwards */ +}; + +static const struct sdhci_msm_bayhub_offset sdhci_msm_bayhub_v5_offset = { + .core_mci_data_cnt =0x35c , + .core_mci_status =0x324 , + .core_mci_fifo_cnt =0x308 , + .core_mci_version =0x318 , + .core_generics =0x320 , + .core_testbus_config =0x32c , + .core_testbus_sel2_bit =3 , + .core_testbus_ena = (1 <<31 ), + .core_testbus_sel2 = (1 <<3 ), + .core_pwrctl_status =0x240 , + .core_pwrctl_mask =0x244 , + .core_pwrctl_clear =0x248 , + .core_pwrctl_ctl =0x24c , + .core_sdcc_debug_reg =0x358 , + .core_dll_config =0x200 , + .core_dll_status =0x208 , + .core_vendor_spec =0x20c , + .core_vendor_spec_adma_err_addr0 =0x214 , + .core_vendor_spec_adma_err_addr1 =0x218 , + .core_vendor_spec_func2 =0x210 , + .core_vendor_spec_capabilities0 =0x21c , + .core_ddr_200_cfg =0x224 , + .core_vendor_spec3 =0x250 , + .core_dll_config_2 =0x254 , + .core_dll_config_3 =0x258 , + .core_ddr_config =0x25c , + .core_dll_usr_ctl =0x388 , +}; + +struct sdhci_msm_bayhub_variant_ops { +u32 (*msm_bayhub_readl_relaxed)(struct sdhci_host *host,u32 offset); +void (*msm_bayhub_writel_relaxed)(u32 val,struct sdhci_host *host, +u32 offset); +}; + +/* +* From V5, register spaces have changed. Wrap this info in a structure +* and choose the data_structure based on version info mentioned in DT. +*/ +struct sdhci_msm_bayhub_variant_info { +bool mci_removed; +bool restore_dll_config; +bool uses_tassadar_dll; +const struct sdhci_msm_bayhub_variant_ops *var_ops; +const struct sdhci_msm_bayhub_offset *offset; +}; + +struct sdhci_msm_bayhub_host { +struct platform_device *pdev; +void __iomem *core_mem;/* MSM SDCC mapped address */ +int pwr_irq;/* power irq */ +struct clk *bus_clk;/* SDHC bus voter clock */ +struct clk *xo_clk;/* TCXO clk needed for FLL feature of cm_dll*/ +struct clk_bulk_data bulk_clks[4 ];/* core, iface, cal, sleep clocks */ +unsigned long clk_rate; +struct mmc_host *mmc; +struct opp_table *opp_table; +bool has_opp_table; +bool use_14lpp_dll_reset; +bool tuning_done; +bool calibration_done; +u8 saved_tuning_phase; +bool use_cdclp533; +u32 curr_pwr_state; +u32 curr_io_level; +wait_queue_head_t pwr_irq_wait; +bool pwr_irq_flag; +u32 caps_0; +bool mci_removed; +bool restore_dll_config; +const struct sdhci_msm_bayhub_variant_ops *var_ops; +const struct sdhci_msm_bayhub_offset *offset; +bool use_cdr; +u32 transfer_mode; +bool updated_ddr_cfg; +bool uses_tassadar_dll; +u32 dll_config; +u32 ddr_config; +bool vqmmc_enabled; +struct ggc_adaptor ggc;/* GGC chip structure is added into Qualcomm host */ +}; + +static const struct sdhci_msm_bayhub_offset *sdhci_priv_msm_bayhub_offset (struct sdhci_host *host) +{ +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_bayhub_host *msm_bayhub_host =sdhci_pltfm_priv (pltfm_host); + +return msm_bayhub_host->offset; +} + +/* +* APIs to read/write to vendor specific registers which were there in the +* core_mem region before MCI was removed. +*/ +static u32 sdhci_msm_bayhub_v5_variant_readl_relaxed (struct sdhci_host *host, +u32 offset) +{ +return readl_relaxed (host->ioaddr + offset); +} + +static void sdhci_msm_bayhub_v5_variant_writel_relaxed (u32 val, +struct sdhci_host *host,u32 offset) +{ +writel_relaxed (val, host->ioaddr + offset); +} + +static unsigned int msm_bayhub_get_clock_mult_for_bus_mode (struct sdhci_host *host) +{ +struct mmc_ios ios = host->mmc->ios; +/* +* The SDHC requires internal clock frequency to be double the +* actual clock that will be set for DDR mode. The controller +* uses the faster clock(100/400MHz) for some of its parts and +* send the actual required clock (50/200MHz) to the card. +*/ +if (ios.timing ==MMC_TIMING_UHS_DDR50 || + ios.timing ==MMC_TIMING_MMC_DDR52 || + ios.timing ==MMC_TIMING_MMC_HS400 || + host->flags &SDHCI_HS400_TUNING ) +return 2 ; +return 1 ; +} + +static void msm_bayhub_set_clock_rate_for_bus_mode (struct sdhci_host *host, +unsigned int clock) +{ +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_bayhub_host *msm_bayhub_host =sdhci_pltfm_priv (pltfm_host); +struct mmc_ios curr_ios = host->mmc->ios; +struct clk *core_clk = msm_bayhub_host->bulk_clks[0 ].clk; +unsigned long achieved_rate; +unsigned int desired_rate; +unsigned int mult; +int rc; + + mult =msm_bayhub_get_clock_mult_for_bus_mode (host); + desired_rate = clock * mult; + rc =dev_pm_opp_set_rate (mmc_dev (host->mmc), desired_rate); +if (rc) { +pr_err ("%s: Failed to set clock at rate %u at timing %d\n" , +mmc_hostname (host->mmc), desired_rate, curr_ios.timing); +return ; + } + +/* +* Qualcomm clock drivers by default round clock _up_ if they can't +* make the requested rate. This is not good for SD. Yell if we +* encounter it. +*/ + achieved_rate =clk_get_rate (core_clk); +if (achieved_rate > desired_rate) +pr_warn ("%s: Card appears overclocked; req %u Hz, actual %lu Hz\n" , +mmc_hostname (host->mmc), desired_rate, achieved_rate); + host->mmc->actual_clock = achieved_rate / mult; + +/* Stash the rate we requested to use in sdhci_msm_bayhub_runtime_resume() */ + msm_bayhub_host->clk_rate = desired_rate; + +pr_debug ("%s: Setting clock at rate %lu at timing %d\n" , +mmc_hostname (host->mmc), achieved_rate, curr_ios.timing); +} + +/* Platform specific tuning */ +static inline int msm_bayhub_dll_poll_ck_out_en (struct sdhci_host *host,u8 poll) +{ +u32 wait_cnt =50 ; +u8 ck_out_en; +struct mmc_host *mmc = host->mmc; +const struct sdhci_msm_bayhub_offset *msm_bayhub_offset = +sdhci_priv_msm_bayhub_offset (host); + +/* Poll for CK_OUT_EN bit. max. poll time = 50us */ + ck_out_en = !!(readl_relaxed (host->ioaddr + + msm_bayhub_offset->core_dll_config) &CORE_CK_OUT_EN ); + +while (ck_out_en != poll) { +if (--wait_cnt ==0 ) { +dev_err (mmc_dev (mmc),"%s: CK_OUT_EN bit is not %d\n" , +mmc_hostname (mmc), poll); +return -ETIMEDOUT ; + } +udelay (1 ); + + ck_out_en = !!(readl_relaxed (host->ioaddr + + msm_bayhub_offset->core_dll_config) &CORE_CK_OUT_EN ); + } + +return 0 ; +} + +static int msm_bayhub_config_cm_dll_phase (struct sdhci_host *host,u8 phase) +{ +int rc; +static const u8 grey_coded_phase_table[] = { +0x0 ,0x1 ,0x3 ,0x2 ,0x6 ,0x7 ,0x5 ,0x4 , +0xc ,0xd ,0xf ,0xe ,0xa ,0xb ,0x9 ,0x8 + }; +unsigned long flags; +u32 config; +struct mmc_host *mmc = host->mmc; +const struct sdhci_msm_bayhub_offset *msm_bayhub_offset = +sdhci_priv_msm_bayhub_offset (host); + +if (phase >0xf ) +return -EINVAL ; + +spin_lock_irqsave (&host->lock, flags); + + config =readl_relaxed (host->ioaddr + msm_bayhub_offset->core_dll_config); + config &= ~(CORE_CDR_EN |CORE_CK_OUT_EN ); + config |= (CORE_CDR_EXT_EN |CORE_DLL_EN ); +writel_relaxed (config, host->ioaddr + msm_bayhub_offset->core_dll_config); + +/* Wait until CK_OUT_EN bit of DLL_CONFIG register becomes '0' */ + rc =msm_bayhub_dll_poll_ck_out_en (host,0 ); +if (rc) + goto err_out; + +/* +* Write the selected DLL clock output phase (0 ... 15) +* to CDR_SELEXT bit field of DLL_CONFIG register. +*/ + config =readl_relaxed (host->ioaddr + msm_bayhub_offset->core_dll_config); + config &= ~CDR_SELEXT_MASK ; + config |= grey_coded_phase_table[phase] <<CDR_SELEXT_SHIFT ; +writel_relaxed (config, host->ioaddr + msm_bayhub_offset->core_dll_config); + + config =readl_relaxed (host->ioaddr + msm_bayhub_offset->core_dll_config); + config |=CORE_CK_OUT_EN ; +writel_relaxed (config, host->ioaddr + msm_bayhub_offset->core_dll_config); + +/* Wait until CK_OUT_EN bit of DLL_CONFIG register becomes '1' */ + rc =msm_bayhub_dll_poll_ck_out_en (host,1 ); +if (rc) + goto err_out; + + config =readl_relaxed (host->ioaddr + msm_bayhub_offset->core_dll_config); + config |=CORE_CDR_EN ; + config &= ~CORE_CDR_EXT_EN ; +writel_relaxed (config, host->ioaddr + msm_bayhub_offset->core_dll_config); + goto out; + +err_out: +dev_err (mmc_dev (mmc),"%s: Failed to set DLL phase: %d\n" , +mmc_hostname (mmc), phase); +out: +spin_unlock_irqrestore (&host->lock, flags); +return rc; +} + +/* +* Find out the greatest range of consecuitive selected +* DLL clock output phases that can be used as sampling +* setting for SD3.0 UHS-I card read operation (in SDR104 +* timing mode) or for eMMC4.5 card read operation (in +* HS400/HS200 timing mode). +* Select the 3/4 of the range and configure the DLL with the +* selected DLL clock output phase. +*/ + +static int msm_bayhub_find_most_appropriate_phase (struct sdhci_host *host, +u8 *phase_table,u8 total_phases) +{ +int ret; +u8 ranges[MAX_PHASES ][MAX_PHASES ] = { {0 }, {0 } }; +u8 phases_per_row[MAX_PHASES ] = {0 }; +int row_index =0 , col_index =0 , selected_row_index =0 , curr_max =0 ; +int i, cnt, phase_0_raw_index =0 , phase_15_raw_index =0 ; +bool phase_0_found = false, phase_15_found = false; +struct mmc_host *mmc = host->mmc; + +if (!total_phases || (total_phases >MAX_PHASES )) { +dev_err (mmc_dev (mmc),"%s: Invalid argument: total_phases=%d\n" , +mmc_hostname (mmc), total_phases); +return -EINVAL ; + } + +for (cnt =0 ; cnt < total_phases; cnt++) { + ranges[row_index][col_index] = phase_table[cnt]; + phases_per_row[row_index] +=1 ; + col_index++; + +if ((cnt +1 ) == total_phases) { +continue ; +/* check if next phase in phase_table is consecutive or not */ + }else if ((phase_table[cnt] +1 ) != phase_table[cnt +1 ]) { + row_index++; + col_index =0 ; + } + } + +if (row_index >=MAX_PHASES ) +return -EINVAL ; + +/* Check if phase-0 is present in first valid window? */ +if (!ranges[0 ][0 ]) { + phase_0_found = true; + phase_0_raw_index =0 ; +/* Check if cycle exist between 2 valid windows */ +for (cnt =1 ; cnt <= row_index; cnt++) { +if (phases_per_row[cnt]) { +for (i =0 ; i < phases_per_row[cnt]; i++) { +if (ranges[cnt][i] ==15 ) { + phase_15_found = true; + phase_15_raw_index = cnt; +break ; + } + } + } + } + } + +/* If 2 valid windows form cycle then merge them as single window */ +if (phase_0_found && phase_15_found) { +/* number of phases in raw where phase 0 is present */ +u8 phases_0 = phases_per_row[phase_0_raw_index]; +/* number of phases in raw where phase 15 is present */ +u8 phases_15 = phases_per_row[phase_15_raw_index]; + +if (phases_0 + phases_15 >=MAX_PHASES ) +/* +* If there are more than 1 phase windows then total +* number of phases in both the windows should not be +* more than or equal to MAX_PHASES. +*/ +return -EINVAL ; + +/* Merge 2 cyclic windows */ + i = phases_15; +for (cnt =0 ; cnt < phases_0; cnt++) { + ranges[phase_15_raw_index][i] = + ranges[phase_0_raw_index][cnt]; +if (++i >=MAX_PHASES ) +break ; + } + + phases_per_row[phase_0_raw_index] =0 ; + phases_per_row[phase_15_raw_index] = phases_15 + phases_0; + } + +for (cnt =0 ; cnt <= row_index; cnt++) { +if (phases_per_row[cnt] > curr_max) { + curr_max = phases_per_row[cnt]; + selected_row_index = cnt; + } + } + + i = (curr_max *3 ) /4 ; +if (i) + i--; + + ret = ranges[selected_row_index][i]; + +if (ret >=MAX_PHASES ) { + ret = -EINVAL ; +dev_err (mmc_dev (mmc),"%s: Invalid phase selected=%d\n" , +mmc_hostname (mmc), ret); + } + +return ret; +} + +static inline void msm_bayhub_cm_dll_set_freq (struct sdhci_host *host) +{ +u32 mclk_freq =0 , config; +const struct sdhci_msm_bayhub_offset *msm_bayhub_offset = +sdhci_priv_msm_bayhub_offset (host); + +/* Program the MCLK value to MCLK_FREQ bit field */ +if (host->clock <=112000000 ) + mclk_freq =0 ; +else if (host->clock <=125000000 ) + mclk_freq =1 ; +else if (host->clock <=137000000 ) + mclk_freq =2 ; +else if (host->clock <=150000000 ) + mclk_freq =3 ; +else if (host->clock <=162000000 ) + mclk_freq =4 ; +else if (host->clock <=175000000 ) + mclk_freq =5 ; +else if (host->clock <=187000000 ) + mclk_freq =6 ; +else if (host->clock <=200000000 ) + mclk_freq =7 ; + + config =readl_relaxed (host->ioaddr + msm_bayhub_offset->core_dll_config); + config &= ~CMUX_SHIFT_PHASE_MASK ; + config |= mclk_freq <<CMUX_SHIFT_PHASE_SHIFT ; +writel_relaxed (config, host->ioaddr + msm_bayhub_offset->core_dll_config); +} + +/* Initialize the DLL (Programmable Delay Line) */ +static int msm_bayhub_init_cm_dll (struct sdhci_host *host) +{ +struct mmc_host *mmc = host->mmc; +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_bayhub_host *msm_bayhub_host =sdhci_pltfm_priv (pltfm_host); +int wait_cnt =50 ; +unsigned long flags, xo_clk =0 ; +u32 config; +const struct sdhci_msm_bayhub_offset *msm_bayhub_offset = + msm_bayhub_host->offset; + +if (msm_bayhub_host->use_14lpp_dll_reset && !IS_ERR_OR_NULL (msm_bayhub_host->xo_clk)) + xo_clk =clk_get_rate (msm_bayhub_host->xo_clk); + +spin_lock_irqsave (&host->lock, flags); + +/* +* Make sure that clock is always enabled when DLL +* tuning is in progress. Keeping PWRSAVE ON may +* turn off the clock. +*/ + config =readl_relaxed (host->ioaddr + msm_bayhub_offset->core_vendor_spec); + config &= ~CORE_CLK_PWRSAVE ; +writel_relaxed (config, host->ioaddr + msm_bayhub_offset->core_vendor_spec); + +if (msm_bayhub_host->dll_config) +writel_relaxed (msm_bayhub_host->dll_config, + host->ioaddr + msm_bayhub_offset->core_dll_config); + +if (msm_bayhub_host->use_14lpp_dll_reset) { + config =readl_relaxed (host->ioaddr + + msm_bayhub_offset->core_dll_config); + config &= ~CORE_CK_OUT_EN ; +writel_relaxed (config, host->ioaddr + + msm_bayhub_offset->core_dll_config); + + config =readl_relaxed (host->ioaddr + + msm_bayhub_offset->core_dll_config_2); + config |=CORE_DLL_CLOCK_DISABLE ; +writel_relaxed (config, host->ioaddr + + msm_bayhub_offset->core_dll_config_2); + } + + config =readl_relaxed (host->ioaddr + + msm_bayhub_offset->core_dll_config); + config |=CORE_DLL_RST ; +writel_relaxed (config, host->ioaddr + + msm_bayhub_offset->core_dll_config); + + config =readl_relaxed (host->ioaddr + + msm_bayhub_offset->core_dll_config); + config |=CORE_DLL_PDN ; +writel_relaxed (config, host->ioaddr + + msm_bayhub_offset->core_dll_config); + +if (!msm_bayhub_host->dll_config) +msm_bayhub_cm_dll_set_freq (host); + +if (msm_bayhub_host->use_14lpp_dll_reset && + !IS_ERR_OR_NULL (msm_bayhub_host->xo_clk)) { +u32 mclk_freq =0 ; + + config =readl_relaxed (host->ioaddr + + msm_bayhub_offset->core_dll_config_2); + config &=CORE_FLL_CYCLE_CNT ; +if (config) + mclk_freq =DIV_ROUND_CLOSEST_ULL ((host->clock *8 ), + xo_clk); +else + mclk_freq =DIV_ROUND_CLOSEST_ULL ((host->clock *4 ), + xo_clk); + + config =readl_relaxed (host->ioaddr + + msm_bayhub_offset->core_dll_config_2); + config &= ~(0xFF <<10 ); + config |= mclk_freq <<10 ; + +writel_relaxed (config, host->ioaddr + + msm_bayhub_offset->core_dll_config_2); +/* wait for 5us before enabling DLL clock */ +udelay (5 ); + } + + config =readl_relaxed (host->ioaddr + + msm_bayhub_offset->core_dll_config); + config &= ~CORE_DLL_RST ; +writel_relaxed (config, host->ioaddr + + msm_bayhub_offset->core_dll_config); + + config =readl_relaxed (host->ioaddr + + msm_bayhub_offset->core_dll_config); + config &= ~CORE_DLL_PDN ; +writel_relaxed (config, host->ioaddr + + msm_bayhub_offset->core_dll_config); + +if (msm_bayhub_host->use_14lpp_dll_reset) { +if (!msm_bayhub_host->dll_config) +msm_bayhub_cm_dll_set_freq (host); + config =readl_relaxed (host->ioaddr + + msm_bayhub_offset->core_dll_config_2); + config &= ~CORE_DLL_CLOCK_DISABLE ; +writel_relaxed (config, host->ioaddr + + msm_bayhub_offset->core_dll_config_2); + } + +/* +* Configure DLL user control register to enable DLL status. +* This setting is applicable to SDCC v5.1 onwards only. +*/ +if (msm_bayhub_host->uses_tassadar_dll) { + config =DLL_USR_CTL_POR_VAL |FINE_TUNE_MODE_EN | +ENABLE_DLL_LOCK_STATUS |BIAS_OK_SIGNAL ; +writel_relaxed (config, host->ioaddr + + msm_bayhub_offset->core_dll_usr_ctl); + + config =readl_relaxed (host->ioaddr + + msm_bayhub_offset->core_dll_config_3); + config &= ~0xFF ; +if (msm_bayhub_host->clk_rate <150000000 ) + config |=DLL_CONFIG_3_LOW_FREQ_VAL ; +else + config |=DLL_CONFIG_3_HIGH_FREQ_VAL ; +writel_relaxed (config, host->ioaddr + + msm_bayhub_offset->core_dll_config_3); + } + + config =readl_relaxed (host->ioaddr + + msm_bayhub_offset->core_dll_config); + config |=CORE_DLL_EN ; +writel_relaxed (config, host->ioaddr + + msm_bayhub_offset->core_dll_config); + + config =readl_relaxed (host->ioaddr + + msm_bayhub_offset->core_dll_config); + config |=CORE_CK_OUT_EN ; +writel_relaxed (config, host->ioaddr + + msm_bayhub_offset->core_dll_config); + +/* Wait until DLL_LOCK bit of DLL_STATUS register becomes '1' */ +while (!(readl_relaxed (host->ioaddr + msm_bayhub_offset->core_dll_status) & +CORE_DLL_LOCK )) { +/* max. wait for 50us sec for LOCK bit to be set */ +if (--wait_cnt ==0 ) { +dev_err (mmc_dev (mmc),"%s: DLL failed to LOCK\n" , +mmc_hostname (mmc)); +spin_unlock_irqrestore (&host->lock, flags); +return -ETIMEDOUT ; + } +udelay (1 ); + } + +spin_unlock_irqrestore (&host->lock, flags); +return 0 ; +} + +static void msm_bayhub_hc_select_default (struct sdhci_host *host) +{ +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_bayhub_host *msm_bayhub_host =sdhci_pltfm_priv (pltfm_host); +u32 config; +const struct sdhci_msm_bayhub_offset *msm_bayhub_offset = + msm_bayhub_host->offset; + +if (!msm_bayhub_host->use_cdclp533) { + config =readl_relaxed (host->ioaddr + + msm_bayhub_offset->core_vendor_spec3); + config &= ~CORE_PWRSAVE_DLL ; +writel_relaxed (config, host->ioaddr + + msm_bayhub_offset->core_vendor_spec3); + } + + config =readl_relaxed (host->ioaddr + msm_bayhub_offset->core_vendor_spec); + config &= ~CORE_HC_MCLK_SEL_MASK ; + config |=CORE_HC_MCLK_SEL_DFLT ; +writel_relaxed (config, host->ioaddr + msm_bayhub_offset->core_vendor_spec); + +/* +* Disable HC_SELECT_IN to be able to use the UHS mode select +* configuration from Host Control2 register for all other +* modes. +* Write 0 to HC_SELECT_IN and HC_SELECT_IN_EN field +* in VENDOR_SPEC_FUNC +*/ + config =readl_relaxed (host->ioaddr + msm_bayhub_offset->core_vendor_spec); + config &= ~CORE_HC_SELECT_IN_EN ; + config &= ~CORE_HC_SELECT_IN_MASK ; +writel_relaxed (config, host->ioaddr + msm_bayhub_offset->core_vendor_spec); + +/* +* Make sure above writes impacting free running MCLK are completed +* before changing the clk_rate at GCC. +*/ +wmb (); +} + +static void msm_bayhub_hc_select_hs400 (struct sdhci_host *host) +{ +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_bayhub_host *msm_bayhub_host =sdhci_pltfm_priv (pltfm_host); +struct mmc_ios ios = host->mmc->ios; +u32 config, dll_lock; +int rc; +const struct sdhci_msm_bayhub_offset *msm_bayhub_offset = + msm_bayhub_host->offset; + +/* Select the divided clock (free running MCLK/2) */ + config =readl_relaxed (host->ioaddr + msm_bayhub_offset->core_vendor_spec); + config &= ~CORE_HC_MCLK_SEL_MASK ; + config |=CORE_HC_MCLK_SEL_HS400 ; + +writel_relaxed (config, host->ioaddr + msm_bayhub_offset->core_vendor_spec); +/* +* Select HS400 mode using the HC_SELECT_IN from VENDOR SPEC +* register +*/ +if ((msm_bayhub_host->tuning_done || ios.enhanced_strobe) && + !msm_bayhub_host->calibration_done) { + config =readl_relaxed (host->ioaddr + + msm_bayhub_offset->core_vendor_spec); + config |=CORE_HC_SELECT_IN_HS400 ; + config |=CORE_HC_SELECT_IN_EN ; +writel_relaxed (config, host->ioaddr + + msm_bayhub_offset->core_vendor_spec); + } +if (!msm_bayhub_host->clk_rate && !msm_bayhub_host->use_cdclp533) { +/* +* Poll on DLL_LOCK or DDR_DLL_LOCK bits in +* core_dll_status to be set. This should get set +* within 15 us at 200 MHz. +*/ + rc =readl_relaxed_poll_timeout (host->ioaddr + + msm_bayhub_offset->core_dll_status, + dll_lock, + (dll_lock & + (CORE_DLL_LOCK | +CORE_DDR_DLL_LOCK )),10 , +1000 ); +if (rc == -ETIMEDOUT ) +pr_err ("%s: Unable to get DLL_LOCK/DDR_DLL_LOCK, dll_status: 0x%08x\n" , +mmc_hostname (host->mmc), dll_lock); + } +/* +* Make sure above writes impacting free running MCLK are completed +* before changing the clk_rate at GCC. +*/ +wmb (); +} + +/* +* sdhci_msm_bayhub_hc_select_mode :- In general all timing modes are +* controlled via UHS mode select in Host Control2 register. +* eMMC specific HS200/HS400 doesn't have their respective modes +* defined here, hence we use these values. +* +* HS200 - SDR104 (Since they both are equivalent in functionality) +* HS400 - This involves multiple configurations +* Initially SDR104 - when tuning is required as HS200 +* Then when switching to DDR @ 400MHz (HS400) we use +* the vendor specific HC_SELECT_IN to control the mode. +* +* In addition to controlling the modes we also need to select the +* correct input clock for DLL depending on the mode. +* +* HS400 - divided clock (free running MCLK/2) +* All other modes - default (free running MCLK) +*/ +static void sdhci_msm_bayhub_hc_select_mode (struct sdhci_host *host) +{ +struct mmc_ios ios = host->mmc->ios; + +if (ios.timing ==MMC_TIMING_MMC_HS400 || + host->flags &SDHCI_HS400_TUNING ) +msm_bayhub_hc_select_hs400 (host); +else +msm_bayhub_hc_select_default (host); +} + +static int sdhci_msm_bayhub_cdclp533_calibration (struct sdhci_host *host) +{ +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_bayhub_host *msm_bayhub_host =sdhci_pltfm_priv (pltfm_host); +u32 config, calib_done; +int ret; +const struct sdhci_msm_bayhub_offset *msm_bayhub_offset = + msm_bayhub_host->offset; + +pr_debug ("%s: %s: Enter\n" ,mmc_hostname (host->mmc), __func__); + +/* +* Retuning in HS400 (DDR mode) will fail, just reset the +* tuning block and restore the saved tuning phase. +*/ + ret =msm_bayhub_init_cm_dll (host); +if (ret) + goto out; + +/* Set the selected phase in delay line hw block */ + ret =msm_bayhub_config_cm_dll_phase (host, msm_bayhub_host->saved_tuning_phase); +if (ret) + goto out; + + config =readl_relaxed (host->ioaddr + msm_bayhub_offset->core_dll_config); + config |=CORE_CMD_DAT_TRACK_SEL ; +writel_relaxed (config, host->ioaddr + msm_bayhub_offset->core_dll_config); + + config =readl_relaxed (host->ioaddr + msm_bayhub_offset->core_ddr_200_cfg); + config &= ~CORE_CDC_T4_DLY_SEL ; +writel_relaxed (config, host->ioaddr + msm_bayhub_offset->core_ddr_200_cfg); + + config =readl_relaxed (host->ioaddr +CORE_CSR_CDC_GEN_CFG ); + config &= ~CORE_CDC_SWITCH_BYPASS_OFF ; +writel_relaxed (config, host->ioaddr +CORE_CSR_CDC_GEN_CFG ); + + config =readl_relaxed (host->ioaddr +CORE_CSR_CDC_GEN_CFG ); + config |=CORE_CDC_SWITCH_RC_EN ; +writel_relaxed (config, host->ioaddr +CORE_CSR_CDC_GEN_CFG ); + + config =readl_relaxed (host->ioaddr + msm_bayhub_offset->core_ddr_200_cfg); + config &= ~CORE_START_CDC_TRAFFIC ; +writel_relaxed (config, host->ioaddr + msm_bayhub_offset->core_ddr_200_cfg); + +/* Perform CDC Register Initialization Sequence */ + +writel_relaxed (0x11800EC , host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); +writel_relaxed (0x3011111 , host->ioaddr +CORE_CSR_CDC_CTLR_CFG1 ); +writel_relaxed (0x1201000 , host->ioaddr +CORE_CSR_CDC_CAL_TIMER_CFG0 ); +writel_relaxed (0x4 , host->ioaddr +CORE_CSR_CDC_CAL_TIMER_CFG1 ); +writel_relaxed (0xCB732020 , host->ioaddr +CORE_CSR_CDC_REFCOUNT_CFG ); +writel_relaxed (0xB19 , host->ioaddr +CORE_CSR_CDC_COARSE_CAL_CFG ); +writel_relaxed (0x4E2 , host->ioaddr +CORE_CSR_CDC_DELAY_CFG ); +writel_relaxed (0x0 , host->ioaddr +CORE_CDC_OFFSET_CFG ); +writel_relaxed (0x16334 , host->ioaddr +CORE_CDC_SLAVE_DDA_CFG ); + +/* CDC HW Calibration */ + + config =readl_relaxed (host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); + config |=CORE_SW_TRIG_FULL_CALIB ; +writel_relaxed (config, host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); + + config =readl_relaxed (host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); + config &= ~CORE_SW_TRIG_FULL_CALIB ; +writel_relaxed (config, host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); + + config =readl_relaxed (host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); + config |=CORE_HW_AUTOCAL_ENA ; +writel_relaxed (config, host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); + + config =readl_relaxed (host->ioaddr +CORE_CSR_CDC_CAL_TIMER_CFG0 ); + config |=CORE_TIMER_ENA ; +writel_relaxed (config, host->ioaddr +CORE_CSR_CDC_CAL_TIMER_CFG0 ); + + ret =readl_relaxed_poll_timeout (host->ioaddr +CORE_CSR_CDC_STATUS0 , + calib_done, + (calib_done &CORE_CALIBRATION_DONE ), +1 ,50 ); + +if (ret == -ETIMEDOUT ) { +pr_err ("%s: %s: CDC calibration was not completed\n" , +mmc_hostname (host->mmc), __func__); + goto out; + } + + ret =readl_relaxed (host->ioaddr +CORE_CSR_CDC_STATUS0 ) + &CORE_CDC_ERROR_CODE_MASK ; +if (ret) { +pr_err ("%s: %s: CDC error code %d\n" , +mmc_hostname (host->mmc), __func__, ret); + ret = -EINVAL ; + goto out; + } + + config =readl_relaxed (host->ioaddr + msm_bayhub_offset->core_ddr_200_cfg); + config |=CORE_START_CDC_TRAFFIC ; +writel_relaxed (config, host->ioaddr + msm_bayhub_offset->core_ddr_200_cfg); +out: +pr_debug ("%s: %s: Exit, ret %d\n" ,mmc_hostname (host->mmc), + __func__, ret); +return ret; +} + +static int sdhci_msm_bayhub_cm_dll_sdc4_calibration (struct sdhci_host *host) +{ +struct mmc_host *mmc = host->mmc; +u32 dll_status, config, ddr_cfg_offset; +int ret; +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_bayhub_host *msm_bayhub_host =sdhci_pltfm_priv (pltfm_host); +const struct sdhci_msm_bayhub_offset *msm_bayhub_offset = +sdhci_priv_msm_bayhub_offset (host); + +pr_debug ("%s: %s: Enter\n" ,mmc_hostname (host->mmc), __func__); + +/* +* Currently the core_ddr_config register defaults to desired +* configuration on reset. Currently reprogramming the power on +* reset (POR) value in case it might have been modified by +* bootloaders. In the future, if this changes, then the desired +* values will need to be programmed appropriately. +*/ +if (msm_bayhub_host->updated_ddr_cfg) + ddr_cfg_offset = msm_bayhub_offset->core_ddr_config; +else + ddr_cfg_offset = msm_bayhub_offset->core_ddr_config_old; +writel_relaxed (msm_bayhub_host->ddr_config, host->ioaddr + ddr_cfg_offset); + +if (mmc->ios.enhanced_strobe) { + config =readl_relaxed (host->ioaddr + + msm_bayhub_offset->core_ddr_200_cfg); + config |=CORE_CMDIN_RCLK_EN ; +writel_relaxed (config, host->ioaddr + + msm_bayhub_offset->core_ddr_200_cfg); + } + + config =readl_relaxed (host->ioaddr + msm_bayhub_offset->core_dll_config_2); + config |=CORE_DDR_CAL_EN ; +writel_relaxed (config, host->ioaddr + msm_bayhub_offset->core_dll_config_2); + + ret =readl_relaxed_poll_timeout (host->ioaddr + + msm_bayhub_offset->core_dll_status, + dll_status, + (dll_status &CORE_DDR_DLL_LOCK ), +10 ,1000 ); + +if (ret == -ETIMEDOUT ) { +pr_err ("%s: %s: CM_DLL_SDC4 calibration was not completed\n" , +mmc_hostname (host->mmc), __func__); + goto out; + } + +/* +* Set CORE_PWRSAVE_DLL bit in CORE_VENDOR_SPEC3. +* When MCLK is gated OFF, it is not gated for less than 0.5us +* and MCLK must be switched on for at-least 1us before DATA +* starts coming. Controllers with 14lpp and later tech DLL cannot +* guarantee above requirement. So PWRSAVE_DLL should not be +* turned on for host controllers using this DLL. +*/ +if (!msm_bayhub_host->use_14lpp_dll_reset) { + config =readl_relaxed (host->ioaddr + + msm_bayhub_offset->core_vendor_spec3); + config |=CORE_PWRSAVE_DLL ; +writel_relaxed (config, host->ioaddr + + msm_bayhub_offset->core_vendor_spec3); + } + +/* +* Drain writebuffer to ensure above DLL calibration +* and PWRSAVE DLL is enabled. +*/ +wmb (); +out: +pr_debug ("%s: %s: Exit, ret %d\n" ,mmc_hostname (host->mmc), + __func__, ret); +return ret; +} + +static int sdhci_msm_bayhub_hs400_dll_calibration (struct sdhci_host *host) +{ +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_bayhub_host *msm_bayhub_host =sdhci_pltfm_priv (pltfm_host); +struct mmc_host *mmc = host->mmc; +int ret; +u32 config; +const struct sdhci_msm_bayhub_offset *msm_bayhub_offset = + msm_bayhub_host->offset; + +pr_debug ("%s: %s: Enter\n" ,mmc_hostname (host->mmc), __func__); + +/* +* Retuning in HS400 (DDR mode) will fail, just reset the +* tuning block and restore the saved tuning phase. +*/ + ret =msm_bayhub_init_cm_dll (host); +if (ret) + goto out; + +if (!mmc->ios.enhanced_strobe) { +/* Set the selected phase in delay line hw block */ + ret =msm_bayhub_config_cm_dll_phase (host, + msm_bayhub_host->saved_tuning_phase); +if (ret) + goto out; + config =readl_relaxed (host->ioaddr + + msm_bayhub_offset->core_dll_config); + config |=CORE_CMD_DAT_TRACK_SEL ; +writel_relaxed (config, host->ioaddr + + msm_bayhub_offset->core_dll_config); + } + +if (msm_bayhub_host->use_cdclp533) + ret =sdhci_msm_bayhub_cdclp533_calibration (host); +else + ret =sdhci_msm_bayhub_cm_dll_sdc4_calibration (host); +out: +pr_debug ("%s: %s: Exit, ret %d\n" ,mmc_hostname (host->mmc), + __func__, ret); +return ret; +} + +static bool sdhci_msm_bayhub_is_tuning_needed (struct sdhci_host *host) +{ +struct mmc_ios *ios = &host->mmc->ios; + +/* +* Tuning is required for SDR104, HS200 and HS400 cards and +* if clock frequency is greater than 100MHz in these modes. +*/ +if (host->clock <=CORE_FREQ_100MHZ || + !(ios->timing ==MMC_TIMING_MMC_HS400 || + ios->timing ==MMC_TIMING_MMC_HS200 || + ios->timing ==MMC_TIMING_UHS_SDR104 ) || + ios->enhanced_strobe) +return false; + +return true; +} + +static int sdhci_msm_bayhub_restore_sdr_dll_config (struct sdhci_host *host) +{ +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_bayhub_host *msm_bayhub_host =sdhci_pltfm_priv (pltfm_host); +int ret; + +/* +* SDR DLL comes into picture only for timing modes which needs +* tuning. +*/ +if (!sdhci_msm_bayhub_is_tuning_needed (host)) +return 0 ; + +/* Reset the tuning block */ + ret =msm_bayhub_init_cm_dll (host); +if (ret) +return ret; + +/* Restore the tuning block */ + ret =msm_bayhub_config_cm_dll_phase (host, msm_bayhub_host->saved_tuning_phase); + +return ret; +} + +static void sdhci_msm_bayhub_set_cdr (struct sdhci_host *host,bool enable) +{ +const struct sdhci_msm_bayhub_offset *msm_bayhub_offset = +sdhci_priv_msm_bayhub_offset (host); +u32 config, oldconfig =readl_relaxed (host->ioaddr + + msm_bayhub_offset->core_dll_config); + + config = oldconfig; +if (enable) { + config |=CORE_CDR_EN ; + config &= ~CORE_CDR_EXT_EN ; + }else { + config &= ~CORE_CDR_EN ; + config |=CORE_CDR_EXT_EN ; + } + +if (config != oldconfig) { +writel_relaxed (config, host->ioaddr + + msm_bayhub_offset->core_dll_config); + } +} + +static int sdhci_msm_bayhub_execute_tuning (struct mmc_host *mmc,u32 opcode) +{ +struct sdhci_host *host =mmc_priv (mmc); +int tuning_seq_cnt =10 ; +u8 phase, tuned_phases[16 ], tuned_phase_cnt =0 ; +int rc; +struct mmc_ios ios = host->mmc->ios; +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_bayhub_host *msm_bayhub_host =sdhci_pltfm_priv (pltfm_host); + +if (!sdhci_msm_bayhub_is_tuning_needed (host)) { + msm_bayhub_host->use_cdr = false; +sdhci_msm_bayhub_set_cdr (host, false); +return 0 ; + } + +/* Clock-Data-Recovery used to dynamically adjust RX sampling point */ + msm_bayhub_host->use_cdr = true; + +/* +* Clear tuning_done flag before tuning to ensure proper +* HS400 settings. +*/ + msm_bayhub_host->tuning_done =0 ; + +/* +* For HS400 tuning in HS200 timing requires: +* - select MCLK/2 in VENDOR_SPEC +* - program MCLK to 400MHz (or nearest supported) in GCC +*/ +if (host->flags &SDHCI_HS400_TUNING ) { +sdhci_msm_bayhub_hc_select_mode (host); +msm_bayhub_set_clock_rate_for_bus_mode (host, ios.clock); + host->flags &= ~SDHCI_HS400_TUNING ; + } + +retry: +/* First of all reset the tuning block */ + rc =msm_bayhub_init_cm_dll (host); +if (rc) +return rc; + + phase =0 ; +do { +/* Set the phase in delay line hw block */ + rc =msm_bayhub_config_cm_dll_phase (host, phase); +if (rc) +return rc; + + rc =mmc_send_tuning (mmc, opcode,NULL ); +if (!rc) { +/* Tuning is successful at this tuning point */ + tuned_phases[tuned_phase_cnt++] = phase; +dev_dbg (mmc_dev (mmc),"%s: Found good phase = %d\n" , +mmc_hostname (mmc), phase); + } + }while (++phase <ARRAY_SIZE (tuned_phases)); + +if (tuned_phase_cnt) { +if (tuned_phase_cnt ==ARRAY_SIZE (tuned_phases)) { +/* +* All phases valid is _almost_ as bad as no phases +* valid. Probably all phases are not really reliable +* but we didn't detect where the unreliable place is. +* That means we'll essentially be guessing and hoping +* we get a good phase. Better to try a few times. +*/ +dev_dbg (mmc_dev (mmc),"%s: All phases valid; try again\n" , +mmc_hostname (mmc)); +if (--tuning_seq_cnt) { + tuned_phase_cnt =0 ; + goto retry; + } + } + + rc =msm_bayhub_find_most_appropriate_phase (host, tuned_phases, + tuned_phase_cnt); +if (rc >=0 ) + phase = rc; +else +return rc; + +/* +* Finally set the selected phase in delay +* line hw block. +*/ + rc =msm_bayhub_config_cm_dll_phase (host, phase); +if (rc) +return rc; + msm_bayhub_host->saved_tuning_phase = phase; +dev_dbg (mmc_dev (mmc),"%s: Setting the tuning phase to %d\n" , +mmc_hostname (mmc), phase); + }else { +if (--tuning_seq_cnt) + goto retry; +/* Tuning failed */ +dev_dbg (mmc_dev (mmc),"%s: No tuning point found\n" , +mmc_hostname (mmc)); + rc = -EIO ; + } + +if (!rc) + msm_bayhub_host->tuning_done = true; +return rc; +} +/* The code porting from sdhci-msm.c part-2 end */ + +/* +* Bayhub patch part-2 start +* The APIs used to initialization GGC chip and structure +*/ +static void cfg_bit_2_byte (int max_bit,int tar,int *byt,int *bit) +{ +u8 cfg_bit_map[6 ] = {6 ,5 ,4 ,2 ,1 ,0 }; + + *byt = (max_bit - tar) /6 ; + *bit = cfg_bit_map[(max_bit - tar) %6 ]; +} + +static u32 cfg_read_bits_ofs_mask (u8 *cfg,struct ggc_reg_op *bits) +{ +u32 rv =0 ; +u32 mask = bits->mask; +int byte =0 , bit =0 ; +int i =0 ; + +while (mask) { +cfg_bit_2_byte (MAX_CFG_BIT_VAL , bits->ofs + i, &byte, &bit); +if (cfg[byte] & (1 << bit)) + rv |=1 << i; + + i++; + mask >>=1 ; + }; +return rv; +} + +static void get_default_setting (struct sdhci_host *host,u8 *data) +{ +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_bayhub_host *vendor_host =sdhci_pltfm_priv (pltfm_host); + + vendor_host->ggc.def_sela_100m = +cfg_read_bits_ofs_mask (data, &vendor_host->ggc.dll_sela_100m_cfg); + vendor_host->ggc.def_sela_200m = +cfg_read_bits_ofs_mask (data, &vendor_host->ggc.dll_sela_200m_cfg); + vendor_host->ggc.def_selb_100m = +cfg_read_bits_ofs_mask (data, &vendor_host->ggc.dll_sela_100m_cfg); + vendor_host->ggc.def_selb_200m = +cfg_read_bits_ofs_mask (data, &vendor_host->ggc.dll_sela_200m_cfg); +} + +static void cfg_write_bits_ofs_mask (u8 *cfg, +struct ggc_reg_op *bits,u32 write_value) +{ +u32 wv = write_value & bits->mask; +u32 mask = bits->mask; +int byte =0 , bit =0 ; +int i =0 ; + +while (mask) { +cfg_bit_2_byte (MAX_CFG_BIT_VAL , bits->ofs + i, &byte, &bit); +if (wv &1 ) + cfg[byte] |= (u8) (1 << bit); +else + cfg[byte] &= (u8) (~(1 << bit)); + + i++; + wv >>=1 ; + mask >>=1 ; + }; +} + +/* Use to get byte offset and bit offset */ +static void ram_bit_2_bt (int tar,int *byt,int *bit) +{ + *byt = tar /8 ; + *bit = tar %8 ; +} + +static void set_gg_reg_cur_val (struct ggc_adaptor *ggc, +u8 *data,u8 len) +{ +memcpy (&ggc->gg_reg_cur[0 ], data, len); +} + +static void get_gg_reg_cur_val (struct ggc_adaptor *ggc, +u8 *data,u8 len) +{ +memcpy (data, &ggc->gg_reg_cur[0 ], len); +} + +static u32 read_ram_bits_ofs_mask (u8 *cfg,struct ggc_reg_op *bits) +{ +u32 rv =0 ; +u32 mask = bits->mask; +int byte =0 , bit =0 ; +int i =0 ; + +while (mask) { +/* No replace interface at bitmap +* The read out address maybe not align at byte +*/ +ram_bit_2_bt (bits->ofs + i, &byte, &bit); +if (cfg[byte] & (1 << bit)) + rv |=1 << i; + + i++; + mask >>=1 ; + }; +return rv; +} + +static void ggc_dll_voltage_init (struct sdhci_host *host) +{ +int i =0 ; +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_bayhub_host *vendor_host = +sdhci_pltfm_priv (pltfm_host); + +for (i =0 ; i <4 ; i++) { + vendor_host->ggc.dll_voltage_scan_map[i] =0 ; + vendor_host->ggc.sdr50.dll_voltage_unlock_cnt[i] =0 ; + vendor_host->ggc.sdr104.dll_voltage_unlock_cnt[i] =0 ; + } +} + +static void ggc_chip_init (struct sdhci_host *host) +{ +u8 data[512 ]; +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_bayhub_host *vendor_host = +sdhci_pltfm_priv (pltfm_host); + +memcpy (data, ggc_cfg_data,sizeof (ggc_cfg_data)); +get_default_setting (host, data); +set_gg_reg_cur_val (&vendor_host->ggc, data,64 ); +} + +static void driver_send_command7 (struct sdhci_host *host) +{ +struct mmc_host *mmc = host->mmc; +struct mmc_command cmd = {0 }; + + cmd.opcode =MMC_SELECT_CARD ; + cmd.arg =0 ; + cmd.flags =MMC_RSP_NONE |MMC_CMD_AC ; +mmc_wait_for_cmd (mmc, &cmd,3 ); +} + +static void driver_send_command24 (struct sdhci_host *host, +u32 *cfg_data,int data_len) +{ +struct mmc_host *mmc = host->mmc; +struct mmc_request mrq = {0 }; +struct mmc_command cmd = {0 }; +struct mmc_data data = {0 }; +struct scatterlist sg; +u8 *data1 =kzalloc (PAGE_SIZE ,GFP_KERNEL ); + +if (!data1) +return ; + +memcpy (data1, (u8 *)&(cfg_data[0 ]), data_len); +sg_init_one (&sg, data1,512 ); + + cmd.opcode =MMC_WRITE_BLOCK ; + cmd.arg =0 ; + cmd.flags =MMC_RSP_R1 |MMC_CMD_ADTC ; + data.blksz =512 ; + data.blocks =1 ; + data.flags =MMC_DATA_WRITE ; + data.timeout_ns =1000 *1000 *1000 ; + data.sg = &sg; + data.sg_len =1 ; + mrq.cmd = &cmd; + mrq.data = &data; + mrq.stop =NULL ; + +mmc_wait_for_req (mmc, &mrq); +kfree (data1); +} + +static void bht_update_cfg (struct mmc_host *mmc_host, +struct mmc_card *card,u32 *cfg_data,int data_len) +{ +struct sdhci_host *host; + + host =mmc_priv (mmc_host); +mmc_set_bus_width (mmc_host,MMC_BUS_WIDTH_4 ); +if (host->ops->reset) + host->ops->reset (host,SDHCI_RESET_CMD |SDHCI_RESET_DATA ); + +driver_send_command7 (host); +driver_send_command24 (host, cfg_data, data_len); +driver_send_command7 (host); + +mmc_set_bus_width (mmc_host,MMC_BUS_WIDTH_1 ); +} + + +static void tx_selb_failed_tb_reset (struct sdhci_host *host) +{ +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_bayhub_host *vendor_host = +sdhci_pltfm_priv (pltfm_host); + +memset (&vendor_host->ggc.sdr104.tx_selb_tb,0xff , +sizeof (vendor_host->ggc.sdr104.tx_selb_tb)); +memset (&vendor_host->ggc.sdr50.tx_selb_tb,0xff , +sizeof (vendor_host->ggc.sdr50.tx_selb_tb)); +} + +static void all_selb_failed_tb_reset (struct sdhci_host *host) +{ +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_bayhub_host *vendor_host = +sdhci_pltfm_priv (pltfm_host); + +memset (vendor_host->ggc.sdr104.all_selb_tb,0xff , +sizeof (vendor_host->ggc.sdr104.all_selb_tb)); +memset (vendor_host->ggc.sdr50.all_selb_tb,0xff , +sizeof (vendor_host->ggc.sdr50.all_selb_tb)); +} + +static void tx_selb_failed_history_reset (struct sdhci_host *host) +{ +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_bayhub_host *vendor_host = +sdhci_pltfm_priv (pltfm_host); + + vendor_host->ggc.sdr50.tx_selb_failed_history =BIT_PASS_MASK ; + vendor_host->ggc.sdr104.tx_selb_failed_history =BIT_PASS_MASK ; +} + +static void ggc_reset_selx_failed_tb (struct sdhci_host *host) +{ +tx_selb_failed_tb_reset (host); +all_selb_failed_tb_reset (host); +tx_selb_failed_history_reset (host); +} + +static void _ggc_reset_sela_tuning_result ( +struct sdhci_msm_bayhub_host *host) +{ +int i =0 ; + +for (i =0 ; i <TUNING_PHASE_SIZE ; i++) + host->ggc.ggc_sela_tuning_result[i] =NO_TUNING ; +} + +static void _ggc_reset_tuning_result_for_dll (struct sdhci_host *host) +{ +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_bayhub_host *vendor_host = +sdhci_pltfm_priv (pltfm_host); + +ggc_reset_selx_failed_tb (host); + vendor_host->ggc.ggc_cmd_tx_selb_failed_range =BIT_PASS_MASK ; + vendor_host->ggc.selx_tuning_done_flag =0 ; +_ggc_reset_sela_tuning_result (vendor_host); +} + +static void ggc_tuning_result_reset (struct sdhci_host *host) +{ +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_bayhub_host *vendor_host = +sdhci_pltfm_priv (pltfm_host); + +_ggc_reset_tuning_result_for_dll (host); + + vendor_host->ggc.sdr50.bus_mode =SD_FNC_AM_SDR50 ; + vendor_host->ggc.sdr104.bus_mode =SD_FNC_AM_SDR104 ; + vendor_host->ggc.driver_strength_reinit_flg =0 ; + vendor_host->ggc.cur_bus_mode =NULL ; + vendor_host->ggc.dll_unlock_reinit_flg =0 ; + vendor_host->ggc.tuning_cmd7_timeout_reinit_flg =0 ; + vendor_host->ggc.tuning_cmd7_timeout_reinit_cnt =0 ; + vendor_host->ggc.sdr50_notuning_sela_inject_flag =1 ; + vendor_host->ggc.sdr50_notuning_crc_error_flag =0 ; + vendor_host->ggc.sdr50_notuning_sela_rx_inject = + vendor_host->ggc.bh201_sdr50_sela_sw_inject; +} + +static void bht_load_hw_inject (struct mmc_host *mmc_host, +struct mmc_card *card,u32 *cfg_data,int data_len, +u32 sel200,u32 sel100) +{ +struct sdhci_host *host =mmc_priv (mmc_host); +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_bayhub_host *vendor_host = +sdhci_pltfm_priv (pltfm_host); +u32 gg_hw_inj[16 ]; + +memcpy (gg_hw_inj, ggc_cfg_data,sizeof (ggc_cfg_data)); + gg_hw_inj[1 ] =0x7364032 ; + gg_hw_inj[11 ] = vendor_host->ggc.bh201_sdr104_selb_hw_inject; + gg_hw_inj[12 ] = vendor_host->ggc.bh201_sdr50_selb_hw_inject; + gg_hw_inj[15 ] = vendor_host->ggc.bh201_drive_strength; + +bht_update_cfg (mmc_host, card, gg_hw_inj, data_len); +} + +static void bht_load (struct mmc_host *mmc_host,struct mmc_card *card) +{ +struct sdhci_host *host =mmc_priv (mmc_host); +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_bayhub_host *vendor_host = +sdhci_pltfm_priv (pltfm_host); +u8 driver_strength_reinit_flg = + vendor_host->ggc.driver_strength_reinit_flg; +int cur_dll_voltage_idx = vendor_host->ggc.cur_dll_voltage_idx; +u32 i =0 ; +u32 reg; +u32 gg_sw_def[16 ]; +u8 data[512 ]; +static const int s_dll_voltage_cfg[4 ][2 ] = { + {0x30503106 ,0x64141711 }, + {0x31503106 ,0x64141711 }, + {0x30503106 ,0x64141751 }, + {0x31503106 ,0x64141751 }, + }; + +memcpy (gg_sw_def, ggc_cfg_data,sizeof (ggc_cfg_data)); + +pr_debug ("%s: Load BHT patch\n" ,mmc_hostname (mmc_host)); + +mmc_send_relative_addr (mmc_host, &card->rca); + mmc_host->card = card; +if (vendor_host->ggc.dll_unlock_reinit_flg) { +pr_debug ("dll unlock reinit: idx=%d\n" , + cur_dll_voltage_idx); +ggc_tuning_result_reset (host); + gg_sw_def[8 ] = s_dll_voltage_cfg[cur_dll_voltage_idx][0 ]; + gg_sw_def[9 ] = s_dll_voltage_cfg[cur_dll_voltage_idx][1 ]; + } +if (vendor_host->ggc.driver_strength_reinit_flg) { +pr_debug ("%s: driver strength should be init to %d\n" , +mmc_hostname (mmc_host), driver_strength_reinit_flg); +ggc_tuning_result_reset (host); +if (vendor_host->ggc.driver_strength_reinit_flg <=7 ) { + gg_sw_def[15 ] &=0x0f0fffff ; + gg_sw_def[15 ] |= (driver_strength_reinit_flg <<28 ) + | (driver_strength_reinit_flg <<20 ); + } + } +driver_send_command7 (host); +if (vendor_host->ggc.tuning_cmd7_timeout_reinit_flg ==0 + && vendor_host->ggc.selx_tuning_done_flag ==0 ) { +bht_load_hw_inject (mmc_host, card, gg_sw_def, +sizeof (gg_sw_def),0x3ff ,0x77f ); +bht_update_cfg (mmc_host, card, gg_sw_def,sizeof (gg_sw_def)); +set_gg_reg_cur_val (&vendor_host->ggc, (u8 *)gg_sw_def,sizeof (gg_sw_def)); + }else { +if (vendor_host->ggc.selx_tuning_done_flag) +pr_debug ("%s: skip load default configuration for tuning done\n" , +mmc_hostname (mmc_host)); +if (vendor_host->ggc.tuning_cmd7_timeout_reinit_flg) { +pr_debug ("%s: write previous inject results to bh201 for cmd7 timeout flag is set\n" , +mmc_hostname (mmc_host)); +get_gg_reg_cur_val (&vendor_host->ggc, data,sizeof (gg_sw_def)); + +pr_debug ("%s: dump config data before write to bh201\n" , __func__); +for (i =0 ; i <128 ; i++) { +memcpy (®, data+i*sizeof (u32),sizeof (u32)); +pr_debug ("ggc_reg32[%03d]=0x%08x\n" , i, reg); + } +bht_update_cfg (mmc_host, card, (u32 *)data,sizeof (data)); + } + } +} + +static void bh201_signal_voltage_on_off (struct sdhci_host *host,u32 on_off) +{ +int card_present_status =0 ; +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_bayhub_host *vendor_host =sdhci_pltfm_priv (pltfm_host); + +if (gpio_is_valid (vendor_host->ggc.det_gpio)) { + card_present_status =gpio_get_value (vendor_host->ggc.det_gpio); +pr_debug ("%s: detect_gpio pin %d status is %d\n" , +mmc_hostname (host->mmc), vendor_host->ggc.det_gpio, card_present_status); + }else { +pr_err ("%s: no det_gpio provided\n" ,mmc_hostname (host->mmc)); + } + +if (on_off) { +pr_debug ("%s: apply bht power on patch\n" ,mmc_hostname (host->mmc)); + +ggc_dll_voltage_init (host); + +ggc_chip_init (host); + +if (gpio_is_valid (vendor_host->ggc.pwr_gpio)) { +gpio_direction_output (vendor_host->ggc.pwr_gpio,1 ); +msleep (100 ); +pr_debug ("%s: pwr_gpio pin %d status is %d\n" , +mmc_hostname (host->mmc), vendor_host->ggc.pwr_gpio, +gpio_get_value (vendor_host->ggc.pwr_gpio)); + }else { +pr_err ("%s: no pwr_gpio provided\n" ,mmc_hostname (host->mmc)); + } + }else { +pr_debug ("%s: apply bht power off patch\n" ,mmc_hostname (host->mmc)); + +ggc_dll_voltage_init (host); + +if (card_present_status <=0 ) { +pr_debug ("%s: clear tuning result for power off and card removed\n" , +mmc_hostname (host->mmc)); +ggc_tuning_result_reset (host); + } +ggc_chip_init (host); + +if (gpio_is_valid (vendor_host->ggc.pwr_gpio)) { +gpio_direction_output (vendor_host->ggc.pwr_gpio,0 ); +pr_debug ("%s: pwr_gpio pin %d status is %d\n" , +mmc_hostname (host->mmc), vendor_host->ggc.pwr_gpio, +gpio_get_value (vendor_host->ggc.pwr_gpio)); + }else { +pr_err ("%s: no pwr_gpio provided\n" ,mmc_hostname (host->mmc)); + } + } +} + +static void sdhci_bh201_parse (struct mmc_host *mmc_host) +{ +struct sdhci_host *host =mmc_priv (mmc_host); +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_bayhub_host *vendor_host =sdhci_pltfm_priv (pltfm_host); +struct device_node *np = vendor_host->pdev->dev.of_node; +struct ggc_reg_op index_array[] = { + {14 ,0xffffffff ,0 }, {46 ,0xffffffff ,0 }, + {205 ,0xffffffff ,0 }, {237 ,0xffffffff ,0 }, + {141 ,0xf ,0 }, {145 ,0xf ,0 }, + {83 ,0xfff ,0 }, {95 ,0xfff ,0 }, + {126 ,0xf ,0 }, {130 ,0xf ,0 }, + {140 ,0xf ,0 }, {144 ,0xf ,0 }, + {183 ,0x1 ,0 }, {184 ,0x1 ,0 }, + {171 ,0x1 ,0 }, {172 ,0x1 ,0 }, + {173 ,0x3f ,0 }, {357 ,0x1 ,0 }, + {93 ,0x7ff ,0 }, {81 ,0x7ff ,0 }, + }; + +memcpy (&vendor_host->ggc.pha_stas_rx_low32, &index_array[0 ], +sizeof (struct ggc_reg_op )); +memcpy (&vendor_host->ggc.pha_stas_rx_high32, &index_array[1 ], +sizeof (struct ggc_reg_op )); +memcpy (&vendor_host->ggc.pha_stas_tx_low32, &index_array[2 ], +sizeof (struct ggc_reg_op )); +memcpy (&vendor_host->ggc.pha_stas_tx_high32, &index_array[3 ], +sizeof (struct ggc_reg_op )); +memcpy (&vendor_host->ggc.dll_sela_after_mask, &index_array[4 ], +sizeof (struct ggc_reg_op )); +memcpy (&vendor_host->ggc.dll_selb_after_mask, &index_array[5 ], +sizeof (struct ggc_reg_op )); + +memcpy (&vendor_host->ggc.dll_delay_100m_backup, &index_array[6 ], +sizeof (struct ggc_reg_op )); +memcpy (&vendor_host->ggc.dll_delay_200m_backup, &index_array[7 ], +sizeof (struct ggc_reg_op )); + +memcpy (&vendor_host->ggc.dll_sela_100m_cfg, &index_array[8 ], +sizeof (struct ggc_reg_op )); +memcpy (&vendor_host->ggc.dll_sela_200m_cfg, &index_array[9 ], +sizeof (struct ggc_reg_op )); +memcpy (&vendor_host->ggc.dll_selb_100m_cfg, &index_array[10 ], +sizeof (struct ggc_reg_op )); +memcpy (&vendor_host->ggc.dll_selb_200m_cfg, &index_array[11 ], +sizeof (struct ggc_reg_op )); +memcpy (&vendor_host->ggc.dll_selb_100m_cfg_en, &index_array[12 ], +sizeof (struct ggc_reg_op )); +memcpy (&vendor_host->ggc.dll_selb_200m_cfg_en, &index_array[13 ], +sizeof (struct ggc_reg_op )); +memcpy (&vendor_host->ggc.internl_tuning_en_100m, &index_array[14 ], +sizeof (struct ggc_reg_op )); +memcpy (&vendor_host->ggc.internl_tuning_en_200m, &index_array[15 ], +sizeof (struct ggc_reg_op )); +memcpy (&vendor_host->ggc.cmd19_cnt_cfg, &index_array[16 ], +sizeof (struct ggc_reg_op )); + +memcpy (&vendor_host->ggc.inject_failure_for_tuning_enable_cfg, + &index_array[17 ],sizeof (struct ggc_reg_op )); +memcpy (&vendor_host->ggc.inject_failure_for_200m_tuning_cfg, + &index_array[18 ],sizeof (struct ggc_reg_op )); +memcpy (&vendor_host->ggc.inject_failure_for_100m_tuning_cfg, + &index_array[19 ],sizeof (struct ggc_reg_op )); + + vendor_host->ggc.bh201_drive_strength =0x3A314177 ; + vendor_host->ggc.bh201_sdr50_sela_sw_inject =0x47F ; + vendor_host->ggc.bh201_sdr50_selb_hw_inject =0x00725777 ; + vendor_host->ggc.bh201_sdr104_selb_hw_inject =0x57336200 ; + host->flags |=SDHCI_SDR50_NEEDS_TUNING ; + host->mmc_host_ops.init_card = bht_load; + +if (of_find_property (np,"bh201_drive_strength" ,NULL )) +of_property_read_u32_index (np,"bh201_drive_strength" ,0 , + &vendor_host->ggc.bh201_drive_strength); +if (of_find_property (np,"bh201_sdr50_sela_sw_inject" ,NULL )) +of_property_read_u32_index (np,"bh201_sdr50_sela_sw_inject" ,0 , + &vendor_host->ggc.bh201_sdr50_sela_sw_inject); +if (of_find_property (np,"bh201_sdr50_selb_hw_inject" ,NULL )) +of_property_read_u32_index (np,"bh201_sdr50_selb_hw_inject" ,0 , + &vendor_host->ggc.bh201_sdr50_selb_hw_inject); +if (of_find_property (np,"bh201_sdr104_selb_hw_inject" ,NULL )) +of_property_read_u32_index (np,"bh201_sdr104_selb_hw_inject" ,0 , + &vendor_host->ggc.bh201_sdr104_selb_hw_inject); + + vendor_host->ggc.pwr_gpio =of_get_named_gpio (np,"pwr-gpios" ,0 ); +if (!gpio_is_valid (vendor_host->ggc.pwr_gpio)) +dev_err (&vendor_host->pdev->dev,"no pwr-gpio provided !\n" ); +else +dev_info (&vendor_host->pdev->dev,"pwr-gpio provided\n" ); + + vendor_host->ggc.det_gpio =of_get_named_gpio (np,"det-gpios" ,0 ); +if (!gpio_is_valid (vendor_host->ggc.det_gpio)) +dev_err (&vendor_host->pdev->dev,"no det-gpio provided !\n" ); +else +dev_info (&vendor_host->pdev->dev,"det-gpio provided\n" ); + +if (gpio_is_valid (vendor_host->ggc.pwr_gpio)) { +devm_gpio_request_one (&vendor_host->pdev->dev, vendor_host->ggc.pwr_gpio, +GPIOF_OUT_INIT_LOW ,"sprd-1-pwr" ); +pr_debug ("%s: detect_gpio pin %d\n" , +mmc_hostname (host->mmc), vendor_host->ggc.pwr_gpio); + }else { +pr_err ("%s: no detect_gpio provided\n" , +mmc_hostname (host->mmc)); + } + +if (gpio_is_valid (vendor_host->ggc.det_gpio)) { +devm_gpio_request_one (&vendor_host->pdev->dev, vendor_host->ggc.det_gpio, +GPIOF_DIR_IN ,"sprd-1-det" ); +pr_debug ("%s: detect_gpio pin %d\n" , +mmc_hostname (host->mmc), vendor_host->ggc.det_gpio); + }else { +pr_err ("%s: no detect_gpio provided\n" , +mmc_hostname (host->mmc)); + } +} +/* Bayhub patch part-2 end */ + +/* +* The code porting from mmc/core start +* We need to porting the code from sdhci-msm.c for below reasons: +* 1.ACMD42 should be added between Bus width switch and transfer mode switch +* 2.Bus width switch should at front of transfer mode switch during mode switch stage +* 3.If above two points can be patched to sd.c directly, we can decrease much redundant code +* 4.Above changes match with SD host spec, but don’t match with current sd initialazation flow +*/ +static const unsigned int freqs[] = {400000 ,300000 ,200000 ,100000 }; +#define SD_POWEROFF_NOTIFY_TIMEOUT_MS 2000 +#define SD_WRITE_EXTR_SINGLE_TIMEOUT_MS 1000 + +struct sd_busy_data { +struct mmc_card *card; +u8 *reg_buf; +}; + +static bool mmc_sd_card_using_v18 (struct mmc_card *card) +{ +/* +* According to the SD spec., the Bus Speed Mode (function group 1) bits +* 2 to 4 are zero if the card is initialized at 3.3V signal level. Thus +* they can be used to determine if the card has already switched to +* 1.8V signaling. +*/ +return card->sw_caps.sd3_bus_mode & + (SD_MODE_UHS_SDR50 |SD_MODE_UHS_SDR104 |SD_MODE_UHS_DDR50 ); +} + +/* +* Fetches and decodes switch information +*/ +static int mmc_read_switch (struct mmc_card *card) +{ +int err; +u8 *status; + +if (card->scr.sda_vsn <SCR_SPEC_VER_1 ) +return 0 ; + +if (!(card->csd.cmdclass &CCC_SWITCH )) { +pr_warn ("%s: card lacks mandatory switch function, performance might suffer\n" , +mmc_hostname (card->host)); +return 0 ; + } + + status =kmalloc (64 ,GFP_KERNEL ); +if (!status) +return -ENOMEM ; + +/* +* Find out the card's support bits with a mode 0 operation. +* The argument does not matter, as the support bits do not +* change with the arguments. +*/ + err =mmc_sd_switch (card,0 ,0 ,0 , status); +if (err) { +/* +* If the host or the card can't do the switch, +* fail more gracefully. +*/ +if (err != -EINVAL && err != -EFAULT ) + goto out; + +pr_warn ("%s: problem reading Bus Speed modes\n" , +mmc_hostname (card->host)); + err =0 ; + + goto out; + } + +if (status[13 ] &SD_MODE_HIGH_SPEED ) + card->sw_caps.hs_max_dtr =HIGH_SPEED_MAX_DTR ; + +if (card->scr.sda_spec3) { + card->sw_caps.sd3_bus_mode = status[13 ]; +/* Driver Strengths supported by the card */ + card->sw_caps.sd3_drv_type = status[9 ]; + card->sw_caps.sd3_curr_limit = status[7 ] | status[6 ] <<8 ; + } + +out: +kfree (status); + +return err; +} + +static int sd_select_driver_type (struct mmc_card *card,u8 *status) +{ +int card_drv_type, drive_strength, drv_type; +int err; + + card->drive_strength =0 ; + + card_drv_type = card->sw_caps.sd3_drv_type |SD_DRIVER_TYPE_B ; + + drive_strength =mmc_select_drive_strength (card, + card->sw_caps.uhs_max_dtr, + card_drv_type, &drv_type); + +if (drive_strength) { + err =mmc_sd_switch (card,1 ,2 , drive_strength, status); +if (err) +return err; +if ((status[15 ] &0xF ) != drive_strength) { +pr_warn ("%s: Problem setting drive strength!\n" , +mmc_hostname (card->host)); +return 0 ; + } + card->drive_strength = drive_strength; + } + +if (drv_type) +mmc_set_driver_type (card->host, drv_type); + +return 0 ; +} + +static void sd_update_bus_speed_mode (struct mmc_card *card) +{ +/* +* If the host doesn't support any of the UHS-I modes, fallback on +* default speed. +*/ +if (!mmc_host_uhs (card->host)) { + card->sd_bus_speed =0 ; +return ; + } + +if ((card->host->caps &MMC_CAP_UHS_SDR104 ) && + (card->sw_caps.sd3_bus_mode &SD_MODE_UHS_SDR104 )) { + card->sd_bus_speed =UHS_SDR104_BUS_SPEED ; + }else if ((card->host->caps &MMC_CAP_UHS_DDR50 ) && + (card->sw_caps.sd3_bus_mode &SD_MODE_UHS_DDR50 )) { + card->sd_bus_speed =UHS_DDR50_BUS_SPEED ; + }else if ((card->host->caps & (MMC_CAP_UHS_SDR104 | +MMC_CAP_UHS_SDR50 )) && (card->sw_caps.sd3_bus_mode & +SD_MODE_UHS_SDR50 )) { + card->sd_bus_speed =UHS_SDR50_BUS_SPEED ; + }else if ((card->host->caps & (MMC_CAP_UHS_SDR104 | +MMC_CAP_UHS_SDR50 |MMC_CAP_UHS_SDR25 )) && + (card->sw_caps.sd3_bus_mode &SD_MODE_UHS_SDR25 )) { + card->sd_bus_speed =UHS_SDR25_BUS_SPEED ; + }else if ((card->host->caps & (MMC_CAP_UHS_SDR104 | +MMC_CAP_UHS_SDR50 |MMC_CAP_UHS_SDR25 | +MMC_CAP_UHS_SDR12 )) && (card->sw_caps.sd3_bus_mode & +SD_MODE_UHS_SDR12 )) { + card->sd_bus_speed =UHS_SDR12_BUS_SPEED ; + } +} + +static int sd_set_bus_speed_mode (struct mmc_card *card,u8 *status) +{ +int err; +unsigned int timing =0 ; + +switch (card->sd_bus_speed) { +case UHS_SDR104_BUS_SPEED : + timing =MMC_TIMING_UHS_SDR104 ; + card->sw_caps.uhs_max_dtr =UHS_SDR104_MAX_DTR ; +break ; +case UHS_DDR50_BUS_SPEED : + timing =MMC_TIMING_UHS_DDR50 ; + card->sw_caps.uhs_max_dtr =UHS_DDR50_MAX_DTR ; +break ; +case UHS_SDR50_BUS_SPEED : + timing =MMC_TIMING_UHS_SDR50 ; + card->sw_caps.uhs_max_dtr =UHS_SDR50_MAX_DTR ; +break ; +case UHS_SDR25_BUS_SPEED : + timing =MMC_TIMING_UHS_SDR25 ; + card->sw_caps.uhs_max_dtr =UHS_SDR25_MAX_DTR ; +break ; +case UHS_SDR12_BUS_SPEED : + timing =MMC_TIMING_UHS_SDR12 ; + card->sw_caps.uhs_max_dtr =UHS_SDR12_MAX_DTR ; +break ; +default : +return 0 ; + } + + err =mmc_sd_switch (card,1 ,0 , card->sd_bus_speed, status); +if (err) +return err; + +if ((status[16 ] &0xF ) != card->sd_bus_speed) +pr_warn ("%s: Problem setting bus speed mode!\n" , +mmc_hostname (card->host)); +else { +mmc_set_timing (card->host, timing); +mmc_set_clock (card->host, card->sw_caps.uhs_max_dtr); + } + +return 0 ; +} + +/* Get host's max current setting at its current voltage */ +static u32 sd_get_host_max_current (struct mmc_host *host) +{ +u32 voltage, max_current; + + voltage =1 << host->ios.vdd; +switch (voltage) { +case MMC_VDD_165_195 : + max_current = host->max_current_180; +break ; +case MMC_VDD_29_30 : +case MMC_VDD_30_31 : + max_current = host->max_current_300; +break ; +case MMC_VDD_32_33 : +case MMC_VDD_33_34 : + max_current = host->max_current_330; +break ; +default : + max_current =0 ; + } + +return max_current; +} + +static int sd_set_current_limit (struct mmc_card *card,u8 *status) +{ +int current_limit =SD_SET_CURRENT_NO_CHANGE ; +int err; +u32 max_current; + +/* +* Current limit switch is only defined for SDR50, SDR104, and DDR50 +* bus speed modes. For other bus speed modes, we do not change the +* current limit. +*/ +if ((card->sd_bus_speed !=UHS_SDR50_BUS_SPEED ) && + (card->sd_bus_speed !=UHS_SDR104_BUS_SPEED ) && + (card->sd_bus_speed !=UHS_DDR50_BUS_SPEED )) +return 0 ; + +/* +* Host has different current capabilities when operating at +* different voltages, so find out its max current first. +*/ + max_current =sd_get_host_max_current (card->host); + +/* +* We only check host's capability here, if we set a limit that is +* higher than the card's maximum current, the card will be using its +* maximum current, e.g. if the card's maximum current is 300ma, and +* when we set current limit to 200ma, the card will draw 200ma, and +* when we set current limit to 400/600/800ma, the card will draw its +* maximum 300ma from the host. +* +* The above is incorrect: if we try to set a current limit that is +* not supported by the card, the card can rightfully error out the +* attempt, and remain at the default current limit. This results +* in a 300mA card being limited to 200mA even though the host +* supports 800mA. Failures seen with SanDisk 8GB UHS cards with +* an iMX6 host. --rmk +*/ +if (max_current >=800 && + card->sw_caps.sd3_curr_limit &SD_MAX_CURRENT_800 ) + current_limit =SD_SET_CURRENT_LIMIT_800 ; +else if (max_current >=600 && + card->sw_caps.sd3_curr_limit &SD_MAX_CURRENT_600 ) + current_limit =SD_SET_CURRENT_LIMIT_600 ; +else if (max_current >=400 && + card->sw_caps.sd3_curr_limit &SD_MAX_CURRENT_400 ) + current_limit =SD_SET_CURRENT_LIMIT_400 ; +else if (max_current >=200 && + card->sw_caps.sd3_curr_limit &SD_MAX_CURRENT_200 ) + current_limit =SD_SET_CURRENT_LIMIT_200 ; + +if (current_limit !=SD_SET_CURRENT_NO_CHANGE ) { + err =mmc_sd_switch (card,1 ,3 , current_limit, status); +if (err) +return err; + +if (((status[15 ] >>4 ) &0x0F ) != current_limit) +pr_warn ("%s: Problem setting current limit!\n" , +mmc_hostname (card->host)); + + } + +return 0 ; +} + +static int mmc_wait_for_app_cmd (struct mmc_host *host,struct mmc_card *card, +struct mmc_command *cmd) +{ +struct mmc_request mrq = {}; +int i, err = -EIO ; + +/* +* We have to resend MMC_APP_CMD for each attempt so +* we cannot use the retries field in mmc_command. +*/ +for (i =0 ; i <=MMC_CMD_RETRIES ; i++) { + err =mmc_app_cmd (host, card); +if (err) { +/* no point in retrying; no APP commands allowed */ +if (mmc_host_is_spi (host)) { +if (cmd->resp[0 ] &R1_SPI_ILLEGAL_COMMAND ) +break ; + } +continue ; + } + +memset (&mrq,0 ,sizeof (struct mmc_request )); + +memset (cmd->resp,0 ,sizeof (cmd->resp)); + cmd->retries =0 ; + + mrq.cmd = cmd; + cmd->data =NULL ; + +mmc_wait_for_req (host, &mrq); + + err = cmd->error; +if (!cmd->error) +break ; + +/* no point in retrying illegal APP commands */ +if (mmc_host_is_spi (host)) { +if (cmd->resp[0 ] &R1_SPI_ILLEGAL_COMMAND ) +break ; + } + } + +return err; +} + +static int mmc_app_set_clr_card_detect (struct mmc_card *card) +{ +struct mmc_command cmd = {}; + + cmd.opcode =42 ; + cmd.flags =MMC_RSP_R1 |MMC_CMD_AC ; + +return mmc_wait_for_app_cmd (card->host, card, &cmd); +} +/* +* UHS-I specific initialization procedure +*/ +static int mmc_sd_init_uhs_card (struct mmc_card *card) +{ +int err; +u8 *status; + +if (!(card->csd.cmdclass &CCC_SWITCH )) +return 0 ; + + status =kmalloc (64 ,GFP_KERNEL ); +if (!status) +return -ENOMEM ; + +/* Set 4-bit bus width */ + err =mmc_app_set_bus_width (card,MMC_BUS_WIDTH_4 ); +if (err) + goto out; + +mmc_set_bus_width (card->host,MMC_BUS_WIDTH_4 ); + +/* +* Select the bus speed mode depending on host +* and card capability. +*/ +sd_update_bus_speed_mode (card); + +/* GGC chip need this command to switch mode */ +mmc_app_set_clr_card_detect (card); + +/* Set the driver strength for the card */ + err =sd_select_driver_type (card, status); +if (err) + goto out; + +/* Set current limit for the card */ + err =sd_set_current_limit (card, status); +if (err) + goto out; + +/* Set bus speed mode of the card */ + err =sd_set_bus_speed_mode (card, status); +if (err) + goto out; + +/* +* SPI mode doesn't define CMD19 and tuning is only valid for SDR50 and +* SDR104 mode SD-cards. Note that tuning is mandatory for SDR104. +*/ +if (!mmc_host_is_spi (card->host) && + (card->host->ios.timing ==MMC_TIMING_UHS_SDR50 || + card->host->ios.timing ==MMC_TIMING_UHS_DDR50 || + card->host->ios.timing ==MMC_TIMING_UHS_SDR104 )) { + err =mmc_execute_tuning (card); + +/* +* As SD Specifications Part1 Physical Layer Specification +* Version 3.01 says, CMD19 tuning is available for unlocked +* cards in transfer state of 1.8V signaling mode. The small +* difference between v3.00 and 3.01 spec means that CMD19 +* tuning is also available for DDR50 mode. +*/ +if (err && card->host->ios.timing ==MMC_TIMING_UHS_DDR50 ) { +pr_warn ("%s: ddr50 tuning failed\n" , +mmc_hostname (card->host)); + err =0 ; + } + } + +out: +kfree (status); + +return err; +} + +static int sd_write_ext_reg (struct mmc_card *card,u8 fno,u8 page,u16 offset, +u8 reg_data) +{ +struct mmc_host *host = card->host; +struct mmc_request mrq = {}; +struct mmc_command cmd = {}; +struct mmc_data data = {}; +struct scatterlist sg; +u8 *reg_buf; + + reg_buf =kzalloc (512 ,GFP_KERNEL ); +if (!reg_buf) +return -ENOMEM ; + + mrq.cmd = &cmd; + mrq.data = &data; + +/* +* Arguments of CMD49: +* [31:31] MIO (0 = memory). +* [30:27] FNO (function number). +* [26:26] MW - mask write mode (0 = disable). +* [25:18] page number. +* [17:9] offset address. +* [8:0] length (0 = 1 byte). +*/ + cmd.arg = fno <<27 | page <<18 | offset <<9 ; + +/* The first byte in the buffer is the data to be written. */ + reg_buf[0 ] = reg_data; + + data.flags =MMC_DATA_WRITE ; + data.blksz =512 ; + data.blocks =1 ; + data.sg = &sg; + data.sg_len =1 ; +sg_init_one (&sg, reg_buf,512 ); + + cmd.opcode =SD_WRITE_EXTR_SINGLE ; + cmd.flags =MMC_RSP_R1 |MMC_CMD_ADTC ; + +mmc_set_data_timeout (&data, card); +mmc_wait_for_req (host, &mrq); + +kfree (reg_buf); + +/* +* Note that, the SD card is allowed to signal busy on DAT0 up to 1s +* after the CMD49. Although, let's leave this to be managed by the +* caller. +*/ + +if (cmd.error) +return cmd.error; +if (data.error) +return data.error; + +return 0 ; +} + +static int sd_read_ext_reg (struct mmc_card *card,u8 fno,u8 page, +u16 offset,u16 len,u8 *reg_buf) +{ +u32 cmd_args; + +/* +* Command arguments of CMD48: +* [31:31] MIO (0 = memory). +* [30:27] FNO (function number). +* [26:26] reserved (0). +* [25:18] page number. +* [17:9] offset address. +* [8:0] length (0 = 1 byte, 1ff = 512 bytes). +*/ + cmd_args = fno <<27 | page <<18 | offset <<9 | (len -1 ); + +return mmc_send_adtc_data (card, card->host,SD_READ_EXTR_SINGLE , + cmd_args, reg_buf,512 ); +} + +static int sd_parse_ext_reg_power (struct mmc_card *card,u8 fno,u8 page, +u16 offset) +{ +int err; +u8 *reg_buf; + + reg_buf =kzalloc (512 ,GFP_KERNEL ); +if (!reg_buf) +return -ENOMEM ; + +/* Read the extension register for power management function. */ + err =sd_read_ext_reg (card, fno, page, offset,512 , reg_buf); +if (err) { +pr_warn ("%s: error %d reading PM func of ext reg\n" , +mmc_hostname (card->host), err); + goto out; + } + +/* PM revision consists of 4 bits. */ + card->ext_power.rev = reg_buf[0 ] &0xf ; + +/* Power Off Notification support at bit 4. */ +if (reg_buf[1 ] &BIT (4 )) + card->ext_power.feature_support |=SD_EXT_POWER_OFF_NOTIFY ; + +/* Power Sustenance support at bit 5. */ +if (reg_buf[1 ] &BIT (5 )) + card->ext_power.feature_support |=SD_EXT_POWER_SUSTENANCE ; + +/* Power Down Mode support at bit 6. */ +if (reg_buf[1 ] &BIT (6 )) + card->ext_power.feature_support |=SD_EXT_POWER_DOWN_MODE ; + + card->ext_power.fno = fno; + card->ext_power.page = page; + card->ext_power.offset = offset; + +out: +kfree (reg_buf); +return err; +} + +static int sd_parse_ext_reg_perf (struct mmc_card *card,u8 fno,u8 page, +u16 offset) +{ +int err; +u8 *reg_buf; + + reg_buf =kzalloc (512 ,GFP_KERNEL ); +if (!reg_buf) +return -ENOMEM ; + + err =sd_read_ext_reg (card, fno, page, offset,512 , reg_buf); +if (err) { +pr_warn ("%s: error %d reading PERF func of ext reg\n" , +mmc_hostname (card->host), err); + goto out; + } + +/* PERF revision. */ + card->ext_perf.rev = reg_buf[0 ]; + +/* FX_EVENT support at bit 0. */ +if (reg_buf[1 ] &BIT (0 )) + card->ext_perf.feature_support |=SD_EXT_PERF_FX_EVENT ; + +/* Card initiated self-maintenance support at bit 0. */ +if (reg_buf[2 ] &BIT (0 )) + card->ext_perf.feature_support |=SD_EXT_PERF_CARD_MAINT ; + +/* Host initiated self-maintenance support at bit 1. */ +if (reg_buf[2 ] &BIT (1 )) + card->ext_perf.feature_support |=SD_EXT_PERF_HOST_MAINT ; + +/* Cache support at bit 0. */ +if (reg_buf[4 ] &BIT (0 )) + card->ext_perf.feature_support |=SD_EXT_PERF_CACHE ; + +/* Command queue support indicated via queue depth bits (0 to 4). */ +if (reg_buf[6 ] &0x1f ) + card->ext_perf.feature_support |=SD_EXT_PERF_CMD_QUEUE ; + + card->ext_perf.fno = fno; + card->ext_perf.page = page; + card->ext_perf.offset = offset; + +out: +kfree (reg_buf); +return err; +} + +static int sd_parse_ext_reg (struct mmc_card *card,u8 *gen_info_buf, +u16 *next_ext_addr) +{ +u8 num_regs, fno, page; +u16 sfc, offset, ext = *next_ext_addr; +u32 reg_addr; + +/* +* Parse only one register set per extension, as that is sufficient to +* support the standard functions. This means another 48 bytes in the +* buffer must be available. +*/ +if (ext +48 >512 ) +return -EFAULT ; + +/* Standard Function Code */ +memcpy (&sfc, &gen_info_buf[ext],2 ); + +/* Address to the next extension. */ +memcpy (next_ext_addr, &gen_info_buf[ext +40 ],2 ); + +/* Number of registers for this extension. */ + num_regs = gen_info_buf[ext +42 ]; + +/* We support only one register per extension. */ +if (num_regs !=1 ) +return 0 ; + +/* Extension register address. */ +memcpy (®_addr, &gen_info_buf[ext +44 ],4 ); + +/* 9 bits (0 to 8) contains the offset address. */ + offset = reg_addr &0x1ff ; + +/* 8 bits (9 to 16) contains the page number. */ + page = reg_addr >>9 &0xff ; + +/* 4 bits (18 to 21) contains the function number. */ + fno = reg_addr >>18 &0xf ; + +/* Standard Function Code for power management. */ +if (sfc ==0x1 ) +return sd_parse_ext_reg_power (card, fno, page, offset); + +/* Standard Function Code for performance enhancement. */ +if (sfc ==0x2 ) +return sd_parse_ext_reg_perf (card, fno, page, offset); + +return 0 ; +} + +static int sd_read_ext_regs (struct mmc_card *card) +{ +int err, i; +u8 num_ext, *gen_info_buf; +u16 rev, len, next_ext_addr; + +if (mmc_host_is_spi (card->host)) +return 0 ; + +if (!(card->scr.cmds &SD_SCR_CMD48_SUPPORT )) +return 0 ; + + gen_info_buf =kzalloc (512 ,GFP_KERNEL ); +if (!gen_info_buf) +return -ENOMEM ; + +/* +* Read 512 bytes of general info, which is found at function number 0, +* at page 0 and with no offset. +*/ + err =sd_read_ext_reg (card,0 ,0 ,0 ,512 , gen_info_buf); +if (err) { +pr_warn ("%s: error %d reading general info of SD ext reg\n" , +mmc_hostname (card->host), err); + goto out; + } + +/* General info structure revision. */ +memcpy (&rev, &gen_info_buf[0 ],2 ); + +/* Length of general info in bytes. */ +memcpy (&len, &gen_info_buf[2 ],2 ); + +/* Number of extensions to be find. */ + num_ext = gen_info_buf[4 ]; + +/* We support revision 0, but limit it to 512 bytes for simplicity. */ +if (rev !=0 || len >512 ) { +pr_warn ("%s: non-supported SD ext reg layout\n" , +mmc_hostname (card->host)); + goto out; + } + +/* +* Parse the extension registers. The first extension should start +* immediately after the general info header (16 bytes). +*/ + next_ext_addr =16 ; +for (i =0 ; i < num_ext; i++) { + err =sd_parse_ext_reg (card, gen_info_buf, &next_ext_addr); +if (err) { +pr_warn ("%s: error %d parsing SD ext reg\n" , +mmc_hostname (card->host), err); + goto out; + } + } + +out: +kfree (gen_info_buf); +return err; +} + +static bool sd_cache_enabled (struct mmc_host *host) +{ +return host->card->ext_perf.feature_enabled &SD_EXT_PERF_CACHE ; +} + +static int sd_flush_cache (struct mmc_host *host) +{ +struct mmc_card *card = host->card; +u8 *reg_buf, fno, page; +u16 offset; +int err; + +if (!sd_cache_enabled (host)) +return 0 ; + + reg_buf =kzalloc (512 ,GFP_KERNEL ); +if (!reg_buf) +return -ENOMEM ; + +/* +* Set Flush Cache at bit 0 in the performance enhancement register at +* 261 bytes offset. +*/ + fno = card->ext_perf.fno; + page = card->ext_perf.page; + offset = card->ext_perf.offset +261 ; + + err =sd_write_ext_reg (card, fno, page, offset,BIT (0 )); +if (err) { +pr_warn ("%s: error %d writing Cache Flush bit\n" , +mmc_hostname (host), err); + goto out; + } + + err =mmc_poll_for_busy (card,SD_WRITE_EXTR_SINGLE_TIMEOUT_MS , false, +MMC_BUSY_EXTR_SINGLE ); +if (err) + goto out; + +/* +* Read the Flush Cache bit. The card shall reset it, to confirm that +* it's has completed the flushing of the cache. +*/ + err =sd_read_ext_reg (card, fno, page, offset,1 , reg_buf); +if (err) { +pr_warn ("%s: error %d reading Cache Flush bit\n" , +mmc_hostname (host), err); + goto out; + } + +if (reg_buf[0 ] &BIT (0 )) + err = -ETIMEDOUT ; +out: +kfree (reg_buf); +return err; +} + +static int sd_enable_cache (struct mmc_card *card) +{ +u8 *reg_buf; +int err; + + card->ext_perf.feature_enabled &= ~SD_EXT_PERF_CACHE ; + + reg_buf =kzalloc (512 ,GFP_KERNEL ); +if (!reg_buf) +return -ENOMEM ; + +/* +* Set Cache Enable at bit 0 in the performance enhancement register at +* 260 bytes offset. +*/ + err =sd_write_ext_reg (card, card->ext_perf.fno, card->ext_perf.page, + card->ext_perf.offset +260 ,BIT (0 )); +if (err) { +pr_warn ("%s: error %d writing Cache Enable bit\n" , +mmc_hostname (card->host), err); + goto out; + } + + err =mmc_poll_for_busy (card,SD_WRITE_EXTR_SINGLE_TIMEOUT_MS , false, +MMC_BUSY_EXTR_SINGLE ); +if (!err) + card->ext_perf.feature_enabled |=SD_EXT_PERF_CACHE ; + +out: +kfree (reg_buf); +return err; +} + +/* +* Handle the detection and initialisation of a card. +* +* In the case of a resume, "oldcard" will contain the card +* we're trying to reinitialise. +*/ +static int mmc_sd_init_card_bayhub (struct mmc_host *host,u32 ocr, +struct mmc_card *oldcard) +{ +struct mmc_card *card; +int err; +u32 cid[4 ]; +u32 rocr =0 ; +bool v18_fixup_failed = false; + +WARN_ON (!host->claimed); +retry: + err =mmc_sd_get_cid (host, ocr, cid, &rocr); +if (err) +return err; + +if (oldcard) { +if (memcmp (cid, oldcard->raw_cid,sizeof (cid)) !=0 ) { +pr_debug ("%s: Perhaps the card was replaced\n" , +mmc_hostname (host)); +return -ENOENT ; + } + + card = oldcard; + }else { +/* +* Allocate card structure. +*/ + card =mmc_alloc_card (host, &sd_type); +if (IS_ERR (card)) +return PTR_ERR (card); + + card->ocr = ocr; + card->type =MMC_TYPE_SD ; +memcpy (card->raw_cid, cid,sizeof (card->raw_cid)); + } + +/* +* Call the optional HC's init_card function to handle quirks. +*/ +if (host->ops->init_card) + host->ops->init_card (host, card); + +/* +* For native busses: get card RCA and quit open drain mode. +*/ +if (!mmc_host_is_spi (host)) { + err =mmc_send_relative_addr (host, &card->rca); +if (err) + goto free_card; + } + +if (!oldcard) { + err =mmc_sd_get_csd (card); +if (err) + goto free_card; + +mmc_decode_cid (card); + } + +/* +* handling only for cards supporting DSR and hosts requesting +* DSR configuration +*/ +if (card->csd.dsr_imp && host->dsr_req) +mmc_set_dsr (host); + +/* +* Select card, as all following commands rely on that. +*/ +if (!mmc_host_is_spi (host)) { + err =mmc_select_card (card); +if (err) + goto free_card; + } + + err =mmc_sd_setup_card (host, card, oldcard !=NULL ); +if (err) + goto free_card; + +/* +* If the card has not been power cycled, it may still be using 1.8V +* signaling. Detect that situation and try to initialize a UHS-I (1.8V) +* transfer mode. +*/ +if (!v18_fixup_failed && !mmc_host_is_spi (host) &&mmc_host_uhs (host) && +mmc_sd_card_using_v18 (card) && + host->ios.signal_voltage !=MMC_SIGNAL_VOLTAGE_180 ) { +/* +* Re-read switch information in case it has changed since +* oldcard was initialized. +*/ +if (oldcard) { + err =mmc_read_switch (card); +if (err) + goto free_card; + } +if (mmc_sd_card_using_v18 (card)) { +if (mmc_host_set_uhs_voltage (host) || +mmc_sd_init_uhs_card (card)) { + v18_fixup_failed = true; +mmc_power_cycle (host, ocr); +if (!oldcard) +mmc_remove_card (card); + goto retry; + } + goto done; + } + } + +/* Initialization sequence for UHS-I cards */ +if (rocr &SD_ROCR_S18A &&mmc_host_uhs (host)) { + err =mmc_sd_init_uhs_card (card); +if (err) + goto free_card; + }else { +/* +* Switch to wider bus (if supported). +* Bayhub patch move mmc_app_set_bus_width in front of mmc_sd_switch_hs +*/ +if ((host->caps &MMC_CAP_4_BIT_DATA ) && + (card->scr.bus_widths &SD_SCR_BUS_WIDTH_4 )) { + err =mmc_app_set_bus_width (card,MMC_BUS_WIDTH_4 ); +if (err) + goto free_card; + +mmc_set_bus_width (host,MMC_BUS_WIDTH_4 ); + } + +/* GGC chip need this command to switch mode */ +mmc_app_set_clr_card_detect (card); + +/* +* Attempt to change to high-speed (if supported) +*/ + err =mmc_sd_switch_hs (card); +if (err >0 ) +mmc_set_timing (card->host,MMC_TIMING_SD_HS ); +else if (err) + goto free_card; + +/* +* Set bus speed. +*/ +mmc_set_clock (host,mmc_sd_get_max_clock (card)); + } + +if (!oldcard) { +/* Read/parse the extension registers. */ + err =sd_read_ext_regs (card); +if (err) + goto free_card; + } + +/* Enable internal SD cache if supported. */ +if (card->ext_perf.feature_support &SD_EXT_PERF_CACHE ) { + err =sd_enable_cache (card); +if (err) + goto free_card; + } + +if (host->cqe_ops && !host->cqe_enabled) { + err = host->cqe_ops->cqe_enable (host, card); +if (!err) { + host->cqe_enabled = true; + host->hsq_enabled = true; +pr_info ("%s: Host Software Queue enabled\n" , +mmc_hostname (host)); + } + } + +if (host->caps2 &MMC_CAP2_AVOID_3_3V && + host->ios.signal_voltage ==MMC_SIGNAL_VOLTAGE_330 ) { +pr_err ("%s: Host failed to negotiate down from 3.3V\n" , +mmc_hostname (host)); + err = -EINVAL ; + goto free_card; + } +done: + host->card = card; +return 0 ; + +free_card: +if (!oldcard) +mmc_remove_card (card); + +return err; +} + +/* +* Host is being removed. Free up the current card. +*/ +static void mmc_sd_remove (struct mmc_host *host) +{ +mmc_remove_card (host->card); + host->card =NULL ; +} + +/* +* Card detection - card is alive. +*/ +static int mmc_sd_alive (struct mmc_host *host) +{ +return mmc_send_status (host->card,NULL ); +} + +/* +* Card detection callback from host. +*/ +static void mmc_sd_detect (struct mmc_host *host) +{ +int err; + +mmc_get_card (host->card,NULL ); + +/* +* Just check if our card has been removed. +*/ + err =_mmc_detect_card_removed (host); + +mmc_put_card (host->card,NULL ); + +if (err) { +mmc_sd_remove (host); + +mmc_claim_host (host); +mmc_detach_bus (host); +mmc_power_off (host); +mmc_release_host (host); + } +} + +static int sd_can_poweroff_notify (struct mmc_card *card) +{ +return card->ext_power.feature_support &SD_EXT_POWER_OFF_NOTIFY ; +} + +static int sd_busy_poweroff_notify_cb (void *cb_data,bool *busy) +{ +struct sd_busy_data *data = cb_data; +struct mmc_card *card = data->card; +int err; + +/* +* Read the status register for the power management function. It's at +* one byte offset and is one byte long. The Power Off Notification +* Ready is bit 0. +*/ + err =sd_read_ext_reg (card, card->ext_power.fno, card->ext_power.page, + card->ext_power.offset +1 ,1 , data->reg_buf); +if (err) { +pr_warn ("%s: error %d reading status reg of PM func\n" , +mmc_hostname (card->host), err); +return err; + } + + *busy = !(data->reg_buf[0 ] &BIT (0 )); +return 0 ;Lines of this file are not shown. View the raw message.
The rest of this diff is not shown. View the raw message.
base-commit:
c9e6606c7fe92b50a02ce51dda82586ebdf99b48Signature · 1 line
2.32.0
Part of the diff is not shown here. View the raw message.