From 46148b7e38e66fb2973ab99a6b680bc347b6b9bb Mon Sep 17 00:00:00 2001 From: Nan Li Date: Tue, 11 Apr 2017 14:57:52 +0800 Subject: [PATCH] emmc: add emmc driver support on m8b PD#141217: initial add emmc dirver support on m8b 1) add emmc driver support HS200 mode 85M 2) add sd driver support HS mode 38M test on m8b_m200. Change-Id: I1575c45af2e1246019d54a8092ee29e1da0a1a70 Signed-off-by: Nan Li --- MAINTAINERS | 11 + arch/arm/boot/dts/amlogic/meson8b.dtsi | 153 ++ arch/arm/boot/dts/amlogic/meson8b_m200.dts | 153 ++ arch/arm/configs/meson32_defconfig | 4 + drivers/amlogic/mmc/Kconfig | 11 + drivers/amlogic/mmc/Makefile | 2 + drivers/amlogic/mmc/aml_sd_emmc.c | 2 +- drivers/amlogic/mmc/aml_sdhc_m8.c | 2477 ++++++++++++++++++++ drivers/amlogic/mmc/aml_sdio.c | 1367 +++++++++++ drivers/amlogic/mmc/amlsd.c | 286 ++- drivers/amlogic/mmc/amlsd_of.c | 48 +- include/linux/amlogic/amlsd.h | 17 +- include/linux/amlogic/cpu_version.h | 1 + include/linux/amlogic/sd.h | 7 + 14 files changed, 4502 insertions(+), 37 deletions(-) create mode 100644 drivers/amlogic/mmc/aml_sdhc_m8.c create mode 100644 drivers/amlogic/mmc/aml_sdio.c diff --git a/MAINTAINERS b/MAINTAINERS index 7da867315d72..b67edfa38951 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13781,6 +13781,17 @@ F: drivers/amlogic/media/video_processor/Kconfig F: drivers/amlogic/media/video_processor/Makefile F: drivers/amlogic/media/video_processor/ionvideo/* +AMLOGIC EMMC S805 DRIVER SUPPORT +M: Nan Li +F: arch/arm/boot/dts/amlogic/meson8b.dtsi +F: arch/arm/boot/dts/amlogic/meson8b_m200.dts +F: drivers/amlogic/emmc/aml_sdhc_m8.c +F: drivers/amlogic/emmc/aml_sdio.c +F: drivers/amlogic/emmc/Kconfig +F: drivers/amlogic/emmc/Makefile +F: include/linux/amlogic/sd.h +F: include/linux/amlogic/cpu_version.h + AMLOGIC PINCTRL SUPPORT FOR M8B M: Xingyu Chen F: drivers/amlogic/pinctrl/pinctrl-meson8b.c diff --git a/arch/arm/boot/dts/amlogic/meson8b.dtsi b/arch/arm/boot/dts/amlogic/meson8b.dtsi index 61f9d0f8b1eb..ead1a1958909 100644 --- a/arch/arm/boot/dts/amlogic/meson8b.dtsi +++ b/arch/arm/boot/dts/amlogic/meson8b.dtsi @@ -18,6 +18,7 @@ #include #include #include +#include #include "skeleton.dtsi" / { @@ -209,6 +210,158 @@ interrupt-controller; #interrupt-cells = <2>; }; + + emmc_clk_cmd_pins:emmc_clk_cmd_pins { + mux { + groups = "sd_cmd_c", + "sd_clk_c"; + function = "sd_c"; + input-enable; + bias-pull-up; + }; + }; + + emmc_all_pins:emmc_all_pins { + mux { + groups = "sd_d0_c", + "sd_d1_c", + "sd_d2_c", + "sd_d3_c", + "sd_cmd_c", + "sd_clk_c"; + function = "sd_c"; + input-enable; + bias-pull-up; + }; + }; + + sd_clk_cmd_pins:sd_clk_cmd_pins{ + mux { + groups = "sd_cmd_b", + "sd_clk_b"; + function = "sd_b"; + input-enable; + bias-pull-up; + }; + }; + + sd_1bit_pins:sd_1bit_pins{ + mux { + groups = "sd_d1_b", + "sd_d2_b", + "sd_d3_b", + "sd_cmd_b", + "sd_clk_b"; + function = "sd_b"; + input-enable; + bias-pull-up; + }; + }; + + sd_all_pins:sd_all_pins{ + mux { + groups = "sd_d0_b", + "sd_d1_b", + "sd_d2_b", + "sd_d3_b", + "sd_cmd_b", + "sd_clk_b"; + function = "sd_b"; + input-enable; + bias-pull-up; + }; + }; + + sdio_clk_cmd_pins:sdio_clk_cmd_pins { + mux { + groups = "sd_clk_a", + "sd_cmd_a"; + function = "sd_a"; + input-enable; + bias-pull-up; + }; + }; + + sdio_all_pins:sdio_all_pins { + mux { + groups = "sd_d0_a", + "sd_d1_a", + "sd_d2_a", + "sd_d3_a", + "sd_clk_a", + "sd_cmd_a"; + function = "sd_a"; + input-enable; + bias-pull-up; + }; + }; + + sdhc_emmc_clk_cmd_pins:sdhc_emmc_clk_cmd_pins { + mux { + groups = "sdxc_clk_c", + "sdxc_cmd_c"; + function = "sdxc_c"; + input-enable; + bias-pull-up; + }; + }; + + sdhc_emmc_all_pins:sdhc_emmc_all_pins { + mux { + groups = "sdxc_d0_c", + "sdxc_d13_c", + "sdxc_d47_c", + "sdxc_clk_c", + "sdxc_cmd_c"; + function = "sdxc_c"; + input-enable; + bias-pull-up; + }; + }; + + sdhc_sd_clk_cmd_pins:sdhc_sd_clk_cmd_pins { + mux { + groups = "sdxc_clk_b", + "sdxc_cmd_b"; + function = "sdxc_b"; + input-enable; + bias-pull-up; + }; + }; + + sdhc_sd_all_pins:sdhc_sd_all_pins { + mux { + groups = "sdxc_d0_b", + "sdxc_d13_b", + "sdxc_clk_b", + "sdxc_cmd_b"; + function = "sdxc_b"; + input-enable; + bias-pull-up; + }; + }; + + sdhc_sdio_clk_cmd_pins:sdhc_sdio_clk_cmd_pins { + mux { + groups = "sdxc_clk_a", + "sdxc_cmd_a"; + function = "sdxc_a"; + input-enable; + bias-pull-up; + }; + }; + + sdhc_sdio_all_pins:sdhc_sdio_all_pins { + mux { + groups = "sdxc_d0_1_a", + "sdxc_d13_1_a", + "sdxc_clk_a", + "sdxc_cmd_a"; + function = "sdxc_a"; + input-enable; + bias-pull-up; + }; + }; }; pinctrl_aobus: pinctrl@c8100084 { diff --git a/arch/arm/boot/dts/amlogic/meson8b_m200.dts b/arch/arm/boot/dts/amlogic/meson8b_m200.dts index 51dc09bcc3a9..0e604a6a2b53 100755 --- a/arch/arm/boot/dts/amlogic/meson8b_m200.dts +++ b/arch/arm/boot/dts/amlogic/meson8b_m200.dts @@ -30,6 +30,159 @@ reg = <0x00000000 0x40000000>; linux,usable-memory = <0x00200000 0x3fe00000>; }; + + sdio { + compatible = "amlogic, aml_sdio"; + dev_name = "aml_sdio.0"; + status = "okay"; + interrupts = <0 28 1>; + reg = <0xc1108c20 0x20>; + pinctrl-names = "sd_clk_cmd_pins", + "sd_all_pins", + "emmc_clk_cmd_pins", + "emmc_all_pins", + "sdio_clk_cmd_pins", + "sdio_all_pins", + "sd_1bit_pins"; + pinctrl-0 = <&sd_clk_cmd_pins>; + pinctrl-1 = <&sd_all_pins>; + pinctrl-2 = <&emmc_clk_cmd_pins>; + pinctrl-3 = <&emmc_all_pins>; + pinctrl-4 = <&sdio_clk_cmd_pins>; + pinctrl-5 = <&sdio_all_pins>; + pinctrl-6 = <&sd_1bit_pins>; + clocks = <&clkc CLKID_SDIO>; + clock-names = "core"; + + sd { + status = "okay"; + port = <1>; + /* 0:sdio_a, + * 1:sdio_b, + * 2:sdio_c, + * 3:sdhc_a, + * 4:sdhc_b, + * 5:sdhc_c + */ + pinname = "sd"; + ocr_avail = <0x200000>; /**VDD voltage 3.3 ~ 3.4 */ + caps = "MMC_CAP_4_BIT_DATA", + "MMC_CAP_MMC_HIGHSPEED", + "MMC_CAP_SD_HIGHSPEED"; + f_min = <300000>; + f_max = <50000000>; + f_max_w = <50000000>; + max_req_size = <0x20000>; /*128KB*/ + gpio_dat3 = <&gpio CARD_4 GPIO_ACTIVE_HIGH>; + jtag_pin = <&gpio CARD_0 GPIO_ACTIVE_HIGH>; + gpio_cd = <&gpio CARD_6 GPIO_ACTIVE_HIGH>; + gpio_ro = <&gpio GPIODV_25 GPIO_ACTIVE_HIGH>; + card_type = <5>; + /* 0:unknown, + * 1:mmc card(include eMMC), + * 2:sd card(include tSD), + * 3:sdio device(ie:sdio-wifi), + * 4:SD combo (IO+mem) card, + * 5:NON sdio device(means sd/mmc card), + * other:reserved + */ + }; + }; + + sdhc { + compatible = "amlogic, aml_sdhc"; + dev_name = "aml_sdhc.0"; + status = "okay"; + interrupts = <0 78 1>; + reg = <0xc1108e00 0x3c>; + pinctrl-names = "sdhc_sd_clk_cmd_pins", + "sdhc_sd_all_pins", + "sdhc_emmc_clk_cmd_pins", + "sdhc_emmc_all_pins", + "sdhc_sdio_clk_cmd_pins", + "sdhc_sdio_all_pins"; + pinctrl-0 = <&sdhc_sd_clk_cmd_pins>; + pinctrl-1 = <&sdhc_sd_all_pins>; + pinctrl-2 = <&sdhc_emmc_clk_cmd_pins>; + pinctrl-3 = <&sdhc_emmc_all_pins>; + pinctrl-4 = <&sdhc_sdio_clk_cmd_pins>; + pinctrl-5 = <&sdhc_sdio_all_pins>; + clocks = <&clkc CLKID_SDHC>, + <&clkc CLKID_FCLK_DIV3>; + clock-names = "core", "div3"; + + emmc { + status = "okay"; + port = <5>; + /* 0:sdio_a, + * 1:sdio_b, + * 2:sdio_c, + * 3:sdhc_a, + * 4:sdhc_b, + * 5:sdhc_c + */ + pinname = "emmc"; + ocr_avail = <0x00200080>; + /* 3.3:0x200000, 1.8+3.3:0x00200080 */ + caps = "MMC_CAP_8_BIT_DATA", + "MMC_CAP_MMC_HIGHSPEED", + "MMC_CAP_SD_HIGHSPEED", + "MMC_CAP_NONREMOVABLE", + "MMC_CAP_ERASE", + "MMC_CAP_HW_RESET"; + caps2 = "MMC_CAP2_HS200_1_8V_SDR"; + f_min = <300000>; + f_max = <100000000>; + max_req_size = <0x20000>; /* 128KB */ + gpio_dat3 = <&gpio BOOT_3 GPIO_ACTIVE_HIGH>; + hw_reset = <&gpio BOOT_9 GPIO_ACTIVE_HIGH>; + card_type = <1>; + /* 0:unknown, + * 1:mmc card(include eMMC), + * 2:sd card(include tSD), + * 3:sdio device(ie:sdio-wifi), + * 4:SD combo (IO+mem) card, + * 5:NON sdio device(means sd/mmc card), + * other:reserved + */ + }; + + sdio { + status = "okay"; + port = <3>; + /* 0:sdio_a, + * 1:sdio_b, + * 2:sdio_c, + * 3:sdhc_a, + * 4:sdhc_b, + * 5:sdhc_c + */ + pinname = "sdio"; + ocr_avail = <0x00200080>; + /* 3.3:0x200000, 1.8+3.3:0x00200080 */ + caps = "MMC_CAP_4_BIT_DATA", + "MMC_CAP_MMC_HIGHSPEED", + "MMC_CAP_SD_HIGHSPEED", + "MMC_CAP_NONREMOVABLE", + "MMC_CAP_UHS_SDR12", + "MMC_CAP_UHS_SDR25", + "MMC_CAP_UHS_SDR50", + "MMC_CAP_UHS_SDR104", + "MMC_PM_KEEP_POWER"; + f_min = <300000>; + f_max = <100000000>; + max_req_size = <0x20000>; /* 128KB */ + card_type = <3>; + /* 0:unknown, + * 1:mmc card(include eMMC), + * 2:sd card(include tSD), + * 3:sdio device(ie:sdio-wifi), + * 4:SD combo (IO+mem) card, + * 5:NON sdio device(means sd/mmc card), + * other:reserved + */ + }; + }; }; &uart_AO { diff --git a/arch/arm/configs/meson32_defconfig b/arch/arm/configs/meson32_defconfig index a7ea61feaa74..9d460f6b2bb6 100644 --- a/arch/arm/configs/meson32_defconfig +++ b/arch/arm/configs/meson32_defconfig @@ -54,6 +54,8 @@ CONFIG_AMLOGIC_CLK=y CONFIG_AMLOGIC_M8B_CLK=y CONFIG_AMLOGIC_CRYPTO=y CONFIG_AMLOGIC_CRYPTO_BLKMV=y +CONFIG_AMLOGIC_MMC=y +CONFIG_AMLOGIC_M8B_MMC=y CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS_MOUNT=y CONFIG_DMA_CMA=y @@ -106,6 +108,8 @@ CONFIG_USB_CONFIGFS_F_MTP=y CONFIG_USB_CONFIGFS_F_PTP=y CONFIG_USB_CONFIGFS_F_ACC=y CONFIG_USB_CONFIGFS_UEVENT=y +CONFIG_MMC=y +CONFIG_MMC_BLOCK_MINORS=16 CONFIG_STAGING=y CONFIG_CHROME_PLATFORMS=y CONFIG_ARM_TIMER_SP804=y diff --git a/drivers/amlogic/mmc/Kconfig b/drivers/amlogic/mmc/Kconfig index 302608a84f4d..8512d5b30286 100644 --- a/drivers/amlogic/mmc/Kconfig +++ b/drivers/amlogic/mmc/Kconfig @@ -16,4 +16,15 @@ config AMLOGIC_MMC MMC 5.1 compliant and supports SD, eMMC and SDIO interfaces. If you have a controller with this interface, say Y here. +config AMLOGIC_M8B_MMC + bool "Amlogic M8B Multimedia Card support" + depends on AMLOGIC_MMC + default n + help + This selects support for the Amlogic SD/MMC Host Controller + found on the S805/M8B family of SoCs. This controller is + MMC 5.1 compliant and supports SD, eMMC and SDIO interfaces. + If you have a controller with this interface, say Y here. + + endmenu diff --git a/drivers/amlogic/mmc/Makefile b/drivers/amlogic/mmc/Makefile index eb7250176f7e..db585e9418b0 100644 --- a/drivers/amlogic/mmc/Makefile +++ b/drivers/amlogic/mmc/Makefile @@ -3,3 +3,5 @@ # obj-$(CONFIG_AMLOGIC_MMC) += amlsd.o amlsd_of.o emmc_partitions.o aml_sd_emmc.o emmc_key.o + +obj-$(CONFIG_AMLOGIC_M8B_MMC) += aml_sdhc_m8.o aml_sdio.o diff --git a/drivers/amlogic/mmc/aml_sd_emmc.c b/drivers/amlogic/mmc/aml_sd_emmc.c index 0fe572194c4b..c55c8bf1a433 100644 --- a/drivers/amlogic/mmc/aml_sd_emmc.c +++ b/drivers/amlogic/mmc/aml_sd_emmc.c @@ -2779,7 +2779,7 @@ static int meson_mmc_probe(struct platform_device *pdev) goto free_host; } - if (amlsd_get_platform_data(pdata, mmc, 0)) + if (amlsd_get_platform_data(pdev, pdata, mmc, 0)) mmc_free_host(mmc); if (aml_card_type_mmc(pdata)) diff --git a/drivers/amlogic/mmc/aml_sdhc_m8.c b/drivers/amlogic/mmc/aml_sdhc_m8.c new file mode 100644 index 000000000000..36c1077ec2fb --- /dev/null +++ b/drivers/amlogic/mmc/aml_sdhc_m8.c @@ -0,0 +1,2477 @@ +/* + * drivers/amlogic/mmc/aml_sdhc_m8.c + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* #define DMC_URGENT_PERIPH */ +unsigned int rx_clk_phase_set = 1; +unsigned int sd_clk_phase_set = 1; +unsigned int rx_endian = 7; +unsigned int tx_endian = 7; +unsigned int val1; +unsigned int cmd25_cnt; +unsigned int fifo_empty_cnt; +unsigned int fifo_full_cnt; +unsigned int timeout_cnt; +static unsigned int sdhc_error_flag; +static unsigned int sdhc_debug_flag; +static int sdhc_err_bak; +static struct semaphore sdhc_sema; + +static void aml_sdhc_send_stop(struct amlsd_host *host); +static void aml_sdhc_emmc_clock_switch_on(struct amlsd_platform *pdata); +static void aml_sdhc_clk_switch_off(struct amlsd_host *host); +static void aml_sdhc_clk_switch(struct amlsd_platform *pdata, + int clk_div, int clk_src_sel); +static int aml_sdhc_status(struct amlsd_host *host); + +static void __attribute__((unused))sdhc_debug_status(struct amlsd_host *host) +{ + switch (sdhc_debug_flag) { + case 1: + host->status = HOST_TX_FIFO_EMPTY; + sdhc_err("Force host->status:%d here\n", host->status); + break; + case 2: + host->status = HOST_RX_FIFO_FULL; + sdhc_err("Force host->status:%d here\n", host->status); + break; + case 3: + host->status = HOST_RSP_CRC_ERR; + sdhc_err("Force host->status:%d here\n", host->status); + break; + case 4: + host->status = HOST_DAT_CRC_ERR; + sdhc_err("Force host->status:%d here\n", host->status); + break; + case 5: + host->status = HOST_DAT_TIMEOUT_ERR; + sdhc_err("Force host->status:%d here\n", host->status); + break; + case 6: + host->status = HOST_RSP_TIMEOUT_ERR; + sdhc_err("Force host->status:%d here\n", host->status); + break; + default: + break; + } + + /* only enable once for debug */ + sdhc_debug_flag = 0; +} + +void aml_debug_print_buf(char *buf, int size) +{ + int i; + + if (size > 512) + size = 512; + + pr_info("%8s : ", "Address"); + for (i = 0; i < 16; i++) + pr_info("%02x ", i); + + pr_info("\n"); + pr_info("==========================================================\n"); + + for (i = 0; i < size; i++) { + if ((i % 16) == 0) + pr_info("%08x : ", i); + + pr_info("%02x ", buf[i]); + + if ((i % 16) == 15) + pr_info("\n"); + } + pr_info("\n"); +} + +int aml_buf_verify(int *buf, int blocks, int lba) +{ + int block_size; + int i, j; + int lba_bak = lba; + + sdhc_err("Enter\n"); + for (i = 0; i < blocks; i++) { + for (j = 0; j < 128; j++) { + if (buf[j] != (lba*512 + j)) { + sdhc_err( + "buf error, lba_bak=%#x,lba=%#x,offset=%#x,blocks=%d\n", + lba_bak, lba, j, blocks); + + sdhc_err("buf[j]=%#x, target=%#x\n", + buf[j], (lba*512 + j)); + + block_size = (lba - lba_bak)*512; + aml_debug_print_buf((char *)(buf+block_size), + 512); + + return -1; + } + } + lba++; + buf += 128; + } + + return 0; +} + +static int aml_sdhc_execute_tuning_(struct mmc_host *mmc, u32 opcode, + struct aml_tuning_data *tuning_data) +{ + struct amlsd_platform *pdata = mmc_priv(mmc); + struct amlsd_host *host = pdata->host; + struct sdhc_clkc *clkc = (struct sdhc_clkc *)&(pdata->clkc); + u32 vclk2, vclk2_bak; + struct sdhc_clk2 *clk2 = (struct sdhc_clk2 *)&vclk2; + const u8 *blk_pattern = tuning_data->blk_pattern; + u8 *blk_test; + unsigned int blksz = tuning_data->blksz; + int ret = 0; + unsigned long flags; + + int n, nmatch, ntries = 10; + int rx_phase = 0; + int wrap_win_start = -1, wrap_win_size = 0; + int best_win_start = -1, best_win_size = -1; + int curr_win_start = -1, curr_win_size = 0; + + u8 rx_tuning_result[20] = { 0 }; + + spin_lock_irqsave(&host->mrq_lock, flags); + pdata->need_retuning = false; + spin_unlock_irqrestore(&host->mrq_lock, flags); + + vclk2_bak = readl(host->base + SDHC_CLK2); + + blk_test = kmalloc(blksz, GFP_KERNEL); + if (!blk_test) + return -ENOMEM; + + for (rx_phase = 0; rx_phase <= clkc->clk_div; rx_phase++) { + /* Perform tuning ntries times per clk_div increment */ + for (n = 0, nmatch = 0; n < ntries; n++) { + struct mmc_request mrq = {NULL}; + struct mmc_command cmd = {0}; + struct mmc_command stop = {0}; + struct mmc_data data = {0}; + struct scatterlist sg; + + cmd.opcode = opcode; + cmd.arg = 0; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + + stop.opcode = MMC_STOP_TRANSMISSION; + stop.arg = 0; + stop.flags = MMC_RSP_R1B | MMC_CMD_AC; + + data.blksz = blksz; + data.blocks = 1; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; + + memset(blk_test, 0, blksz); + sg_init_one(&sg, blk_test, blksz); + + mrq.cmd = &cmd; + mrq.stop = &stop; + mrq.data = &data; + host->mrq = &mrq; + + vclk2 = readl(host->base + SDHC_CLK2); + clk2->rx_clk_phase = rx_phase; + writel(vclk2, host->base + SDHC_CLK2); + pdata->clk2 = vclk2; + + mmc_wait_for_req(mmc, &mrq); + + if (!cmd.error && !data.error) { + if (!memcmp(blk_pattern, blk_test, blksz)) + nmatch++; + else { + sdhc_dbg(AMLSD_DBG_TUNING, + "Tuning pattern mismatch: rx_phase=%d nmatch=%d\n", + rx_phase, nmatch); + } + } else { + sdhc_dbg(AMLSD_DBG_TUNING, "Tuning transfer error:"); + sdhc_dbg(AMLSD_DBG_TUNING, + "rx_phase=%d nmatch=%d cmd.error=%d data.error=%d\n", + rx_phase, nmatch, + cmd.error, data.error); + } + } + + if (rx_phase < sizeof(rx_tuning_result) + /sizeof(rx_tuning_result[0])) + rx_tuning_result[rx_phase] = nmatch; + + if (nmatch == ntries) { + if (rx_phase == 0) + wrap_win_start = rx_phase; + + if (wrap_win_start >= 0) + wrap_win_size++; + + if (curr_win_start < 0) + curr_win_start = rx_phase; + + curr_win_size++; + } else { + if (curr_win_start >= 0) { + if (best_win_start < 0) { + best_win_start = curr_win_start; + best_win_size = curr_win_size; + } else { + if (best_win_size < curr_win_size) { + best_win_start = curr_win_start; + best_win_size = curr_win_size; + } + } + + wrap_win_start = -1; + curr_win_start = -1; + curr_win_size = 0; + } + } + } + sdhc_dbg(AMLSD_DBG_TUNING, "RX Tuning Result\n"); + for (n = 0; n <= clkc->clk_div; n++) { + if (n < ARRAY_SIZE(rx_tuning_result)) + sdhc_dbg(AMLSD_DBG_TUNING, + "RX[%d]=%d\n", n, rx_tuning_result[n]); + } + + sdhc_dbg(AMLSD_DBG_TUNING, "curr_win_start=%d\n", curr_win_start); + sdhc_dbg(AMLSD_DBG_TUNING, "curr_win_size=%d\n", curr_win_size); + sdhc_dbg(AMLSD_DBG_TUNING, "best_win_start=%d\n", best_win_start); + sdhc_dbg(AMLSD_DBG_TUNING, "best_win_size=%d\n", best_win_size); + sdhc_dbg(AMLSD_DBG_TUNING, "wrap_win_start=%d\n", wrap_win_start); + sdhc_dbg(AMLSD_DBG_TUNING, "wrap_win_size=%d\n", wrap_win_size); + if (curr_win_start >= 0) { + if (best_win_start < 0) { + best_win_start = curr_win_start; + best_win_size = curr_win_size; + } else if (wrap_win_size > 0) { + /* Wrap around case */ + if (curr_win_size + wrap_win_size > best_win_size) { + best_win_start = curr_win_start; + best_win_size = curr_win_size + wrap_win_size; + } + } else if (best_win_size < curr_win_size) { + best_win_start = curr_win_start; + best_win_size = curr_win_size; + } + + curr_win_start = -1; + curr_win_size = 0; + } + + if (best_win_start < 0) { + sdhc_err("Tuning failed to find a valid window, using default rx phase\n"); + ret = -EIO; + writel(vclk2_bak, host->base + SDHC_CLK2); + pdata->clk2 = vclk2_bak; + /* rx_phase = rx_clk_phase_set; */ + } else { + pdata->is_tuned = true; + + rx_phase = best_win_start + (best_win_size / 2); + + if (rx_phase > clkc->clk_div) + rx_phase -= (clkc->clk_div + 1); + + vclk2 = readl(host->base + SDHC_CLK2); + clk2->rx_clk_phase = rx_phase; + writel(vclk2, host->base + SDHC_CLK2); + pdata->clk2 = vclk2; + pdata->tune_phase = vclk2; + + sdhc_dbg(AMLSD_DBG_TUNING, + "Tuning result: rx_phase=%d\n", rx_phase); + } + sdhc_dbg(AMLSD_DBG_TUNING, "Final Result\n"); + sdhc_dbg(AMLSD_DBG_TUNING, "curr_win_start=%d\n", curr_win_start); + sdhc_dbg(AMLSD_DBG_TUNING, "curr_win_size=%d\n", curr_win_size); + sdhc_dbg(AMLSD_DBG_TUNING, "best_win_start=%d\n", best_win_start); + sdhc_dbg(AMLSD_DBG_TUNING, "best_win_size=%d\n", best_win_size); + sdhc_dbg(AMLSD_DBG_TUNING, "wrap_win_start=%d\n", wrap_win_start); + sdhc_dbg(AMLSD_DBG_TUNING, "wrap_win_size=%d\n", wrap_win_size); + kfree(blk_test); + + /* do not dynamical tuning for eMMC */ + if ((pdata->is_in) && !aml_card_type_mmc(pdata)) + schedule_delayed_work(&pdata->retuning, 15*HZ); + return ret; +} + +static int aml_sdhc_execute_tuning(struct mmc_host *mmc, u32 opcode) +{ + struct aml_tuning_data tuning_data; + int err = 0; + + if (opcode == MMC_SEND_TUNING_BLOCK_HS200) { + if (mmc->ios.bus_width == MMC_BUS_WIDTH_8) { + tuning_data.blk_pattern = tuning_blk_pattern_8bit; + tuning_data.blksz = sizeof(tuning_blk_pattern_8bit); + } else if (mmc->ios.bus_width == MMC_BUS_WIDTH_4) { + tuning_data.blk_pattern = tuning_blk_pattern_4bit; + tuning_data.blksz = sizeof(tuning_blk_pattern_4bit); + } else + return -EINVAL; + } else if (opcode == MMC_SEND_TUNING_BLOCK) { + tuning_data.blk_pattern = tuning_blk_pattern_4bit; + tuning_data.blksz = sizeof(tuning_blk_pattern_4bit); + } else { + sdhc_err("Undefined command(%d) for tuning\n", opcode); + return -EINVAL; + } + + err = aml_sdhc_execute_tuning_(mmc, opcode, &tuning_data); + + return err; +} + +/*soft reset after errors*/ +void aml_sdhc_host_reset(struct amlsd_host *host) +{ + writel(SDHC_SRST_ALL, host->base+SDHC_SRST); + udelay(5); + + writel(0, host->base+SDHC_SRST); + udelay(10); +} + +static int aml_sdhc_clktree_init(struct amlsd_host *host) +{ + int ret = 0; + + host->core_clk = devm_clk_get(host->dev, "core"); + if (IS_ERR(host->core_clk)) { + ret = PTR_ERR(host->core_clk); + pr_err("devm_clk_get core_clk fail %d\n", ret); + return ret; + } + ret = clk_prepare_enable(host->core_clk); + if (ret) { + pr_err("clk_prepare_enable core_clk fail %d\n", ret); + return ret; + } + + host->div3_clk = devm_clk_get(host->dev, "div3"); + if (IS_ERR(host->div3_clk)) { + ret = PTR_ERR(host->div3_clk); + pr_err("devm_clk_get div3_clk fail %d\n", ret); + return ret; + } + ret = clk_prepare_enable(host->div3_clk); + if (ret) { + pr_err("clk_prepare_enable div3_clk fail %d\n", ret); + return ret; + } + + pr_info("aml_sdhc_clktree_init ok\n"); + return 0; +} + +/*setup reg initial value*/ +static void aml_sdhc_reg_init(struct amlsd_host *host) +{ + struct sdhc_ctrl ctrl = {0}; + /* struct sdhc_pdma pdma = {0}; */ + u32 vpdma = readl(host->base+SDHC_PDMA); + struct sdhc_pdma *pdma = (struct sdhc_pdma *)&vpdma; + struct sdhc_misc misc = {0}; + u32 vclkc; + struct sdhc_clkc *clkc = (struct sdhc_clkc *)&vclkc; + /* struct sdhc_clk2 clk2 = {0}; */ + u32 venhc; + struct sdhc_enhc *enhc = (struct sdhc_enhc *)&venhc; + /* u32 val; */ +#ifdef DMC_URGENT_PERIPH + u32 dmc_ctl; +#endif + /*switch_mod_gate_by_type(MOD_SDHC, 1);*/ + /* print_dbg("HHI_GCLK_MPEG0=%#x\n", READ_CBUS_REG(HHI_GCLK_MPEG0)); */ + + aml_sdhc_host_reset(host); + +#ifdef DMC_URGENT_PERIPH + dmc_ctl = readl(P_DMC_AM7_CHAN_CTRL); + dmc_ctl |= (1<<18); + writel(dmc_ctl, P_DMC_AM7_CHAN_CTRL); +#endif + + aml_sdhc_clktree_init(host); + + ctrl.rx_period = 0xf;/* 0x08; // 0xf; */ + ctrl.rx_timeout = 0x7f;/* 0x40; // 0x7f; */ + + /*R/W endian 7*/ + ctrl.rx_endian = 0x7; + ctrl.tx_endian = 0x7; + writel(*(u32 *)&ctrl, host->base + SDHC_CTRL); + + vclkc = readl(host->base+SDHC_CLKC); + clkc->mem_pwr_off = 0; + writel(vclkc, host->base+SDHC_CLKC); + + pdma->dma_mode = 0; + pdma->dma_urgent = 1; + pdma->wr_burst = 7;/* 3; // means 4 */ + + + pdma->txfifo_th = 49; /* means 49 */ + pdma->rd_burst = 15; /* means 8 */ + pdma->rxfifo_th = 7; /* means 8 */ + writel(vpdma, host->base+SDHC_PDMA); + + /*Send Stop Cmd automatically*/ + misc.txstart_thres = 7; /* [29:31] = 7 */ + + misc.manual_stop = 0; + misc.wcrc_err_patt = 5; + misc.wcrc_ok_patt = 2; + writel(*(u32 *)&misc, host->base + SDHC_MISC); + + venhc = readl(host->base+SDHC_ENHC); + + enhc->reg.meson.rxfifo_th = 63; + enhc->reg.meson.dma_rd_resp = 0; + enhc->reg.meson.dma_wr_resp = 1; + enhc->reg.meson.sdio_irq_period = 12; + enhc->reg.meson.rx_timeout = 255; + + writel(venhc, host->base + SDHC_ENHC); + + /*Disable All Irq*/ + writel(0, host->base + SDHC_ICTL); + + /*Write 1 Clear all irq status*/ + writel(SDHC_ISTA_W1C_ALL, host->base + SDHC_ISTA); +} + +/*wait sdhc controller cmd send*/ +int aml_sdhc_wait_ready(struct amlsd_host *host, u32 timeout) +{ + u32 i, vstat = 0; + struct sdhc_stat *stat; + u32 esta; + + for (i = 0; i < timeout; i++) { + vstat = readl(host->base + SDHC_STAT); + stat = (struct sdhc_stat *)&vstat; + + esta = readl(host->base + SDHC_ESTA); + if (!stat->cmd_busy && (!((esta >> 11) & 7))) + /* if(!stat->cmd_busy) */ + return 0; + udelay(1); + } + + sdhc_err("SDHC_STAT=%#x, sdhc controller is busy.\n", vstat); + /* aml_sdhc_print_reg(host); */ + aml_sdhc_host_reset(host); + /* WARN_ON(1); */ + return -1; +} + +/* + * read response from REG0_ARGU(136bit or 48bit) + */ +int aml_sdhc_read_response(struct mmc_host *mmc, struct mmc_command *cmd) +{ + u32 i = 0, j = 0; + struct amlsd_platform *pdata = mmc_priv(mmc); + struct amlsd_host *host = pdata->host; + u32 vpdma = readl(host->base+SDHC_PDMA); + struct sdhc_pdma *pdma = (struct sdhc_pdma *)&vpdma; + u32 *presp = (u32 *)cmd->resp; + + pdma->dma_mode = 0; + if (cmd->flags & MMC_RSP_136) { /*136 bit*/ + for (i = MMC_RSP_136_NUM, j = 0; i >= 1; i--, j++) { + pdma->pio_rdresp = i; + writel(vpdma, host->base+SDHC_PDMA); + presp[j] = readl(host->base+SDHC_ARGU); + sdhc_dbg(AMLSD_DBG_RESP, + "Cmd %d ,Resp[%d] 0x%x\n", + cmd->opcode, j, presp[j]); + } + } else { /*48 bit*/ + pdma->pio_rdresp = i; + writel(vpdma, host->base+SDHC_PDMA); + presp[0] = readl(host->base+SDHC_ARGU); + sdhc_dbg(AMLSD_DBG_RESP, + "Cmd %d ,Resp 0x%x\n", cmd->opcode, presp[0]); + } + return 0; +} + +/*clear fifo after transfer data*/ +void aml_sdhc_clear_fifo(struct amlsd_host *host) +{ + struct sdhc_srst srst; + + srst.rxfifo = 1; + srst.txfifo = 1; + writel(*(u32 *)&srst, host->base+SDHC_SRST); + udelay(1); +} + +/*enable irq bit in reg SDHC_ICTL*/ +void aml_sdhc_enable_imask(struct amlsd_host *host, u32 irq) +{ + u32 ictl = readl(host->base+SDHC_ICTL); + + ictl |= irq; + writel(ictl, host->base+SDHC_ICTL); +} + +/*disable irq bit in reg SDHC_ICTL*/ +void aml_sdhc_disable_imask(struct amlsd_host *host, u32 irq) +{ + u32 ictl = readl(host->base+SDHC_ICTL); + + ictl &= ~irq; + writel(ictl, host->base+SDHC_ICTL); +} + +void aml_sdhc_set_pdma(struct amlsd_platform *pdata, struct mmc_request *mrq) +{ + struct amlsd_host *host = pdata->host; + u32 vpdma = readl(host->base+SDHC_PDMA); + struct sdhc_pdma *pdma = (struct sdhc_pdma *)&vpdma; + + WARN_ON(!mrq->data); + pdma->dma_mode = 1; + + if (mrq->data->flags & MMC_DATA_WRITE) { + /* self-clear-fill, recommend to write before sd send + * init sets rd_burst to 15 + */ + pdma->rd_burst = 31; + pdma->txfifo_fill = 1; + writel(*(u32 *)pdma, host->base+SDHC_PDMA); + pdma->txfifo_fill = 1; + } else { + if (aml_card_type_sdio(pdata)) + pdma->rxfifo_manual_flush = 0; + else + pdma->rxfifo_manual_flush = 1; + } + + writel(*(u32 *)pdma, host->base+SDHC_PDMA); + if (mrq->data->flags & MMC_DATA_WRITE) { + /* init sets rd_burst to 15 */ + /* change back to 15 after fill */ + pdma->rd_burst = 15; + writel(*(u32 *)pdma, host->base+SDHC_PDMA); + } +} + +/*copy buffer from data->sg to dma buffer, set dma addr to reg*/ +void aml_sdhc_prepare_dma(struct amlsd_host *host, struct mmc_request *mrq) +{ + struct mmc_data *data = mrq->data; + + /* for temp write test */ + if (data->flags & MMC_DATA_WRITE) { + aml_sg_copy_buffer(data->sg, data->sg_len, + host->bn_buf, data->blksz*data->blocks, 1); + sdhc_dbg(AMLSD_DBG_WR_DATA, "W Cmd %d, %x-%x-%x-%x\n", + mrq->cmd->opcode, + host->bn_buf[0], host->bn_buf[1], + host->bn_buf[2], host->bn_buf[3]); + } +} + +/* + * set host->clkc_w for 8bit emmc write cmd as it would fail on TXFIFO EMPTY, + * we decrease the clock for write cmd, and set host->clkc for other cmd + */ +void aml_sdhc_set_clkc(struct amlsd_platform *pdata) +{ + struct amlsd_host *host = pdata->host; + u32 vclkc = readl(host->base + SDHC_CLKC); + u32 clk2 = readl(host->base+SDHC_CLK2); + + if (!host->is_gated && (pdata->clkc == vclkc) && (pdata->clk2 == clk2)) + return; + + if (host->is_gated) { /* if clock is switch off, we need turn on */ + struct sdhc_clkc *clkc = (struct sdhc_clkc *)&(pdata->clkc); + + aml_sdhc_clk_switch(pdata, clkc->clk_div, clkc->clk_src_sel); + } else { + writel(pdata->clkc, host->base+SDHC_CLKC); + } + + writel(pdata->clk2, host->base+SDHC_CLK2); +} + +void aml_sdhc_start_cmd(struct amlsd_platform *pdata, struct mmc_request *mrq) +{ + struct amlsd_host *host = pdata->host; + struct sdhc_send send = {0}; + struct sdhc_ictl ictl = {0}; + u32 vctrl = readl(host->base + SDHC_CTRL); + struct sdhc_ctrl *ctrl = (struct sdhc_ctrl *)&vctrl; + u32 vstat; + struct sdhc_stat *stat = (struct sdhc_stat *)&vstat; + u32 vsrst; + struct sdhc_srst *srst = (struct sdhc_srst *)&vsrst; + u32 vmisc = readl(host->base + SDHC_MISC); + struct sdhc_misc *misc = (struct sdhc_misc *)&vmisc; + int i; + int loop_limit; + u32 vesta; + + /*Set clock for each port, change clock before wait ready*/ + aml_sdhc_set_clkc(pdata); + + /*Set Irq Control*/ + ictl.data_timeout = 1; + ictl.data_err_crc = 1; + ictl.rxfifo_full = 1; + ictl.txfifo_empty = 1; + ictl.resp_timeout = 1; /* try */ + ictl.resp_err_crc = 1; /* try */ + + sdhc_dbg(AMLSD_DBG_REQ, + "%s %d %s: cmd:%d, flags:0x%x, args:0x%x\n", + __func__, __LINE__, + pdata->pinname, mrq->cmd->opcode, + mrq->cmd->flags, mrq->cmd->arg); + if (mrq->data) { + if (((mrq->cmd->opcode == SD_IO_RW_DIRECT) || + (mrq->cmd->opcode == SD_IO_RW_EXTENDED)) + && (mrq->data->blocks > 1)) { + misc->manual_stop = 1; + } else{ + if (mmc_host_cmd23(host->mmc)) + misc->manual_stop = 1; + else + misc->manual_stop = 0; + } + writel(vmisc, host->base + SDHC_MISC); + + vstat = readl(host->base + SDHC_STAT); + if (stat->txfifo_cnt || stat->rxfifo_cnt) { +#ifdef CONFIG_MMC_AML_DEBUG + sdhc_err("cmd%d: txfifo_cnt:%d, rxfifo_cnt:%d\n", + mrq->cmd->opcode, stat->txfifo_cnt, + stat->rxfifo_cnt); +#endif + if (aml_sdhc_wait_ready(host, STAT_POLL_TIMEOUT)) { + /*Wait command busy*/ + sdhc_err("aml_sdhc_wait_ready error before start cmd fifo\n"); + } + vsrst = readl(host->base + SDHC_SRST); + srst->rxfifo = 1; + srst->txfifo = 1; + srst->main_ctrl = 1; + writel(vsrst, host->base+SDHC_SRST); + udelay(5); + writel(vsrst, host->base+SDHC_SRST); + } + vstat = readl(host->base + SDHC_STAT); + if (stat->txfifo_cnt || stat->rxfifo_cnt) { + sdhc_err("FAIL to clear FIFO, cmd%d: txfifo_cnt:%d, rxfifo_cnt:%d\n", + mrq->cmd->opcode, + stat->txfifo_cnt, stat->rxfifo_cnt); + } + + /*Command has data*/ + send.cmd_has_data = 1; + + /*Read/Write data direction*/ + if (mrq->data->flags & MMC_DATA_WRITE) + send.data_dir = 1; + + /*Set package size*/ + if (mrq->data->blksz < 512) + ctrl->pack_len = mrq->data->blksz; + else + ctrl->pack_len = 0; + + /*Set blocks in package*/ + send.total_pack = mrq->data->blocks - 1; + + /* + * If command with no data, just wait response done + * interrupt(int[0]), and if command with data transfer, just + * wait dma done interrupt(int[11]), don't need care about + * dat0 busy or not. + */ + if ((mrq->data->flags & MMC_DATA_WRITE) + || aml_card_type_sdio(pdata)) + ictl.dma_done = 1; /* for hardware automatical flush */ + else + ictl.data_xfer_ok = 1; /* for software flush */ + } else + ictl.resp_ok = 1; + + if (mrq->cmd->opcode == MMC_STOP_TRANSMISSION) + send.data_stop = 1; + + /*Set Bus Width*/ + ctrl->dat_type = pdata->width; + + if (mrq->cmd->flags & MMC_RSP_136) + send.resp_len = 1; + if (mrq->cmd->flags & MMC_RSP_PRESENT) + send.cmd_has_resp = 1; + if (!(mrq->cmd->flags & MMC_RSP_CRC) || mrq->cmd->flags & MMC_RSP_136) + send.resp_no_crc = 1; + + /*Command Index*/ + send.cmd_index = mrq->cmd->opcode; + + writel(*(u32 *)&ictl, host->base+SDHC_ICTL); + + /*Set irq status: write 1 clear*/ + writel(SDHC_ISTA_W1C_ALL, host->base+SDHC_ISTA); + + writel(mrq->cmd->arg, host->base+SDHC_ARGU); + writel(vctrl, host->base+SDHC_CTRL); + writel(host->bn_dma_buf, host->base+SDHC_ADDR); + if (aml_sdhc_wait_ready(host, STAT_POLL_TIMEOUT)) { + /*Wait command busy*/ + sdhc_err("aml_sdhc_wait_ready error before start cmd\n"); + } + if (mrq->data) + aml_sdhc_set_pdma(pdata, mrq);/*Start dma transfer*/ + +#ifdef CONFIG_MMC_AML_DEBUG + if (0) { + memcpy_fromio(host->reg_buf, host->base, 0x3C); + host->reg_buf[SDHC_SEND/4] = *(u32 *)&send; + } +#endif + + loop_limit = 100; + for (i = 0; i < loop_limit; i++) { + vesta = readl(host->base + SDHC_ESTA); + if (vesta == 0) { +#ifdef CONFIG_MMC_AML_DEBUG + sdhc_err("ok: %s: cmd%d, SDHC_ESTA=%#x, i=%d\n", + mmc_hostname(host->mmc), + mrq->cmd->opcode, vesta, i); +#endif + break; + } + if (i > 50) { + sdhc_err("udelay\n"); + udelay(1); + } + } + if (i >= loop_limit) { + sdhc_err("Warning: %s: cmd%d, SDHC_ESTA=%#x\n", + mmc_hostname(host->mmc), + mrq->cmd->opcode, vesta); + } + + if (mrq->data && (mrq->data->flags & MMC_DATA_WRITE)) { + for (i = 0; i < loop_limit; i++) { + vstat = readl(host->base + SDHC_STAT); + if (stat->txfifo_cnt != 0) { +#ifdef CONFIG_MMC_AML_DEBUG + sdhc_err("OK: %s: cmd%d, txfifo_cnt=%d, i=%d\n", + mmc_hostname(host->mmc), + mrq->cmd->opcode, + stat->txfifo_cnt, i); +#endif + break; + } + udelay(1); + } + if (i >= loop_limit) { + sdhc_err("Warning: %s: cmd%d, txfifo_cnt=%d\n", + mmc_hostname(host->mmc), + mrq->cmd->opcode, stat->txfifo_cnt); + } + } + writel(*(u32 *)&send, host->base+SDHC_SEND); /*Command send*/ +} + +/*mmc_request_done & do nothing in xfer_post*/ +void aml_sdhc_request_done(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct amlsd_platform *pdata = mmc_priv(mmc); + struct amlsd_host *host = pdata->host; + unsigned long flags; + + spin_lock_irqsave(&host->mrq_lock, flags); + host->xfer_step = XFER_FINISHED; + host->mrq = NULL; + host->status = HOST_INVALID; + spin_unlock_irqrestore(&host->mrq_lock, flags); + +#ifdef CONFIG_MMC_AML_DEBUG + host->req_cnt--; + + aml_dbg_verify_pinmux(pdata); + aml_dbg_verify_pull_up(pdata); + + if (0) { + sdhc_err("%s: cmd%d, start sdhc reg:\n", + mmc_hostname(host->mmc), mrq->cmd->opcode); + aml_sdhc_print_reg_(host->reg_buf); + sdhc_err("done reg:\n"); + aml_sdhc_print_reg(host); + } +#endif + + if (pdata->xfer_post) + pdata->xfer_post(mmc); + + aml_sdhc_disable_imask(host, SDHC_ICTL_ALL); + /*Set irq status: write 1 clear*/ + writel(SDHC_ISTA_W1C_ALL, host->base+SDHC_ISTA); + if (aml_sdhc_wait_ready(host, STAT_POLL_TIMEOUT)) { + /*Wait command busy*/ + sdhc_err("aml_sdhc_wait_ready request done\n"); + } + aml_sdhc_clk_switch_off(host); + mmc_request_done(host->mmc, mrq); +} + +char *msg_err[] = { + "invalid", /* 0 */ + "rxfifo full", /* 1 */ + "txfifo empty", /* 2 */ + "rsp CRC", /* 3 */ + "data CRC", /* 4 */ + "rsp timeout", /* 5 */ + "data timeout", /* 6 */ +}; + +static void aml_sdhc_print_err(struct amlsd_host *host) +{ + char *msg, *msg_timer = ""; + char *p; + u32 tmp_reg, xfer_step, xfer_step_prev, status; + int left_size; + + int clk18_clk_rate = clk_get_rate(host->core_clk); + struct amlsd_platform *pdata = mmc_priv(host->mmc); + u32 vista = readl(host->base + SDHC_ISTA); + u32 vstat = readl(host->base + SDHC_STAT); + u32 vclkc = readl(host->base+SDHC_CLKC); + struct sdhc_clkc *clkc = (struct sdhc_clkc *)&vclkc; + u32 vctrl = readl(host->base + SDHC_CTRL); + struct sdhc_ctrl *ctrl = (struct sdhc_ctrl *)&vctrl; + u32 clk_rate; + unsigned long flags; + + if ((host->mrq->cmd->opcode == MMC_SEND_TUNING_BLOCK) + || (host->mrq->cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200)) + /* not print err msg for tuning cmd */ + return; + + spin_lock_irqsave(&host->mrq_lock, flags); + xfer_step = host->xfer_step; + xfer_step_prev = host->xfer_step_prev; + status = host->status; + spin_unlock_irqrestore(&host->mrq_lock, flags); + + clk_rate = clk_get_rate(host->div3_clk); // for SDHC_CLOCK_SRC_FCLK_DIV3 + + p = host->msg_buf; + left_size = MESSAGE_BUF_SIZE; + + if (((status < HOST_ERR_END) && (status > HOST_INVALID)) + && (status < ARRAY_SIZE(msg_err))) /* valid sdhc errors */ + msg = msg_err[status]; + else + msg = "status is invalid"; + + if (xfer_step == XFER_TIMER_TIMEOUT) { /* by aml_sdhc_timeout() */ + if (((status < HOST_ERR_END) && (status > HOST_INVALID)) + && (status < ARRAY_SIZE(msg_err))) + /* valid sdhc errors */ + msg_timer = "timer timeout WITH sdhc "; + else { + msg_timer = "timer timeout"; + msg = ""; + } + } + + aml_snprint(&p, &left_size, "%s: %s%s error, port=%d, Cmd%d Arg %08x, ", + mmc_hostname(host->mmc), + msg_timer, + msg, + pdata->port, + host->mrq->cmd->opcode, + host->mrq->cmd->arg); + aml_snprint(&p, &left_size, "xfer_step=%d, status=%d, cmd25=%d, ", + xfer_step, + status, + cmd25_cnt); + aml_snprint(&p, &left_size, "fifo_empty=%d, fifo_full=%d, timeout=%d, ", + fifo_empty_cnt, + fifo_full_cnt, + timeout_cnt); + + switch (status) { + case HOST_RX_FIFO_FULL: + case HOST_TX_FIFO_EMPTY: + aml_snprint(&p, &left_size, "clk81=%d, clock=%d, ", + clk18_clk_rate, + clk_rate/(clkc->clk_div+1)); + break; + } + + if (xfer_step == XFER_TIMER_TIMEOUT) { + /* by aml_sdhc_timeout() */ + aml_snprint(&p, &left_size, + "xfer_step_prev=%d, ", xfer_step_prev); + } + + if (host->mrq->data) { + int byte = host->mrq->data->blksz*host->mrq->data->blocks; + + aml_snprint(&p, &left_size, "Xfer %d Bytes, ", byte); + if (byte > 512) /* multi-block mode */ + aml_snprint(&p, &left_size, "SEND=%#x, ", + readl(host->base+SDHC_SEND)); + + if (pdata->width != ctrl->dat_type) { + /* error: bus width is different */ + aml_snprint(&p, &left_size, + "pdata->width=%d, ctrl->dat_type=%d, ", + pdata->width, ctrl->dat_type); + } + + if (pdata->clkc != vclkc) { + /* error: clock setting is different */ + aml_snprint(&p, &left_size, + "pdata->clkc=%d, vclkc=%#x, ", + pdata->clkc, vclkc); + } + } + aml_snprint(&p, &left_size, "iSTA=%#x, STAT=%#x", vista, vstat); + sdhc_err("%s\n", host->msg_buf); + + tmp_reg = aml_read_cbus(HHI_GCLK_MPEG0); + if (!(tmp_reg & 0x00004000)) { + sdhc_err( + "Error: SDHC is gated clock,HHI_GCLK_MPEG0=%#x, bit14 is 0\n", + tmp_reg); + } + /* aml_dbg_print_pinmux();*/ + +#ifdef CONFIG_MMC_AML_DEBUG + if (xfer_step == XFER_TIMER_TIMEOUT) { /* by aml_sdhc_timeout() */ + sdhc_err("old sdhc reg:\n"); + aml_sdhc_print_reg_(host->reg_buf); + } +#endif + /* aml_sdhc_print_reg(host);*/ +} + +/*error handler*/ +static void aml_sdhc_timeout(struct work_struct *work) +{ + static int timeout_cnt; + struct amlsd_host *host = + container_of(work, struct amlsd_host, timeout.work); + struct mmc_request *mrq; + struct amlsd_platform *pdata = mmc_priv(host->mmc); + unsigned long flags; + /* struct timeval ts_current; */ + unsigned long time_start_cnt = aml_read_cbus(ISA_TIMERE); + + time_start_cnt = (time_start_cnt - host->time_req_sta) / 1000; + + WARN_ON(!host->mrq || !host->mrq->cmd); + + spin_lock_irqsave(&host->mrq_lock, flags); + if (host->xfer_step == XFER_FINISHED) { + spin_unlock_irqrestore(&host->mrq_lock, flags); + sdhc_err("timeout after xfer finished\n"); + return; + } + + if ((host->xfer_step == XFER_IRQ_TASKLET_DATA) + || (host->xfer_step == XFER_IRQ_TASKLET_CMD)) { + schedule_delayed_work(&host->timeout, 50); + host->time_req_sta = aml_read_cbus(ISA_TIMERE); + + timeout_cnt++; + if (timeout_cnt > 30) + goto timeout_handle; + + spin_unlock_irqrestore(&host->mrq_lock, flags); + sdhc_err( + "%s:cmd%d, xfer_step=%d,time_start_cnt=%ldmS,timeout_cnt=%d\n", + mmc_hostname(host->mmc), + host->mrq->cmd->opcode, host->xfer_step, + time_start_cnt, timeout_cnt); + up(&sdhc_sema); + return; + } +timeout_handle: + timeout_cnt = 0; + + mrq = host->mrq; + host->xfer_step_prev = host->xfer_step; + host->xfer_step = XFER_TIMER_TIMEOUT; + mrq->cmd->error = -ETIMEDOUT; + + /* do not retry for sdcard & sdio wifi */ + if (!aml_card_type_mmc(pdata)) { + sdhc_error_flag = 0; + mrq->cmd->retries = 0; + } else if (((sdhc_error_flag & (1<<3)) == 0) + && (mrq->data != NULL) + && pdata->is_in) { + /* set cmd retry cnt when first error. */ + sdhc_error_flag |= (1<<3); + mrq->cmd->retries = AML_TIMEOUT_RETRY_COUNTER; + } + + if (sdhc_error_flag && (mrq->cmd->retries == 0)) { + sdhc_error_flag |= (1<<30); + sdhc_err("Command retried failed\n"); + } + + aml_sdhc_status(host); + + spin_unlock_irqrestore(&host->mrq_lock, flags); + aml_sdhc_read_response(host->mmc, mrq->cmd); + sdhc_err("time_start_cnt:%ld\n", time_start_cnt); + + aml_sdhc_print_err(host); + + aml_sdhc_host_reset(host); + + /* do not send stop for sdio wifi case */ + if (host->mrq->stop && aml_card_type_mmc(pdata) + && !host->cmd_is_stop + && (host->mrq->cmd->opcode != MMC_SEND_TUNING_BLOCK) + && (host->mrq->cmd->opcode != + MMC_SEND_TUNING_BLOCK_HS200)) { + aml_sdhc_send_stop(host); + } else{ + spin_lock_irqsave(&host->mrq_lock, flags); + if (host->cmd_is_stop) + host->cmd_is_stop = 0; + spin_unlock_irqrestore(&host->mrq_lock, flags); + + aml_sdhc_request_done(host->mmc, mrq); + } +} + +static void aml_sdhc_tuning_timer(struct work_struct *work) +{ + struct amlsd_platform *pdata = + container_of(work, struct amlsd_platform, retuning.work); + struct amlsd_host *host = (void *)pdata->host; + unsigned long flags; + + spin_lock_irqsave(&host->mrq_lock, flags); + pdata->need_retuning = true; + spin_unlock_irqrestore(&host->mrq_lock, flags); +} + +/*cmd request interface*/ +void aml_sdhc_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct amlsd_platform *pdata; + struct amlsd_host *host; + /* u32 vista; */ + unsigned long flags; + unsigned int timeout; + u32 tuning_opcode; + + WARN_ON(!mmc); + WARN_ON(!mrq); + + pdata = mmc_priv(mmc); + host = (void *)pdata->host; + + if (aml_check_unsupport_cmd(mmc, mrq)) + return; + aml_sdhc_emmc_clock_switch_on(pdata); + /* only for SDCARD */ + if (!pdata->is_in || (!host->init_flag && aml_card_type_sd(pdata))) { + spin_lock_irqsave(&host->mrq_lock, flags); + mrq->cmd->error = -ENOMEDIUM; + mrq->cmd->retries = 0; + spin_unlock_irqrestore(&host->mrq_lock, flags); + mmc_request_done(mmc, mrq); + return; + } + + if (pdata->need_retuning && mmc->card) { + /* eMMC uses cmd21 but sd and sdio use cmd19 */ + tuning_opcode = (mmc->card->type == MMC_TYPE_MMC) ? + MMC_SEND_TUNING_BLOCK_HS200 : MMC_SEND_TUNING_BLOCK; + aml_sdhc_execute_tuning(mmc, tuning_opcode); + } + + aml_sdhc_disable_imask(host, SDHC_ICTL_ALL); + + sdhc_dbg(AMLSD_DBG_REQ, "%s: starting CMD%u arg %08x flags %08x\n", + mmc_hostname(mmc), mrq->cmd->opcode, + mrq->cmd->arg, mrq->cmd->flags); + +#ifdef CONFIG_AML_MMC_DEBUG_FORCE_SINGLE_BLOCK_RW + if ((mrq->cmd->opcode == 18) || + (mrq->cmd->opcode == 25)) { /* for debug */ + sdhc_err("cmd%d\n", mrq->cmd->opcode); + } +#endif + + if (mrq->cmd->opcode == 0) { + cmd25_cnt = 0; + fifo_empty_cnt = 0; + fifo_full_cnt = 0; + timeout_cnt = 0; + host->init_flag = 1; + } + + if (mrq->cmd->opcode == 25) + cmd25_cnt++; + + /* clear error flag if last command retried failed here */ + if (sdhc_error_flag & (1<<30)) + sdhc_error_flag = 0; + + /*setup reg especially for cmd with transferring data*/ + if (mrq->data) { + /*Copy data to dma buffer for write request*/ + aml_sdhc_prepare_dma(host, mrq); + + sdhc_dbg(AMLSD_DBG_REQ, + "%s: blksz %d blocks %d flags %08x tsac %d ms nsac %d\n", + mmc_hostname(mmc), mrq->data->blksz, + mrq->data->blocks, mrq->data->flags, + mrq->data->timeout_ns / 1000000, + mrq->data->timeout_clks); + } + + /*clear pinmux & set pinmux*/ + if (pdata->xfer_pre) + pdata->xfer_pre(mmc); + +#ifdef CONFIG_MMC_AML_DEBUG + aml_dbg_verify_pull_up(pdata); + aml_dbg_verify_pinmux(pdata); +#endif + + if (!mrq->data) + timeout = 100; /* 1s */ + else + timeout = 500; + + if (mrq->cmd->opcode == MMC_SEND_STATUS) + timeout = 300; + + /* about 30S for erase cmd. */ + if (mrq->cmd->opcode == MMC_ERASE) + timeout = 3000; + schedule_delayed_work(&host->timeout, timeout); + + spin_lock_irqsave(&host->mrq_lock, flags); + if (host->xfer_step != XFER_FINISHED && host->xfer_step != XFER_INIT) + sdhc_err("host->xfer_step %d\n", host->xfer_step); + + /*host->mrq, used in irq & tasklet*/ + host->mrq = mrq; + host->mmc = mmc; + host->xfer_step = XFER_START; + host->opcode = mrq->cmd->opcode; + host->arg = mrq->cmd->arg; + host->time_req_sta = aml_read_cbus(ISA_TIMERE); + + /*setup reg for all cmd*/ + aml_sdhc_start_cmd(pdata, mrq); + host->xfer_step = XFER_AFTER_START; + spin_unlock_irqrestore(&host->mrq_lock, flags); +} + +static int aml_sdhc_status(struct amlsd_host *host) +{ + int ret = -1; + u32 victl = readl(host->base + SDHC_ICTL); + u32 vista = readl(host->base + SDHC_ISTA); + struct sdhc_ista *ista = (struct sdhc_ista *)&vista; + struct mmc_request *mrq = host->mrq; + + if (!mrq) { + sdhc_err("NULL mrq\n"); + return ret; + } + + if (victl & vista) { + if (ista->rxfifo_full) { + host->status = HOST_RX_FIFO_FULL; + goto _status_exit; + } + if (ista->txfifo_empty) { + host->status = HOST_TX_FIFO_EMPTY; + goto _status_exit; + } + if (ista->resp_err_crc) { + host->status = HOST_RSP_CRC_ERR; + goto _status_exit; + } + if (ista->data_err_crc) { + host->status = HOST_DAT_CRC_ERR; + goto _status_exit; + } + if (ista->resp_timeout) { + host->status = HOST_RSP_TIMEOUT_ERR; + goto _status_exit; + } + if (ista->data_timeout) { + host->status = HOST_DAT_TIMEOUT_ERR; + goto _status_exit; + } + if (ista->dma_done) { + host->status = HOST_TASKLET_DATA; + ret = 0; /* ok */ + goto _status_exit; + } + if (ista->data_xfer_ok) { + host->status = HOST_TASKLET_DATA; + ret = 0; /* ok */ + goto _status_exit; + } + if (ista->resp_ok_noclear) { + host->status = HOST_TASKLET_CMD; + ret = 0; /* ok */ + goto _status_exit; + } + } + ret = 0; /* ok */ +_status_exit: + return ret; +} + +/*sdhc controller irq*/ +static irqreturn_t aml_sdhc_irq(int irq, void *dev_id) +{ + struct amlsd_host *host = dev_id; + struct mmc_host *mmc; + struct amlsd_platform *pdata; + struct mmc_request *mrq; + unsigned long flags; + bool exception_flag = false; + u32 victl; + u32 vista; + + spin_lock_irqsave(&host->mrq_lock, flags); + victl = readl(host->base + SDHC_ICTL); + vista = readl(host->base + SDHC_ISTA); + + mrq = host->mrq; + mmc = host->mmc; + pdata = mmc_priv(mmc); + + sdhc_dbg(AMLSD_DBG_IRQ, + "%s %d %s occurred, vstat:0x%x\n", + __func__, __LINE__, pdata->pinname, vista); + if (!mrq) { + sdhc_err("NULL mrq in aml_sdhc_irq step %d\n", host->xfer_step); + if (host->xfer_step == XFER_FINISHED || + host->xfer_step == XFER_TIMER_TIMEOUT){ + spin_unlock_irqrestore(&host->mrq_lock, flags); + return IRQ_HANDLED; + } + WARN_ON(!mrq); + /* aml_sdhc_print_reg(host);*/ + spin_unlock_irqrestore(&host->mrq_lock, flags); + return IRQ_HANDLED; + } + + if ((host->xfer_step != XFER_AFTER_START) && (!host->cmd_is_stop)) { + sdhc_err("host->xfer_step=%d\n", host->xfer_step); + exception_flag = true; + } + + if (host->cmd_is_stop) + host->xfer_step = XFER_IRQ_TASKLET_BUSY; + else + host->xfer_step = XFER_IRQ_OCCUR; + + if (victl & vista) { + aml_sdhc_status(host); + if (exception_flag) + sdhc_err("victl=%#x, vista=%#x,status=%#x\n", + victl, vista, host->status); + switch (host->status) { + case HOST_RX_FIFO_FULL: + mrq->cmd->error = -HOST_RX_FIFO_FULL; + fifo_full_cnt++; + break; + case HOST_TX_FIFO_EMPTY: + mrq->cmd->error = -HOST_TX_FIFO_EMPTY; + fifo_empty_cnt++; + break; + case HOST_RSP_CRC_ERR: + case HOST_DAT_CRC_ERR: + mrq->cmd->error = -EILSEQ; + break; + case HOST_RSP_TIMEOUT_ERR: + case HOST_DAT_TIMEOUT_ERR: + if (!host->cmd_is_stop) + timeout_cnt++; + mrq->cmd->error = -ETIMEDOUT; + break; + case HOST_TASKLET_DATA: + case HOST_TASKLET_CMD: + writel(vista, host->base+SDHC_ISTA); + break; + default: + sdhc_err( + "Unknown irq status, victl=%#x, vista=%#x,status=%#x\n", + victl, vista, host->status); + break; + } + + spin_unlock_irqrestore(&host->mrq_lock, flags); + return IRQ_WAKE_THREAD; + + } + host->xfer_step = XFER_IRQ_UNKNOWN_IRQ; + sdhc_err( + "%s Unknown Irq Ictl 0x%x, Ista 0x%x, cmd %d,xfer %d bytes\n", + pdata->pinname, victl, vista, mrq->cmd->opcode, + mrq->data?mrq->data->blksz*mrq->data->blocks:0); + spin_unlock_irqrestore(&host->mrq_lock, flags); + return IRQ_HANDLED; +} + +static void aml_sdhc_com_err_handler (struct amlsd_host *host) +{ + if (delayed_work_pending(&host->timeout)) + cancel_delayed_work_sync(&host->timeout); + aml_sdhc_read_response(host->mmc, host->mrq->cmd); + aml_sdhc_print_err(host); + aml_sdhc_host_reset(host); + aml_sdhc_request_done(host->mmc, host->mrq); +} + +static void aml_sdhc_not_timeout_err_handler (struct amlsd_host *host) +{ + if (aml_sdhc_wait_ready(host, (STAT_POLL_TIMEOUT<<2))) { + /*Wait command busy*/ + sdhc_err("aml_sdhc_wait_ready error not timeout error handler\n"); + } + aml_sdhc_com_err_handler(host); +} + +struct mmc_command aml_sdhc_cmd = { + .opcode = MMC_STOP_TRANSMISSION, + .flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC, +}; +struct mmc_request aml_sdhc_stop = { + .cmd = &aml_sdhc_cmd, +}; + +static void aml_sdhc_send_stop(struct amlsd_host *host) +{ + struct amlsd_platform *pdata = mmc_priv(host->mmc); + unsigned long flags; + + /*Already in mrq_lock*/ + schedule_delayed_work(&host->timeout, 50); + spin_lock_irqsave(&host->mrq_lock, flags); + sdhc_err_bak = host->mrq->cmd->error; + host->mrq->cmd->error = 0; + host->cmd_is_stop = 1; + aml_sdhc_start_cmd(pdata, &aml_sdhc_stop); + spin_unlock_irqrestore(&host->mrq_lock, flags); +} + +static unsigned int clock[] = {90000000, + 80000000, 75000000, 70000000, 65000000, 60000000, 50000000}; +static void aml_sdhc_set_clk_rate(struct mmc_host *mmc, unsigned int clk_ios); +irqreturn_t aml_sdhc_data_thread(int irq, void *data) +{ + struct amlsd_host *host = data; + u32 xfer_bytes; + struct mmc_request *mrq; + enum aml_mmc_waitfor xfer_step; + unsigned long flags; + u32 vstat, status; + struct sdhc_stat *stat = (struct sdhc_stat *)&vstat; + struct amlsd_platform *pdata = mmc_priv(host->mmc); + int cnt = 0; + + u32 esta = readl(host->base + SDHC_ESTA); + int i; + /* u32 dmc_sts = 0;*/ + u32 vpdma = readl(host->base+SDHC_PDMA); + struct sdhc_pdma *pdma = (struct sdhc_pdma *)&vpdma; + + spin_lock_irqsave(&host->mrq_lock, flags); + mrq = host->mrq; + xfer_step = host->xfer_step; + status = host->status; + + if ((xfer_step == XFER_FINISHED) || (xfer_step == XFER_TIMER_TIMEOUT)) { + sdhc_err( + "Warning: xfer_step=%d,host->status=%d\n", + xfer_step, status); + spin_unlock_irqrestore(&host->mrq_lock, flags); + return IRQ_HANDLED; + } + + WARN_ON((host->xfer_step != XFER_IRQ_OCCUR) + && (host->xfer_step != XFER_IRQ_TASKLET_BUSY)); + + if (!mrq) { + sdhc_err("!mrq xfer_step %d\n", xfer_step); + if (xfer_step == XFER_FINISHED || + xfer_step == XFER_TIMER_TIMEOUT){ + spin_unlock_irqrestore(&host->mrq_lock, flags); + return IRQ_HANDLED; + } + /* BUG(); */ + aml_sdhc_print_err(host); + } + if (host->cmd_is_stop) { + int delay = 1; + + if (mrq->cmd->error) + sdhc_err("cmd12 error %d\n", mrq->cmd->error); + host->cmd_is_stop = 0; + mrq->cmd->error = sdhc_err_bak; + spin_unlock_irqrestore(&host->mrq_lock, flags); + if (delayed_work_pending(&host->timeout)) + cancel_delayed_work_sync(&host->timeout); + msleep(delay); + sdhc_err("delay %dms\n", delay); + aml_sdhc_request_done(host->mmc, host->mrq); + return IRQ_HANDLED; + } + spin_unlock_irqrestore(&host->mrq_lock, flags); + + WARN_ON(!host->mrq->cmd); + switch (status) { + case HOST_TASKLET_DATA: + sdhc_error_flag = 0; + WARN_ON(!mrq->data); + if (delayed_work_pending(&host->timeout)) + cancel_delayed_work_sync(&host->timeout); + + xfer_bytes = mrq->data->blksz*mrq->data->blocks; + /* copy buffer from dma to data->sg in read cmd*/ + if (host->mrq->data->flags & MMC_DATA_READ) { + if (!aml_card_type_sdio(pdata)) { + for (i = 0; i < STAT_POLL_TIMEOUT; i++) { + + esta = readl(host->base + SDHC_ESTA); + esta = readl(host->base + SDHC_ESTA); + /* read twice, we just + * focus on the second result + */ + /* REGC_ESTA[13:11]=0? then OK */ + if (((esta >> 11) & 0x7) == 0) + break; + else if (i == 10) + sdhc_err("SDHC_ESTA=0x%x\n", + esta); + } + + if (i == STAT_POLL_TIMEOUT) { /* error */ + sdhc_err("Warning:DMA state is wrong!"); + sdhc_err(" SDHC_ESTA=0x%x\n", esta); + } + + pdma->rxfifo_manual_flush |= 0x02; + /* bit[30] */ + writel(vpdma, host->base+SDHC_PDMA); + /* check ddr dma status after + * controller dma status OK + */ +#if 0 + for (i = 0; i < STAT_POLL_TIMEOUT; i++) { + dmc_sts = + aml_read_reg32(P_DMC_CHAN_STS); + dmc_sts = (dmc_sts >> 15)&1; + if (dmc_sts) + break; + else if (i == 10) + sdhc_err("SDHC_ESTA=0x%x\n", + esta); + } + + if (i == STAT_POLL_TIMEOUT) /* error */ + sdhc_err( + "DMA wrong!SDHC_ESTA=0x%x dmc_sts:%d\n", + esta, dmc_sts); +#endif + } + aml_sg_copy_buffer(mrq->data->sg, + mrq->data->sg_len, + host->bn_buf, xfer_bytes, 0); + sdhc_dbg(AMLSD_DBG_RD_DATA, "R Cmd%d, arg %x, size=%d\n", + mrq->cmd->opcode, + mrq->cmd->arg, xfer_bytes); + sdhc_dbg(AMLSD_DBG_RD_DATA, "R Cmd %d, %x-%x-%x-%x-%x-%x-%x-%x\n", + host->mrq->cmd->opcode, + host->bn_buf[0], host->bn_buf[1], + host->bn_buf[2], host->bn_buf[3], + host->bn_buf[4], host->bn_buf[5], + host->bn_buf[6], host->bn_buf[7]); + /* aml_debug_print_buf(host->bn_buf, xfer_bytes); */ + } + + vstat = readl(host->base + SDHC_STAT); + if (stat->rxfifo_cnt) { + sdhc_err("cmd%d, rxfifo_cnt=%d\n", + mrq->cmd->opcode, stat->rxfifo_cnt); + } + + spin_lock_irqsave(&host->mrq_lock, flags); + mrq->cmd->error = 0; + mrq->data->bytes_xfered = xfer_bytes; + host->xfer_step = XFER_TASKLET_DATA; + spin_unlock_irqrestore(&host->mrq_lock, flags); + /* do not check device ready status here */ + if (aml_sdhc_wait_ready(host, + (STAT_POLL_TIMEOUT<<2))) { /*Wait command busy*/ + sdhc_err("aml_sdhc_wait_ready error after data thread\n"); + } + aml_sdhc_read_response(host->mmc, mrq->cmd); + aml_sdhc_request_done(host->mmc, mrq); + break; + case HOST_TASKLET_CMD: + sdhc_error_flag = 0; + if (!host->mrq->data) { + if (delayed_work_pending(&host->timeout)) + cancel_delayed_work_sync(&host->timeout); + spin_lock_irqsave(&host->mrq_lock, flags); + host->mrq->cmd->error = 0; + host->xfer_step = XFER_TASKLET_CMD; + spin_unlock_irqrestore(&host->mrq_lock, flags); + if (aml_sdhc_wait_ready(host, STAT_POLL_TIMEOUT)) { + /*Wait command busy*/ + sdhc_err("aml_sdhc_wait_ready error cmd thread\n"); + } + aml_sdhc_read_response(host->mmc, host->mrq->cmd); + aml_sdhc_request_done(host->mmc, mrq); + } else { + sdhc_err( + "xfer_step is HOST_TASKLET_CMD, while host->mrq->data is not NULL\n"); + } + break; + case HOST_TX_FIFO_EMPTY: + case HOST_RX_FIFO_FULL: + case HOST_RSP_TIMEOUT_ERR: + case HOST_DAT_TIMEOUT_ERR: + if (delayed_work_pending(&host->timeout)) + cancel_delayed_work_sync(&host->timeout); + if (aml_sdhc_wait_ready(host, + (STAT_POLL_TIMEOUT<<2))) { /*Wait command busy*/ + sdhc_err("aml_sdhc_wait_ready error fifo or timeout thread\n"); + } + aml_sdhc_read_response(host->mmc, host->mrq->cmd); + aml_sdhc_print_err(host); + aml_sdhc_host_reset(host); + writel(SDHC_ISTA_W1C_ALL, host->base+SDHC_ISTA); + spin_lock_irqsave(&host->mrq_lock, flags); + if ((sdhc_error_flag == 0) && + (host->mrq->cmd->opcode + != MMC_SEND_TUNING_BLOCK) + && (host->mrq->cmd->opcode + != MMC_SEND_TUNING_BLOCK_HS200) + && host->mrq->data){ + /* set cmd retry cnt when first error. */ + sdhc_error_flag |= (1<<0); + if ((status == HOST_RSP_TIMEOUT_ERR) || + (status == HOST_DAT_TIMEOUT_ERR)) { + if (aml_card_type_mmc(pdata)) + mrq->cmd->retries + = AML_TIMEOUT_RETRY_COUNTER; + else { + sdhc_error_flag = 0; + mrq->cmd->retries = 0; + } + } else + mrq->cmd->retries = AML_ERROR_RETRY_COUNTER; + } + + if (sdhc_error_flag && (mrq->cmd->retries == 0)) { + sdhc_error_flag |= (1<<30); + sdhc_err( + "Command retried failed line:%d, status:%d\n", + __LINE__, status); + } + spin_unlock_irqrestore(&host->mrq_lock, flags); + + /* do not send stop for sdio wifi case */ + if (host->mrq->stop && aml_card_type_mmc(pdata) + && pdata->is_in + && (host->mrq->cmd->opcode != + MMC_SEND_TUNING_BLOCK) + && (host->mrq->cmd->opcode + != MMC_SEND_TUNING_BLOCK_HS200)) + aml_sdhc_send_stop(host); + else + aml_sdhc_request_done(host->mmc, mrq); + break; + case HOST_RSP_CRC_ERR: + case HOST_DAT_CRC_ERR: + pdata = mmc_priv(host->mmc); + if (aml_card_type_sdio(pdata) /* sdio_wifi */ + && (host->mrq->cmd->opcode + != MMC_SEND_TUNING_BLOCK) + && (host->mrq->cmd->opcode + != MMC_SEND_TUNING_BLOCK_HS200)) { + sdhc_err("host->mmc->ios.clock:%d\n", + host->mmc->ios.clock); + while (host->mmc->ios.clock <= clock[cnt]) { + cnt++; + if (cnt >= (ARRAY_SIZE(clock) - 1)) + break; + } + spin_lock_irqsave(&host->mrq_lock, flags); + + host->mmc->ios.clock = clock[cnt]; + pdata->need_retuning = true; + /* retuing will be done in the next request */ + mrq->cmd->retries = (ARRAY_SIZE(clock) - 1) - cnt; + spin_unlock_irqrestore(&host->mrq_lock, flags); + aml_sdhc_set_clk_rate(host->mmc, host->mmc->ios.clock); + } else if (aml_card_type_mmc(pdata) + && (host->mrq->cmd->opcode + != MMC_SEND_TUNING_BLOCK) + && (host->mrq->cmd->opcode + != MMC_SEND_TUNING_BLOCK_HS200)) { + spin_lock_irqsave(&host->mrq_lock, flags); + + if (sdhc_error_flag == 0) { + /* set cmd retry cnt when first error. */ + sdhc_error_flag |= (1<<1); + mrq->cmd->retries = AML_ERROR_RETRY_COUNTER; + } + spin_unlock_irqrestore(&host->mrq_lock, flags); + } + if (sdhc_error_flag && (mrq->cmd->retries == 0)) { + sdhc_error_flag |= (1<<30); + /* sdhc_err("Command retried failed\n"); */ + } + + aml_sdhc_not_timeout_err_handler(host); + break; + default: + sdhc_err("BUG xfer_step=%d, host->status=%d\n", + xfer_step, status); + aml_sdhc_print_err(host); + break; + } + + return IRQ_HANDLED; +} + +static void aml_sdhc_clk_switch_off(struct amlsd_host *host) +{ + u32 vclkc = readl(host->base+SDHC_CLKC); + struct sdhc_clkc *clkc = (struct sdhc_clkc *)&vclkc; + + if (host->is_gated) { + /*sdhc_err("direct return\n");*/ + return; + } + + /*Turn off Clock*/ + clkc->tx_clk_on = 0; + clkc->rx_clk_on = 0; + clkc->sd_clk_on = 0; + writel(vclkc, host->base+SDHC_CLKC); + clkc->mod_clk_on = 0; + writel(vclkc, host->base+SDHC_CLKC); + + host->is_gated = true; + /*sdhc_err("clock off\n");*/ +} + +static void aml_sdhc_emmc_clock_switch_on(struct amlsd_platform *pdata) +{ + struct amlsd_host *host = (void *)pdata->host; + u32 vclkc = readl(host->base+SDHC_CLKC); + struct sdhc_clkc *clkc = (struct sdhc_clkc *)&vclkc; + + /*Turn on Clock*/ + clkc->mod_clk_on = 1; + writel(vclkc, host->base+SDHC_CLKC); + + clkc->tx_clk_on = 1; + clkc->rx_clk_on = 1; + clkc->sd_clk_on = 1; + writel(vclkc, host->base+SDHC_CLKC); + + host->is_gated = false; +} + +static void aml_sdhc_clk_switch_on(struct amlsd_platform *pdata, + int clk_div, int clk_src_sel) +{ + struct amlsd_host *host = (void *)pdata->host; + u32 vclkc = readl(host->base+SDHC_CLKC); + struct sdhc_clkc *clkc = (struct sdhc_clkc *)&vclkc; + + /*Set clock divide*/ + clkc->clk_div = clk_div; + clkc->clk_src_sel = clk_src_sel; + + writel(vclkc, host->base+SDHC_CLKC); + + /*Turn on Clock*/ + clkc->mod_clk_on = 1; + writel(vclkc, host->base+SDHC_CLKC); + + clkc->tx_clk_on = 1; + clkc->rx_clk_on = 1; + clkc->sd_clk_on = 1; + writel(vclkc, host->base+SDHC_CLKC); + + host->is_gated = false; +} + +static void aml_sdhc_clk_switch(struct amlsd_platform *pdata, + int clk_div, int clk_src_sel) +{ + struct amlsd_host *host = (void *)pdata->host; + u32 vclkc = readl(host->base + SDHC_CLKC); + struct sdhc_clkc *clkc = (struct sdhc_clkc *)&vclkc; + + if (!host->is_gated && (clkc->clk_div == clk_div) + && (clkc->clk_src_sel == clk_src_sel)) { + /*sdhc_err("direct return\n");*/ + return; /* if the same, return directly */ + } + + aml_sdhc_clk_switch_off(host); + /* mdelay(1); */ + aml_sdhc_clk_switch_on(pdata, clk_div, clk_src_sel); +} + +/* + * 1. clock valid range + * 2. clk config enable + * 3. select clock source + * 4. set clock divide + */ +static void aml_sdhc_set_clk_rate(struct mmc_host *mmc, unsigned int clk_ios) +{ + u32 clk_rate, clk_div, clk_src_sel, clk_src_div; + unsigned long flags; + struct amlsd_platform *pdata = mmc_priv(mmc); + struct amlsd_host *host = (void *)pdata->host; + u32 vclk2; + struct sdhc_clk2 *clk2 = (struct sdhc_clk2 *)&vclk2; + + if (clk_ios == 0) { + aml_sdhc_clk_switch_off(host); + return; + } + + if ((clk_ios > 100000000) && (val1 > 100000000)) /* for debug, 100M */ + clk_ios = val1; + clk_src_div = -1; + clk_src_sel = SDHC_CLOCK_SRC_FCLK_DIV3; + switch (clk_src_sel) { + case SDHC_CLOCK_SRC_FCLK_DIV3: + clk_src_div = 3; + break; + case SDHC_CLOCK_SRC_FCLK_DIV4: + clk_src_div = 4; + break; + case SDHC_CLOCK_SRC_FCLK_DIV5: + clk_src_div = 5; + break; + case SDHC_CLOCK_SRC_OSC: + clk_src_div = 0; + break; + default: + sdhc_err("Clock source error: %d\n", clk_src_sel); + return; /* clk_src_div = -1; */ + } + + if (clk_src_sel != SDHC_CLOCK_SRC_OSC) + clk_rate = clk_get_rate(host->div3_clk); /* 850000000 */ + else /* OSC, 24MHz */ + clk_rate = 24000000; + + spin_lock_irqsave(&host->mrq_lock, flags); + + if (clk_ios > pdata->f_max) + clk_ios = pdata->f_max; + if (clk_ios < pdata->f_min) + clk_ios = pdata->f_min; + + /*0: dont set it, 1:div2, 2:div3, 3:div4...*/ + clk_div = clk_rate / clk_ios - !(clk_rate%clk_ios); + if (!(clk_div & 0x01)) /* if even number, turn it to an odd one */ + clk_div++; + + aml_sdhc_clk_switch(pdata, clk_div, clk_src_sel); + pdata->clkc = readl(host->base+SDHC_CLKC); + + pdata->mmc->actual_clock = clk_rate / (clk_div + 1); + + vclk2 = readl(host->base+SDHC_CLK2); + clk2->sd_clk_phase = 1; /* 1 */ + if (pdata->mmc->actual_clock > 100000000) { /* if > 100M */ + clk2->rx_clk_phase = rx_clk_phase_set; + /* clk2->sd_clk_phase = sd_clk_phase_set; // 1 */ + } else if (pdata->mmc->actual_clock > 45000000) { /* if > 45M */ + if (mmc->ios.signal_voltage + == MMC_SIGNAL_VOLTAGE_330) /* 3.3V */ + clk2->rx_clk_phase = 15; + else + clk2->rx_clk_phase = 11; + } else if (pdata->mmc->actual_clock >= 25000000) { /* if >= 25M */ + clk2->rx_clk_phase = 15; /* 10 */ + } else if (pdata->mmc->actual_clock > 5000000) { /* if > 5M */ + clk2->rx_clk_phase = 23; + } else if (pdata->mmc->actual_clock > 1000000) { /* if > 1M */ + clk2->rx_clk_phase = 55; + } else { + clk2->rx_clk_phase = 1061; /* 63; // 24; */ + } + writel(vclk2, host->base+SDHC_CLK2); + pdata->clk2 = vclk2; + + /*Disable All Irq*/ + writel(0, host->base+SDHC_ICTL); + + /*Wait for a while after clock setting*/ + /* udelay(100); */ + + spin_unlock_irqrestore(&host->mrq_lock, flags); + sdhc_dbg(AMLSD_DBG_IOS, "Clk IOS %d, Clk Src %d, Host Max Clk %d,", + clk_ios, clk_rate, pdata->f_max); + sdhc_dbg(AMLSD_DBG_IOS, " vclkc=%#x, clk2=%#x, actual_clock=%d,", + readl(host->base+SDHC_CLKC), + readl(host->base+SDHC_CLK2), + pdata->mmc->actual_clock); + sdhc_dbg(AMLSD_DBG_IOS, " rx_clk_phase=%d, sd_clk_phase=%d\n", + clk2->rx_clk_phase, clk2->sd_clk_phase); +} + +/*setup bus width, 1bit, 4bits, 8bits*/ +static void aml_sdhc_set_bus_width(struct amlsd_platform *pdata, u32 busw_ios) +{ + struct amlsd_host *host = (void *)pdata->host; + u32 vctrl = readl(host->base + SDHC_CTRL); + struct sdhc_ctrl *ctrl = (struct sdhc_ctrl *)&vctrl; + u32 width = 0; + + switch (busw_ios) { + case MMC_BUS_WIDTH_1: + width = 0; + break; + case MMC_BUS_WIDTH_4: + width = 1; + break; + case MMC_BUS_WIDTH_8: + width = 2; + break; + default: + sdhc_err("Error Data Bus\n"); + break; + } + + ctrl->dat_type = width; + pdata->width = width; + writel(vctrl, host->base+SDHC_CTRL); + sdhc_dbg(AMLSD_DBG_COMMON, "Bus Width Ios %d\n", busw_ios); +} + +/*call by mmc, power on, power off ...*/ +static void aml_sdhc_set_power(struct amlsd_platform *pdata, u32 power_mode) +{ + switch (power_mode) { + case MMC_POWER_ON: + if (pdata->pwr_pre) + pdata->pwr_pre(pdata); + if (pdata->pwr_on) + pdata->pwr_on(pdata); + break; + case MMC_POWER_UP: + break; + case MMC_POWER_OFF: + default: + if (pdata->pwr_pre) + pdata->pwr_pre(pdata); + if (pdata->pwr_off) + pdata->pwr_off(pdata); + break; + } +} + +/*call by mmc, set ios: power, clk, bus width*/ +static void aml_sdhc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct amlsd_platform *pdata = mmc_priv(mmc); + + if (!pdata->is_in) + return; + + /*Set Power*/ + aml_sdhc_set_power(pdata, ios->power_mode); + + /*Set Clock*/ + aml_sdhc_set_clk_rate(mmc, ios->clock); + + /*Set Bus Width*/ + aml_sdhc_set_bus_width(pdata, ios->bus_width); +#if 1 + if (ios->chip_select == MMC_CS_HIGH) + aml_cs_high(mmc); + else if (ios->chip_select == MMC_CS_DONTCARE) + aml_cs_dont_care(mmc); +#endif +} + +/*get readonly: 0 for rw, 1 for ro*/ +static int aml_sdhc_get_ro(struct mmc_host *mmc) +{ + struct amlsd_platform *pdata = mmc_priv(mmc); + u32 ro = 0; + + if (pdata->ro) + ro = pdata->ro(pdata); + return ro; +} + +/*get card detect: 1 for insert, 0 for removed*/ +int aml_sdhc_get_cd(struct mmc_host *mmc) +{ + struct amlsd_platform *pdata = mmc_priv(mmc); + + return pdata->is_in; /* 0: no inserted 1: inserted */ +} + +int aml_sdhc_signal_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios) +{ + return aml_sd_voltage_switch(mmc, ios->signal_voltage); +} + +/* Check if the card is pulling dat[0:3] low */ +static int aml_sdhc_card_busy(struct mmc_host *mmc) +{ + struct amlsd_platform *pdata = mmc_priv(mmc); + struct amlsd_host *host = pdata->host; + u32 vstat; + struct sdhc_stat *stat = (struct sdhc_stat *)&vstat; + + vstat = readl(host->base + SDHC_STAT); + sdhc_dbg(AMLSD_DBG_COMMON, "dat[0:3]=%#x\n", stat->dat3_0); + + /* return (stat->dat3_0 == 0); */ + if (stat->dat3_0 == 0) + return 1; + else + return 0; +} + +#if 0/* def CONFIG_PM */ +static int aml_sdhc_suspend(struct platform_device *pdev, pm_message_t state) +{ + int ret = 0; + int i; + struct amlsd_host *host = platform_get_drvdata(pdev); + struct mmc_host *mmc; + struct amlsd_platform *pdata; + + pr_info("***Entered %s:%s\n", __FILE__, __func__); + i = 0; + list_for_each_entry(pdata, &host->sibling, sibling) { + cancel_delayed_work_sync(&pdata->retuning); + pdata->need_retuning = false; + + mmc = pdata->mmc; + /* mmc_power_save_host(mmc); */ + ret = mmc_suspend_host(mmc); + if (ret) + break; + i++; + } + + if (ret) { + list_for_each_entry(pdata, &host->sibling, sibling) { + i--; + if (i < 0) + break; + + mmc = pdata->mmc; + mmc_resume_host(mmc); + } + } + pr_info("***Exited %s:%s\n", __FILE__, __func__); + + return ret; +} + +static int aml_sdhc_resume(struct platform_device *pdev) +{ + int ret = 0; + struct amlsd_host *host = platform_get_drvdata(pdev); + struct mmc_host *mmc; + struct amlsd_platform *pdata; + + pr_info("***Entered %s:%s\n", __FILE__, __func__); + list_for_each_entry(pdata, &host->sibling, sibling) { + /* detect if a card is exist or not if it is removable */ + if (!(pdata->caps & MMC_CAP_NONREMOVABLE)) + aml_sd_uart_detect(pdata); + + mmc = pdata->mmc; + /* mmc_power_restore_host(mmc); */ + ret = mmc_resume_host(mmc); + if (ret) + break; + } + pr_info("***Exited %s:%s\n", __FILE__, __func__); + return ret; +} +#else +#define aml_sdhc_suspend NULL +#define aml_sdhc_resume NULL +#endif + +static const struct mmc_host_ops aml_sdhc_ops = { + .request = aml_sdhc_request, + .set_ios = aml_sdhc_set_ios, + .get_cd = aml_sdhc_get_cd, + .get_ro = aml_sdhc_get_ro, + .start_signal_voltage_switch = aml_sdhc_signal_voltage_switch, + .card_busy = aml_sdhc_card_busy, + .execute_tuning = aml_sdhc_execute_tuning, + .hw_reset = aml_emmc_hw_reset, +}; + +/*for multi host claim host*/ +/* static struct mmc_claim aml_sdhc_claim;*/ + +static ssize_t sdhc_debug_func(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + int i; + + i = kstrtoint(buf, 0, &sdhc_debug_flag); + pr_info("sdhc_debug_flag: %d\n", sdhc_debug_flag); + return count; +} + +static ssize_t show_sdhc_debug(struct class *class, + struct class_attribute *attr, char *buf) +{ + pr_info("sdhc_debug_flag: %d\n", sdhc_debug_flag); + pr_info("1 : Force sdhc HOST_TX_FIFO_EMPTY error\n"); + pr_info("2 : Force sdhc HOST_RX_FIFO_FULL error\n"); + pr_info("3 : Force sdhc HOST_RSP_CRC_ERR error\n"); + pr_info("4 : Force sdhc HOST_DAT_CRC_ERR error\n"); + pr_info("5 : Force sdhc HOST_DAT_TIMEOUT_ERR error\n"); + pr_info("6 : Force sdhc HOST_RSP_TIMEOUT_ERR error\n"); + pr_info("9 : Force sdhc irq timeout error\n"); + + return 0; +} + +static struct class_attribute sdhc_class_attrs[] = { + __ATTR(debug, 0644, show_sdhc_debug, sdhc_debug_func), + __ATTR_NULL +}; + +static struct amlsd_host *aml_sdhc_init_host(struct amlsd_host *host) +{ + /* spin_lock_init(&aml_sdhc_claim.lock); + * init_waitqueue_head(&aml_sdhc_claim.wq); + */ + if (request_threaded_irq(host->irq, aml_sdhc_irq, + aml_sdhc_data_thread, + IRQF_SHARED, "sdhc", (void *)host)) { + sdhc_err("Request SDHC Irq Error!\n"); + return NULL; + } + + host->bn_buf = dma_alloc_coherent(NULL, SDHC_BOUNCE_REQ_SIZE, + &host->bn_dma_buf, GFP_KERNEL); + if (host->bn_buf == NULL) { + sdhc_err("Dma alloc Fail!\n"); + return NULL; + } + INIT_DELAYED_WORK(&host->timeout, aml_sdhc_timeout); + + spin_lock_init(&host->mrq_lock); + mutex_init(&host->pinmux_lock); + host->xfer_step = XFER_INIT; + + INIT_LIST_HEAD(&host->sibling); + + host->init_flag = 1; + + host->version = AML_MMC_VERSION; + /* host->storage_flag = storage_flag;*/ + host->pinctrl = NULL; + host->is_gated = false; + host->status = HOST_INVALID; + host->msg_buf = kmalloc(MESSAGE_BUF_SIZE, GFP_KERNEL); + if (!host->msg_buf) + pr_info("malloc message buffer fail\n"); + +#ifdef CONFIG_MMC_AML_DEBUG + host->req_cnt = 0; + sdhc_err("CONFIG_MMC_AML_DEBUG is on!\n"); +#endif + +#ifdef CONFIG_AML_MMC_DEBUG_FORCE_SINGLE_BLOCK_RW + sdhc_err("CONFIG_AML_MMC_DEBUG_FORCE_SINGLE_BLOCK_RW is on!\n"); +#endif + + host->debug.name = + kzalloc(strlen((const char *)AML_SDHC_MAGIC)+1, GFP_KERNEL); + strcpy((char *)(host->debug.name), (const char *)AML_SDHC_MAGIC); + host->debug.class_attrs = sdhc_class_attrs; + if (class_register(&host->debug)) + pr_info(" class register nand_class fail!\n"); + + return host; +} + +static int aml_sdhc_probe(struct platform_device *pdev) +{ + struct mmc_host *mmc = NULL; + struct amlsd_host *host = NULL; + struct amlsd_platform *pdata; + struct resource *res_mem; + /* struct reset_control *sdhc_reset;*/ + int size; + int ret = 0, i; + u32 gate_clk; + + host = kzalloc(sizeof(struct amlsd_host), GFP_KERNEL); + if (!host) + return -ENODEV; + + aml_mmc_ver_msg_show(); + + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res_mem) { + pr_info("error to get IORESOURCE\n"); + goto fail_init_host; + } + size = resource_size(res_mem); + + gate_clk = aml_read_cbus(HHI_GCLK_MPEG0); + gate_clk |= 0x4000; + aml_write_cbus(HHI_GCLK_MPEG0, gate_clk); + + /* sdhc_reset = devm_reset_control_get(&pdev->dev, "sdhc_gate"); + * reset_control_deassert(sdhc_reset); + */ + + host->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); + pr_info("host->irq = %d\n", host->irq); + + host->pinmux_base = ioremap(0xc1108000, 0x200); + host->base = devm_ioremap_nocache(&pdev->dev, res_mem->start, size); + aml_sdhc_init_host(host); + + /* if(amlsd_get_reg_base(pdev, host)) + * goto fail_init_host; + */ + + host->pdev = pdev; + host->dev = &pdev->dev; + platform_set_drvdata(pdev, host); + aml_sdhc_reg_init(host); + + /* for (i = 0; i < MMC_MAX_DEVICE; i++) {*/ + for (i = 0; i < 2; i++) { + /*malloc extra amlsd_platform*/ + mmc = mmc_alloc_host(sizeof(struct amlsd_platform), &pdev->dev); + if (!mmc) { + ret = -ENOMEM; + goto probe_free_host; + } + + pdata = mmc_priv(mmc); + memset(pdata, 0, sizeof(struct amlsd_platform)); + if (amlsd_get_platform_data(pdev, pdata, mmc, i)) { + mmc_free_host(mmc); + break; + } + +#if 0 + if (pdata->port == PORT_SDHC_C) { + if (is_emmc_exist(host)) { + mmc->is_emmc_port = 1; + } else { /* there is not eMMC/tsd */ + pr_info( + "[%s]: there is not eMMC/tsd,skip sdhc_c dts config!\n", + __func__); + + /* skip the port written in the dts */ + i++; + memset(pdata, 0, sizeof(struct amlsd_platform)); + if (amlsd_get_platform_data(pdev, + pdata, mmc, i)) { + mmc_free_host(mmc); + break; + } + } + } +#endif + + dev_set_name(&mmc->class_dev, "%s", pdata->pinname); + INIT_DELAYED_WORK(&pdata->retuning, aml_sdhc_tuning_timer); + if (pdata->caps & MMC_CAP_NONREMOVABLE) + pdata->is_in = true; + + if (pdata->caps & MMC_PM_KEEP_POWER) + mmc->pm_caps |= MMC_PM_KEEP_POWER; + pdata->host = host; + pdata->mmc = mmc; + pdata->is_fir_init = true; + pdata->is_tuned = false; + pdata->need_retuning = false; + pdata->signal_voltage = 0xff; + + /*mmc->index = i;*/ + mmc->ops = &aml_sdhc_ops; + /*mmc->alldev_claim = &aml_sdhc_claim;*/ + mmc->ios.clock = 400000; + mmc->ios.bus_width = MMC_BUS_WIDTH_1; + mmc->max_blk_count = 4095; + mmc->max_blk_size = 4095; + mmc->max_req_size = pdata->max_req_size; + mmc->max_seg_size = mmc->max_req_size; + mmc->max_segs = 1024; + mmc->ocr_avail = pdata->ocr_avail; + /*mmc->ocr = pdata->ocr_avail;*/ + mmc->caps = pdata->caps; + mmc->caps2 = pdata->caps2; + mmc->f_min = pdata->f_min; + mmc->f_max = pdata->f_max; + mmc->max_current_180 = 300; /* 300 mA in 1.8V */ + mmc->max_current_330 = 300; /* 300 mA in 3.3V */ + + if (aml_card_type_sdio(pdata)) /* if sdio_wifi */ + mmc->rescan_entered = 1; + /* do NOT run mmc_rescan for the first time */ + else + mmc->rescan_entered = 0; + + if (pdata->port_init) + pdata->port_init(pdata); + /* aml_sduart_pre(pdata);*/ + + ret = mmc_add_host(mmc); + if (ret) { /* error */ + sdhc_err("Failed to add mmc host.\n"); + goto probe_free_host; + } else { /* ok */ + if (aml_card_type_sdio(pdata)) { /* if sdio_wifi */ + sdio_host = mmc; + /* mmc->rescan_entered = 1; // do NOT + * run mmc_rescan for the first time + */ + } + } + + /*aml_sdhc_init_debugfs(mmc);*/ + /*Add each mmc host pdata to this controller host list*/ + INIT_LIST_HEAD(&pdata->sibling); + list_add_tail(&pdata->sibling, &host->sibling); + + /*Register card detect irq : plug in & unplug*/ + if (pdata->gpio_cd + && aml_card_type_non_sdio(pdata)) { + pdata->irq_init(pdata); + mutex_init(&pdata->in_out_lock); + ret = request_threaded_irq(pdata->irq_cd, + aml_sd_irq_cd, aml_irq_cd_thread, + IRQF_TRIGGER_RISING + | IRQF_TRIGGER_FALLING + | IRQF_ONESHOT, + "sdhc_mmc_cd", pdata); + if (ret) { + sd_emmc_err("Failed to request SD IN detect\n"); + goto probe_free_host; + } + } + } + + print_tmp("%s() success!\n", __func__); + return 0; + +probe_free_host: + list_for_each_entry(pdata, &host->sibling, sibling) { + mmc = pdata->mmc; + mmc_remove_host(mmc); + mmc_free_host(mmc); + } +fail_init_host: + iounmap(host->base); + free_irq(host->irq, host); + dma_free_coherent(NULL, SDHC_BOUNCE_REQ_SIZE, host->bn_buf, + (dma_addr_t)host->bn_dma_buf); + kfree(host); + print_tmp("aml_sdhc_probe() fail!\n"); + return ret; +} + +int aml_sdhc_remove(struct platform_device *pdev) +{ + struct amlsd_host *host = platform_get_drvdata(pdev); + struct mmc_host *mmc; + struct amlsd_platform *pdata; + + dma_free_coherent(NULL, SDHC_BOUNCE_REQ_SIZE, host->bn_buf, + (dma_addr_t)host->bn_dma_buf); + + free_irq(host->irq, host); + iounmap(host->base); + + list_for_each_entry(pdata, &host->sibling, sibling) { + mmc = pdata->mmc; + mmc_remove_host(mmc); + mmc_free_host(mmc); + } + /* aml_devm_pinctrl_put(host);*/ + + kfree(host->msg_buf); + kfree(host); + + /* switch_mod_gate_by_type(MOD_SDHC, 0); // gate clock of SDHC */ + return 0; +} + +static const struct of_device_id aml_sdhc_dt_match[] = { + { + .compatible = "amlogic, aml_sdhc", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, aml_sdhc_dt_match); + +static struct platform_driver aml_sdhc_driver = { + .probe = aml_sdhc_probe, + .remove = aml_sdhc_remove, + .suspend = aml_sdhc_suspend, + .resume = aml_sdhc_resume, + .driver = { + .name = "aml_sdhc", + .owner = THIS_MODULE, + .of_match_table = aml_sdhc_dt_match, + }, +}; + +static int __init aml_sdhc_init(void) +{ + return platform_driver_register(&aml_sdhc_driver); +} + +static void __exit aml_sdhc_cleanup(void) +{ + platform_driver_unregister(&aml_sdhc_driver); +} + +module_init(aml_sdhc_init); +module_exit(aml_sdhc_cleanup); + +MODULE_DESCRIPTION("Amlogic Multimedia Card driver"); +MODULE_LICENSE("GPL"); + +static int __init rx_clk_phase_setup(char *str) +{ + int ret; + + ret = kstrtol(str, 0, (long *)&rx_clk_phase_set); + print_dbg("rx_clk_phase=%d\n", rx_clk_phase_set); + return ret; +} +__setup("rx_clk_phase=", rx_clk_phase_setup); + +static int __init sd_clk_phase_setup(char *str) +{ + int ret; + + ret = kstrtol(str, 0, (long *)&sd_clk_phase_set); + print_dbg("sd_clk_phase_set=%d\n", sd_clk_phase_set); + return ret; +} +__setup("sd_clk_phase=", sd_clk_phase_setup); + +static int __init rx_endian_setup(char *str) +{ + int ret; + + ret = kstrtol(str, 0, (long *)&rx_endian); + print_dbg("rx_endian=%#x\n", rx_endian); + return ret; +} +__setup("rx_endian=", rx_endian_setup); + +static int __init tx_endian_setup(char *str) +{ + int ret; + + ret = kstrtol(str, 0, (long *)&tx_endian); + print_dbg("tx_endian=%#x\n", tx_endian); + return ret; +} +__setup("tx_endian=", tx_endian_setup); + +static int __init val1_setup(char *str) +{ + int ret; + + ret = kstrtol(str, 0, (long *)&val1); + print_dbg("val1=%d\n", val1); + return ret; +} +__setup("val1=", val1_setup); diff --git a/drivers/amlogic/mmc/aml_sdio.c b/drivers/amlogic/mmc/aml_sdio.c new file mode 100644 index 000000000000..59d9fc8c70c4 --- /dev/null +++ b/drivers/amlogic/mmc/aml_sdio.c @@ -0,0 +1,1367 @@ +/* + * drivers/amlogic/mmc/aml_sdio.c + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* static struct mmc_claim aml_sdio_claim; */ +#define sdio_cmd_busy_bit 4 +int CMD_PROCESS_JIT; +int SDIO_IRQ_SUPPORT; +static void aml_sdio_send_stop(struct amlsd_host *host); +static unsigned int sdio_error_flag; +static unsigned int sdio_debug_flag; +static unsigned int sdio_err_bak; +static unsigned int timeout_cmd_cnt; + +void sdio_debug_irqstatus(struct sdio_status_irq *irqs, struct cmd_send *send) +{ + switch (sdio_debug_flag) { + case 1: + irqs->sdio_response_crc7_ok = 0; + send->response_do_not_have_crc7 = 0; + sdhc_err("Force sdio cmd response crc error here\n"); + break; + case 2: + irqs->sdio_data_read_crc16_ok = 0; + irqs->sdio_data_write_crc16_ok = 0; + sdhc_err("Force sdio data crc here\n"); + break; + default: + break; + } + /* only enable once for debug */ + sdio_debug_flag = 0; +} + +static void aml_sdio_soft_reset(struct amlsd_host *host) +{ + struct sdio_irq_config irqc = {0}; + + /*soft reset*/ + irqc.soft_reset = 1; + writel(*(u32 *)&irqc, host->base + SDIO_IRQC); + udelay(2); +} + +static int aml_sdio_clktree_init(struct amlsd_host *host) +{ + int ret = 0; + + host->core_clk = devm_clk_get(host->dev, "core"); + if (IS_ERR(host->core_clk)) { + ret = PTR_ERR(host->core_clk); + pr_err("devm_clk_get core_clk fail %d\n", ret); + return ret; + } + ret = clk_prepare_enable(host->core_clk); + if (ret) { + pr_err("clk_prepare_enable core_clk fail %d\n", ret); + return ret; + } + + pr_info("aml_sdio_clktree_init ok\n"); + return 0; +} + +/* + * init sdio reg + */ +static void aml_sdio_init_param(struct amlsd_host *host) +{ + struct sdio_status_irq irqs = {0}; + struct sdio_config conf = {0}; + + aml_sdio_clktree_init(host); + + /* write 1 clear bit8,9 */ + irqs.sdio_if_int = 1; + irqs.sdio_cmd_int = 1; + writel(*(u32 *)&irqs, host->base + SDIO_IRQS); + + /* setup config */ + conf.sdio_write_crc_ok_status = 2; + conf.sdio_write_nwr = 2; + conf.m_endian = 3; + conf.cmd_argument_bits = 39; + conf.cmd_out_at_posedge = 0; + conf.cmd_disable_crc = 0; + conf.data_latch_at_negedge = 0; + conf.cmd_clk_divide = CLK_DIV; + writel(*(u32 *)&conf, host->base + SDIO_CONF); +} + +static bool is_card_last_block(struct amlsd_platform *pdata, u32 lba, u32 cnt) +{ + if (!pdata->card_capacity) + pdata->card_capacity = mmc_capacity(pdata->mmc->card); + + return (lba + cnt) == pdata->card_capacity; +} + +/* + * read response from REG0_ARGU(136bit or 48bit) + */ +void aml_sdio_read_response(struct amlsd_platform *pdata, + struct mmc_request *mrq) +{ + int i, resp[4]; + struct amlsd_host *host = pdata->host; + u32 vmult = readl(host->base + SDIO_MULT); + struct sdio_mult_config *mult = (void *)&vmult; + struct mmc_command *cmd = mrq->cmd; + + mult->write_read_out_index = 1; + mult->response_read_index = 0; + writel(vmult, host->base + SDIO_MULT); + + if (cmd->flags & MMC_RSP_136) { + for (i = 0; i <= 3; i++) + resp[3-i] = readl(host->base + SDIO_ARGU); + cmd->resp[0] = (resp[0]<<8)|((resp[1]>>24)&0xff); + cmd->resp[1] = (resp[1]<<8)|((resp[2]>>24)&0xff); + cmd->resp[2] = (resp[2]<<8)|((resp[3]>>24)&0xff); + cmd->resp[3] = (resp[3]<<8); + sdio_dbg(AMLSD_DBG_RESP, "Cmd %d ,Resp %x-%x-%x-%x\n", + cmd->opcode, cmd->resp[0], cmd->resp[1], + cmd->resp[2], cmd->resp[3]); + } else if (cmd->flags & MMC_RSP_PRESENT) { + cmd->resp[0] = readl(host->base + SDIO_ARGU); + sdio_dbg(AMLSD_DBG_RESP, "Cmd %d, Resp 0x%x\n", + cmd->opcode, cmd->resp[0]); + + /* Now in sdio controller, something is wrong. + * When we read last block in multi-blocks-mode, + * it will cause "ADDRESS_OUT_OF_RANGE" error in + * card status, we must clear it. + */ + /* status error: address out of range */ + if ((cmd->resp[0] & R1_OUT_OF_RANGE) + && (mrq->data) && + (is_card_last_block(pdata, cmd->arg, + mrq->data->blocks))) { + cmd->resp[0] &= (~R1_OUT_OF_RANGE); + /* clear the error */ + } + } +} + +/*copy buffer from data->sg to dma buffer, set dma addr to reg*/ +void aml_sdio_prepare_dma(struct amlsd_host *host, struct mmc_request *mrq) +{ + struct mmc_data *data = mrq->data; + + if (data->flags & MMC_DATA_WRITE) { + aml_sg_copy_buffer(data->sg, data->sg_len, + host->bn_buf, data->blksz*data->blocks, 1); + sdio_dbg(AMLSD_DBG_WR_DATA, "W Cmd %d, %x-%x-%x-%x\n", + mrq->cmd->opcode, + host->bn_buf[0], host->bn_buf[1], + host->bn_buf[2], host->bn_buf[3]); + } + /* host->dma_addr = host->bn_dma_buf; */ +} + +void aml_sdio_set_port_ios(struct mmc_host *mmc) +{ + struct amlsd_platform *pdata = mmc_priv(mmc); + struct amlsd_host *host = pdata->host; + u32 vconf = readl(host->base + SDIO_CONF); + struct sdio_config *conf = (void *)&vconf; + + if (aml_card_type_sdio(pdata) + && (pdata->mmc->actual_clock > 50000000)) { + /* if > 50MHz */ + conf->data_latch_at_negedge = 1; /* [19] //0 */ + conf->do_not_delay_data = 1; /* [18] */ + conf->cmd_out_at_posedge = 0; + } else { + conf->data_latch_at_negedge = 0; /* [19] //0 */ + conf->do_not_delay_data = 0; /* [18] */ + conf->cmd_out_at_posedge = 0; + } + writel(vconf, host->base+SDIO_CONF); + if ((conf->cmd_clk_divide == pdata->clkc) + && (conf->bus_width == pdata->width)) + return; + + /*Setup Clock*/ + conf->cmd_clk_divide = pdata->clkc; + /*Setup Bus Width*/ + conf->bus_width = pdata->width; + writel(vconf, host->base+SDIO_CONF); +} + +static void aml_sdio_enable_irq(struct mmc_host *mmc, int enable) +{ + struct amlsd_platform *pdata = mmc_priv(mmc); + struct amlsd_host *host = pdata->host; + u32 virqc; + struct sdio_irq_config *irqc; + u32 virqs; + struct sdio_status_irq *irqs; + u32 vmult; + struct sdio_mult_config *mult; + unsigned long flags; + + if (host->xfer_step == XFER_START + || host->xfer_step == XFER_AFTER_START) { + return; + + } + if (enable) { + spin_lock_irqsave(&host->mrq_lock, flags); + if (host->xfer_step == XFER_START + || host->xfer_step == XFER_AFTER_START) { + /* pr_info("cmd irq is running + * when aml_sdio_enable_irq() + * enable = %d\n", enable); + */ + /* pr_info("irqs->sdio_cmd_int = %d\n", + * irqs->sdio_cmd_int ); + */ + spin_unlock_irqrestore(&host->mrq_lock, flags); + return; + } + virqc = readl(host->base + SDIO_IRQC); + irqc = (void *)&virqc; + virqs = readl(host->base + SDIO_IRQS); + irqs = (void *)&virqs; + vmult = readl(host->base + SDIO_MULT); + mult = (void *)&vmult; + + /* u32 vmult = readl(host->base + SDIO_MULT); */ + /* struct sdio_mult_config* mult = (void*)&vmult; */ + + /* enable if int irq */ + irqc->arc_if_int_en = 1; + irqs->sdio_if_int = 1; + + mult->sdio_port_sel = pdata->port; + writel(vmult, host->base + SDIO_MULT); + writel(virqs, host->base + SDIO_IRQS); + writel(virqc, host->base + SDIO_IRQC); + spin_unlock_irqrestore(&host->mrq_lock, flags); + } else { + + virqc = readl(host->base + SDIO_IRQC); + irqc = (void *)&virqc; + virqs = readl(host->base + SDIO_IRQS); + + irqs = (void *)&virqs; + vmult = readl(host->base + SDIO_MULT); + mult = (void *)&vmult; + + irqc->arc_if_int_en = 0; + irqs->sdio_if_int = 1; + + mult->sdio_port_sel = pdata->port; + writel(vmult, host->base + SDIO_MULT); + + writel(virqs, host->base + SDIO_IRQS); + writel(virqc, host->base + SDIO_IRQC); + } +} + +/*set to register, start xfer*/ +void aml_sdio_start_cmd(struct mmc_host *mmc, struct mmc_request *mrq) +{ + u32 pack_size; + struct amlsd_platform *pdata = mmc_priv(mmc); + struct amlsd_host *host = pdata->host; + struct cmd_send send = {0}; + struct sdio_extension ext = {0}; + u32 virqc = readl(host->base + SDIO_IRQC); + struct sdio_irq_config *irqc = (void *)&virqc; + u32 virqs = readl(host->base + SDIO_IRQS); + struct sdio_status_irq *irqs = (void *)&virqs; + u32 vmult = readl(host->base + SDIO_MULT); + struct sdio_mult_config *mult = (void *)&vmult; + + switch (mmc_resp_type(mrq->cmd)) { + case MMC_RSP_R1: + case MMC_RSP_R1B: + case MMC_RSP_R3: + /*7(cmd)+32(respnse)+7(crc)-1 data*/ + send.cmd_response_bits = 45; + break; + case MMC_RSP_R2: + /* 7(cmd)+120(respnse)+7(crc)-1 data */ + send.cmd_response_bits = 133; + send.response_crc7_from_8 = 1; + break; + default: + /*no response*/ + break; + } + + if (!(mrq->cmd->flags & MMC_RSP_CRC)) + send.response_do_not_have_crc7 = 1; + + if (mrq->cmd->flags & MMC_RSP_BUSY) + send.check_busy_on_dat0 = 1; + + /* clear here */ + timeout_cmd_cnt = 0; + + if (mrq->data) { + /*total package num*/ + send.repeat_package_times = mrq->data->blocks - 1; + WARN_ON(mrq->data->blocks > 256); + /*package size*/ + if (pdata->width) /*0: 1bit, 1: 4bit*/ + pack_size = mrq->data->blksz*8 + (16-1)*4; + else + pack_size = mrq->data->blksz*8 + (16-1); + ext.data_rw_number = pack_size; + if (mrq->data->flags & MMC_DATA_WRITE) + send.cmd_send_data = 1; + else + send.response_have_data = 1; + } + /*cmd index*/ + send.cmd_command = 0x40|mrq->cmd->opcode; + + aml_sdio_soft_reset(host); + + /*enable cmd irq*/ + irqc->arc_cmd_int_en = 1; + + /*clear pending*/ + irqs->sdio_cmd_int = 1; + + aml_sdio_set_port_ios(host->mmc); + + mult->sdio_port_sel = pdata->port; + vmult |= (1<<31); + writel(vmult, host->base + SDIO_MULT); + writel(virqs, host->base + SDIO_IRQS); + writel(virqc, host->base + SDIO_IRQC); + /* setup all reg to send cmd */ + writel(mrq->cmd->arg, host->base + SDIO_ARGU); + writel(*(u32 *)&ext, host->base + SDIO_EXT); + writel(*(u32 *)&send, host->base + SDIO_SEND); +} + +/* + * clear struct & call mmc_request_done + */ +void aml_sdio_request_done(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct amlsd_platform *pdata = mmc_priv(mmc); + struct amlsd_host *host = pdata->host; + unsigned long flags; + struct mmc_command *cmd; + + if (delayed_work_pending(&host->timeout)) + cancel_delayed_work_sync(&host->timeout); + + spin_lock_irqsave(&host->mrq_lock, flags); + WARN_ON(!host->mrq->cmd); + WARN_ON(host->xfer_step == XFER_FINISHED); + aml_sdio_read_response(pdata, host->mrq); + + cmd = host->mrq->cmd; /* for debug */ + host->mrq = NULL; + host->xfer_step = XFER_FINISHED; + spin_unlock_irqrestore(&host->mrq_lock, flags); + + if (cmd->flags & MMC_RSP_136) { + sdio_dbg(AMLSD_DBG_RESP, "Cmd %d ,Resp %x-%x-%x-%x\n", + cmd->opcode, cmd->resp[0], cmd->resp[1], + cmd->resp[2], cmd->resp[3]); + } else if (cmd->flags & MMC_RSP_PRESENT) { + sdio_dbg(AMLSD_DBG_RESP, "Cmd %d ,Resp 0x%x\n", + cmd->opcode, cmd->resp[0]); + } + +#ifdef CONFIG_MMC_AML_DEBUG + host->req_cnt--; + + aml_dbg_verify_pinmux(pdata); + aml_dbg_verify_pull_up(pdata); +#endif + + if (pdata->xfer_post) + pdata->xfer_post(mmc); + + mmc_request_done(host->mmc, mrq); +} + +static void aml_sdio_print_err(struct amlsd_host *host, char *msg) +{ + struct amlsd_platform *pdata = mmc_priv(host->mmc); + u32 virqs = readl(host->base + SDIO_IRQS); + u32 virqc = readl(host->base + SDIO_IRQC); + u32 vconf = readl(host->base + SDIO_CONF); + struct sdio_config *conf = (void *)&vconf; + u32 clk_rate = clk_get_rate(host->core_clk) / 2; + + sdio_err("%s: %s,", mmc_hostname(host->mmc), msg); + pr_info("Cmd%d arg %#x,", host->mrq->cmd->opcode, host->mrq->cmd->arg); + pr_info("Xfer %d Bytes,", host->mrq->data?host->mrq->data->blksz + *host->mrq->data->blocks:0); + pr_info("host->xfer_step=%d,", host->xfer_step); + pr_info("host->cmd_is_stop=%d,", host->cmd_is_stop); + pr_info("pdata->port=%d,", pdata->port); + pr_info("virqs=%#0x, virqc=%#0x,", virqs, virqc); + pr_info("conf->cmd_clk_divide=%d,", conf->cmd_clk_divide); + pr_info("pdata->clkc=%d,", pdata->clkc); + pr_info("conf->bus_width=%d,", conf->bus_width); + pr_info("pdata->width=%d,", pdata->width); + pr_info("conf=%#x, clock=%d\n", vconf, + clk_rate / (conf->cmd_clk_divide + 1)); +} + +/*setup delayed workstruct in aml_sdio_request*/ +static void aml_sdio_timeout(struct work_struct *work) +{ + static int timeout_cnt; + struct amlsd_host *host = + container_of(work, struct amlsd_host, timeout.work); + u32 virqs; + struct sdio_status_irq *irqs; + u32 virqc; + struct sdio_irq_config *irqc; + unsigned long flags; + struct amlsd_platform *pdata = mmc_priv(host->mmc); + int is_mmc_stop = 0; + unsigned long time_start_cnt = aml_read_cbus(ISA_TIMERE); + + + time_start_cnt = (time_start_cnt - host->time_req_sta) / 1000; + + virqs = readl(host->base + SDIO_IRQS); + irqs = (void *)&virqs; + virqc = readl(host->base + SDIO_IRQC); + irqc = (void *)&virqc; + + spin_lock_irqsave(&host->mrq_lock, flags); + if (host->xfer_step == XFER_FINISHED) { + spin_unlock_irqrestore(&host->mrq_lock, flags); + sdio_err("timeout after xfer finished\n"); + return; + } + if ((irqs->sdio_cmd_int) /* irq have been occurred */ + || (host->xfer_step == XFER_IRQ_OCCUR)) { + /* isr have been run */ + spin_unlock_irqrestore(&host->mrq_lock, flags); + schedule_delayed_work(&host->timeout, msecs_to_jiffies(500)); + host->time_req_sta = aml_read_cbus(ISA_TIMERE); + + if (irqs->sdio_cmd_int) { + timeout_cnt++; + if (timeout_cnt > 30) + goto timeout_handle; + sdio_err("%s: cmd%d,", mmc_hostname(host->mmc), + host->mrq->cmd->opcode); + pr_info("ISR have been run,"); + pr_info("xfer_step=%d,", host->xfer_step); + pr_info("time_start_cnt=%ldmS,", time_start_cnt); + pr_info("timeout_cnt=%d\n", timeout_cnt); + } else + sdio_err("%s: isr have been run\n", + mmc_hostname(host->mmc)); + return; + } + spin_unlock_irqrestore(&host->mrq_lock, flags); +timeout_handle: + timeout_cnt = 0; + + if (!(irqc->arc_cmd_int_en)) { + sdio_err("%s: arc_cmd_int_en is not enable\n", + mmc_hostname(host->mmc)); + } + + /* Disable Command-Done-Interrupt to avoid irq occurs + * It will be enabled again in the next cmd. + */ + irqc->arc_cmd_int_en = 0; /* disable cmd irq */ + writel(virqc, host->base + SDIO_IRQC); + + spin_lock_irqsave(&host->mrq_lock, flags); + + /* do not retry for sdcard */ + if (!aml_card_type_mmc(pdata)) { + sdio_error_flag |= (1<<30); + host->mrq->cmd->retries = 0; + } else if (((sdio_error_flag & (1<<3)) == 0) + && (host->mrq->data != NULL) + && pdata->is_in) { + /* set cmd retry cnt when first error. */ + sdio_error_flag |= (1<<3); + host->mrq->cmd->retries = AML_TIMEOUT_RETRY_COUNTER; + } + + /* here clear error flags after error retried */ + if (sdio_error_flag && (host->mrq->cmd->retries == 0)) + sdio_error_flag |= (1<<30); + + host->xfer_step = XFER_TIMEDOUT; + host->mrq->cmd->error = -ETIMEDOUT; + spin_unlock_irqrestore(&host->mrq_lock, flags); + + sdio_err("time_start_cnt:%ld\n", time_start_cnt); + aml_sdio_print_err(host, "Timeout error"); + +#ifdef CONFIG_MMC_AML_DEBUG + aml_dbg_verify_pinmux(pdata); + aml_dbg_verify_pull_up(pdata); + aml_sdio_print_reg(host); + /* aml_dbg_print_pinmux(); */ +#endif + + if (host->mrq->stop && aml_card_type_mmc(pdata) && !host->cmd_is_stop) { + /* sdio_err("Send stop cmd before timeout retry..\n"); */ + spin_lock_irqsave(&host->mrq_lock, flags); + aml_sdio_send_stop(host); + spin_unlock_irqrestore(&host->mrq_lock, flags); + is_mmc_stop = 1; + schedule_delayed_work(&host->timeout, 50); + } else { + if (host->cmd_is_stop) + host->cmd_is_stop = 0; + aml_sdio_request_done(host->mmc, host->mrq); + } +} + +/* + * aml handle request + * 1. setup data + * 2. send cmd + * 3. return (aml_sdio_request_done in irq function) + */ +void aml_sdio_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct amlsd_platform *pdata; + struct amlsd_host *host; + unsigned long flags; + unsigned int timeout; + u32 virqc; + struct sdio_irq_config *irqc; + + WARN_ON(!mmc); + WARN_ON(!mrq); + + pdata = mmc_priv(mmc); + host = (void *)pdata->host; + + virqc = readl(host->base + SDIO_IRQC); + irqc = (void *)&virqc; + + if (aml_card_type_non_sdio(pdata)) { + irqc->arc_if_int_en = 0; + writel(virqc, host->base + SDIO_IRQC); + } + + if (aml_check_unsupport_cmd(mmc, mrq)) + return; + + /* only for SDCARD hotplag */ + if ((!pdata->is_in + || (!host->init_flag + && aml_card_type_non_sdio(pdata))) + && (mrq->cmd->opcode != 0)) { + spin_lock_irqsave(&host->mrq_lock, flags); + mrq->cmd->error = -ENOMEDIUM; + mrq->cmd->retries = 0; + host->mrq = NULL; + host->xfer_step = XFER_FINISHED; + spin_unlock_irqrestore(&host->mrq_lock, flags); + + mmc_request_done(mmc, mrq); + return; + } + +#ifdef CONFIG_MMC_AML_DEBUG + if (host->req_cnt) + sdio_err("Reentry error! host->req_cnt=%d\n", host->req_cnt); + host->req_cnt++; +#endif + + if (mrq->cmd->opcode == 0) + host->init_flag = 1; + + sdio_dbg(AMLSD_DBG_REQ, "%s: starting CMD%u arg %08x flags %08x\n", + mmc_hostname(mmc), mrq->cmd->opcode, + mrq->cmd->arg, mrq->cmd->flags); + if (mrq->data) { + /*Copy data to dma buffer for write request*/ + aml_sdio_prepare_dma(host, mrq); + writel(host->bn_dma_buf, host->base + SDIO_ADDR); + sdio_dbg(AMLSD_DBG_REQ, + "%s: blksz %d blocks %d flags %08x tsac %d ms nsac %d\n", + mmc_hostname(mmc), mrq->data->blksz, + mrq->data->blocks, mrq->data->flags, + mrq->data->timeout_ns / 1000000, + mrq->data->timeout_clks); + } + + /*clear pinmux & set pinmux*/ + if (pdata->xfer_pre) + pdata->xfer_pre(mmc); + +#ifdef CONFIG_MMC_AML_DEBUG + aml_dbg_verify_pull_up(pdata); + aml_dbg_verify_pinmux(pdata); +#endif + + if (!mrq->data) + timeout = 1000; + else + timeout = 5000; + /* 5s */ + if (mrq->cmd->opcode == MMC_ERASE) /* maybe over 30S for erase cmd. */ + timeout = 30000; + + schedule_delayed_work(&host->timeout, msecs_to_jiffies(timeout)); + + CMD_PROCESS_JIT = timeout; + spin_lock_irqsave(&host->mrq_lock, flags); + if (SDIO_IRQ_SUPPORT) + if ((mmc->caps & MMC_CAP_SDIO_IRQ) + && (mmc->ops->enable_sdio_irq)) + mmc->ops->enable_sdio_irq(mmc, 0); + + if (host->xfer_step != XFER_FINISHED + && host->xfer_step != XFER_INIT) + sdio_err("host->xfer_step %d\n", host->xfer_step); + + /* clear error flag if last command retried failed here */ + if (sdio_error_flag & (1<<30)) + sdio_error_flag = 0; + + host->mrq = mrq; + host->mmc = mmc; + host->xfer_step = XFER_START; + host->opcode = mrq->cmd->opcode; + host->arg = mrq->cmd->arg; + host->time_req_sta = aml_read_cbus(ISA_TIMERE); + + aml_sdio_start_cmd(mmc, mrq); + host->xfer_step = XFER_AFTER_START; + spin_unlock_irqrestore(&host->mrq_lock, flags); +} + +struct mmc_command aml_sdio_cmd = { + .opcode = MMC_STOP_TRANSMISSION, + .flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC, +}; +struct mmc_request aml_sdio_stop = { + .cmd = &aml_sdio_cmd, +}; + +static void aml_sdio_send_stop(struct amlsd_host *host) +{ + /*Already in mrq_lock*/ + host->cmd_is_stop = 1; + sdio_err_bak = host->mrq->cmd->error; + host->mrq->cmd->error = 0; + aml_sdio_start_cmd(host->mmc, &aml_sdio_stop); +} + +/* + * enable cmd & data irq, call tasket, do aml_sdio_request_done + */ +static irqreturn_t aml_sdio_irq(int irq, void *dev_id) +{ + struct amlsd_host *host = (void *)dev_id; + u32 virqs = readl(host->base + SDIO_IRQS); + struct sdio_status_irq *irqs = (void *)&virqs; + struct mmc_request *mrq; + unsigned long flags; + int sdio_cmd_int = 0; + + spin_lock_irqsave(&host->mrq_lock, flags); + mrq = host->mrq; + if (!mrq && !irqs->sdio_if_int) { + + if (host->xfer_step == XFER_FINISHED || + host->xfer_step == XFER_TIMEDOUT){ + spin_unlock_irqrestore(&host->mrq_lock, flags); + return IRQ_HANDLED; + } + WARN_ON(!mrq); + /* aml_sdio_print_reg(host);*/ + spin_unlock_irqrestore(&host->mrq_lock, flags); + return IRQ_HANDLED; + } + + if (irqs->sdio_cmd_int && mrq) { + if (host->cmd_is_stop) + host->xfer_step = XFER_IRQ_TASKLET_BUSY; + else + host->xfer_step = XFER_IRQ_OCCUR; + spin_unlock_irqrestore(&host->mrq_lock, flags); + if ((SDIO_IRQ_SUPPORT) + && !(irqs->sdio_if_int) + && (host->mmc->sdio_irq_pending != true)) + host->mmc->ops->enable_sdio_irq(host->mmc, 1); + if (irqs->sdio_if_int && SDIO_IRQ_SUPPORT) + sdio_cmd_int = 1; + else + return IRQ_WAKE_THREAD; + } else + spin_unlock_irqrestore(&host->mrq_lock, flags); + + if (irqs->sdio_if_int) { + if ((host->mmc->sdio_irq_thread) + && (!atomic_read(&host->mmc->sdio_irq_thread_abort))) + mmc_signal_sdio_irq(host->mmc); + } + + if (SDIO_IRQ_SUPPORT && sdio_cmd_int) + return IRQ_WAKE_THREAD; + + /* if cmd has stop, call aml_sdio_send_stop */ + return IRQ_HANDLED; +} + +irqreturn_t aml_sdio_irq_thread(int irq, void *data) +{ + struct amlsd_host *host = (void *)data; + u32 virqs = readl(host->base + SDIO_IRQS); + struct sdio_status_irq *irqs = (void *)&virqs; + u32 vsend = readl(host->base + SDIO_SEND); + struct cmd_send *send = (void *)&vsend; + unsigned long flags; + struct mmc_request *mrq; + enum aml_mmc_waitfor xfer_step; + struct amlsd_platform *pdata = mmc_priv(host->mmc); + + spin_lock_irqsave(&host->mrq_lock, flags); + mrq = host->mrq; + xfer_step = host->xfer_step; + + if ((xfer_step == XFER_FINISHED) || (xfer_step == XFER_TIMER_TIMEOUT)) { + sdhc_err("Warning: xfer_step=%d\n", xfer_step); + spin_unlock_irqrestore(&host->mrq_lock, flags); + return IRQ_HANDLED; + } + + if (!mrq) { + sdio_err("CMD%u, arg %08x, mrq NULL xfer_step %d\n", + host->opcode, host->arg, xfer_step); + if (xfer_step == XFER_FINISHED || + xfer_step == XFER_TIMEDOUT){ + spin_unlock_irqrestore(&host->mrq_lock, flags); + sdio_err("[aml_sdio_irq_thread] out\n"); + return IRQ_HANDLED; + } + spin_unlock_irqrestore(&host->mrq_lock, flags); + return IRQ_HANDLED; + } + + if ((SDIO_IRQ_SUPPORT) + && (host->xfer_step == XFER_TASKLET_DATA)) { + spin_unlock_irqrestore(&host->mrq_lock, flags); + return IRQ_HANDLED; + } + + if (host->cmd_is_stop) { + host->cmd_is_stop = 0; + mrq->cmd->error = sdio_err_bak; + spin_unlock_irqrestore(&host->mrq_lock, flags); + aml_sdio_request_done(host->mmc, mrq); + return IRQ_HANDLED; + } + host->xfer_step = XFER_TASKLET_DATA; + + if (!mrq->data) { + if (irqs->sdio_response_crc7_ok + || send->response_do_not_have_crc7) { + mrq->cmd->error = 0; + spin_unlock_irqrestore(&host->mrq_lock, flags); + } else { + mrq->cmd->error = -EILSEQ; + spin_unlock_irqrestore(&host->mrq_lock, flags); + aml_sdio_print_err(host, "cmd crc7 error"); + } + aml_sdio_request_done(host->mmc, mrq); + } else{ + if (irqs->sdio_data_read_crc16_ok + || irqs->sdio_data_write_crc16_ok) { + mrq->cmd->error = 0; + spin_unlock_irqrestore(&host->mrq_lock, flags); + } else { + mrq->cmd->error = -EILSEQ; + if ((sdio_error_flag == 0) + && aml_card_type_mmc(pdata)) { + /* set cmd retry cnt when first error. */ + sdio_error_flag |= (1<<0); + mrq->cmd->retries = AML_ERROR_RETRY_COUNTER; + } + spin_unlock_irqrestore(&host->mrq_lock, flags); + aml_sdio_print_err(host, "data crc16 error"); + } + spin_lock_irqsave(&host->mrq_lock, flags); + mrq->data->bytes_xfered = mrq->data->blksz*mrq->data->blocks; + + if ((mrq->cmd->error == 0) || (sdio_error_flag + && (mrq->cmd->retries == 0))) { + sdio_error_flag |= (1<<30); + } + + spin_unlock_irqrestore(&host->mrq_lock, flags); + if (mrq->data->flags & MMC_DATA_READ) { + aml_sg_copy_buffer(mrq->data->sg, mrq->data->sg_len, + host->bn_buf, + mrq->data->blksz*mrq->data->blocks, 0); + sdio_dbg(AMLSD_DBG_RD_DATA, "R Cmd %d, %x-%x-%x-%x\n", + host->mrq->cmd->opcode, + host->bn_buf[0], host->bn_buf[1], + host->bn_buf[2], host->bn_buf[3]); + } + spin_lock_irqsave(&host->mrq_lock, flags); + if (mrq->stop) { + aml_sdio_send_stop(host); + spin_unlock_irqrestore(&host->mrq_lock, flags); + } else { + spin_unlock_irqrestore(&host->mrq_lock, flags); + aml_sdio_request_done(host->mmc, mrq); + } + } + return IRQ_HANDLED; +} + +/* + * 1. clock valid range + * 2. clk config enable + * 3. select clock source + * 4. set clock divide + */ +static void aml_sdio_set_clk_rate(struct amlsd_platform *pdata, u32 clk_ios) +{ + struct amlsd_host *host = (void *)pdata->host; + u32 vconf = readl(host->base + SDIO_CONF); + struct sdio_config *conf = (void *)&vconf; + u32 clk_rate = clk_get_rate(host->core_clk) / 2; /* tmp for 3.10 */ + u32 clk_div; + + if (clk_ios > pdata->f_max) + clk_ios = pdata->f_max; + if (clk_ios < pdata->f_min) + clk_ios = pdata->f_min; + + WARN_ON(!clk_ios); + + /*0: dont set it, 1:div2, 2:div3, 3:div4...*/ + clk_div = clk_rate / clk_ios - !(clk_rate%clk_ios); + + if (aml_card_type_sdio(pdata) + && (pdata->f_max > 50000000)) /* if > 50MHz */ + clk_div = 0; + + conf->cmd_clk_divide = clk_div; + pdata->clkc = clk_div; + pdata->mmc->actual_clock = clk_rate / (clk_div + 1); + writel(vconf, host->base + SDIO_CONF); + sdio_dbg(AMLSD_DBG_IOS, + "Clk IOS %d, Clk Src %d, Host Max Clk %d, clk_divide=%d\n", + clk_ios, (clk_rate*2), pdata->f_max, clk_div); +} + +static void aml_sdio_set_bus_width(struct amlsd_platform *pdata, u32 busw_ios) +{ + u32 bus_width = 0; + struct amlsd_host *host = (void *)pdata->host; + u32 vconf = readl(host->base + SDIO_CONF); + struct sdio_config *conf = (void *)&vconf; + + switch (busw_ios) { + case MMC_BUS_WIDTH_1: + bus_width = 0; + break; + case MMC_BUS_WIDTH_4: + bus_width = 1; + break; + case MMC_BUS_WIDTH_8: + default: + sdio_err("SDIO Controller Can Not Support 8bit Data Bus\n"); + break; + } + + conf->bus_width = bus_width; + pdata->width = bus_width; + writel(vconf, host->base + SDIO_CONF); + sdio_dbg(AMLSD_DBG_IOS, "Bus Width Ios %d\n", bus_width); +} + +static void aml_sdio_set_power(struct amlsd_platform *pdata, u32 power_mode) +{ + switch (power_mode) { + case MMC_POWER_ON: + if (pdata->pwr_pre) + pdata->pwr_pre(pdata); + if (pdata->pwr_on) + pdata->pwr_on(pdata); + break; + case MMC_POWER_UP: + break; + case MMC_POWER_OFF: + default: + if (pdata->pwr_pre) + pdata->pwr_pre(pdata); + if (pdata->pwr_off) + pdata->pwr_off(pdata); + break; + } +} + +/* Routine to configure clock values. Exposed API to core */ +static void aml_sdio_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct amlsd_platform *pdata = mmc_priv(mmc); + + if (!pdata->is_in) + return; + + /*set power*/ + aml_sdio_set_power(pdata, ios->power_mode); + + /*set clock*/ + if (ios->clock) + aml_sdio_set_clk_rate(pdata, ios->clock); + + /*set bus width*/ + aml_sdio_set_bus_width(pdata, ios->bus_width); +#if 1 + if (ios->chip_select == MMC_CS_HIGH) + aml_cs_high(mmc); + else if (ios->chip_select == MMC_CS_DONTCARE) + aml_cs_dont_care(mmc); +#endif +} + +static int aml_sdio_get_ro(struct mmc_host *mmc) +{ + struct amlsd_platform *pdata = mmc_priv(mmc); + u32 ro = 0; + + if (pdata->ro) + ro = pdata->ro(pdata); + return ro; +} + +int aml_sdio_get_cd(struct mmc_host *mmc) +{ + struct amlsd_platform *pdata = mmc_priv(mmc); + + return pdata->is_in; /* 0: no inserted 1: inserted */ +} + +#if 0/* def CONFIG_PM */ +static int aml_sdio_suspend(struct platform_device *pdev, pm_message_t state) +{ + int ret = 0; + int i; + struct amlsd_host *host = platform_get_drvdata(pdev); + struct mmc_host *mmc; + struct amlsd_platform *pdata; + + pr_info("***Entered %s:%s\n", __FILE__, __func__); + i = 0; + list_for_each_entry(pdata, &host->sibling, sibling) { + mmc = pdata->mmc; + /* mmc_power_save_host(mmc); */ + ret = mmc_suspend_host(mmc); + if (ret) + break; + i++; + } + + if (ret) { + list_for_each_entry(pdata, &host->sibling, sibling) { + i--; + if (i < 0) + break; + + if (!(pdata->caps & MMC_CAP_NONREMOVABLE)) + aml_sd_uart_detect(pdata); + + mmc = pdata->mmc; + mmc_resume_host(mmc); + } + } + pr_info("***Exited %s:%s\n", __FILE__, __func__); + + return ret; +} + +static int aml_sdio_resume(struct platform_device *pdev) +{ + int ret = 0; + struct amlsd_host *host = platform_get_drvdata(pdev); + struct mmc_host *mmc; + struct amlsd_platform *pdata; + + pr_info("***Entered %s:%s\n", __FILE__, __func__); + list_for_each_entry(pdata, &host->sibling, sibling) { + + /* detect if a card is exist or not if it is removable */ + if (!(pdata->caps & MMC_CAP_NONREMOVABLE)) + aml_sd_uart_detect(pdata); + + mmc = pdata->mmc; + /* mmc_power_restore_host(mmc); */ + ret = mmc_resume_host(mmc); + if (ret) + break; + } + pr_info("***Exited %s:%s\n", __FILE__, __func__); + return ret; +} +#else +#define aml_sdio_suspend NULL +#define aml_sdio_resume NULL +#endif + +static const struct mmc_host_ops aml_sdio_ops = { + .request = aml_sdio_request, + .set_ios = aml_sdio_set_ios, + .enable_sdio_irq = aml_sdio_enable_irq, + .get_cd = aml_sdio_get_cd, + .get_ro = aml_sdio_get_ro, + .hw_reset = aml_emmc_hw_reset, +}; + +static ssize_t sdio_debug_func(struct class *class, + struct class_attribute *attr, const char *buf, size_t count) +{ + count = kstrtoint(buf, 0, &sdio_debug_flag); + pr_info("sdio_debug_flag: %d\n", sdio_debug_flag); + + return count; +} + +static ssize_t show_sdio_debug(struct class *class, + struct class_attribute *attr, char *buf) +{ + pr_info("sdio_debug_flag: %d\n", sdio_debug_flag); + pr_info("1 : Force sdio cmd crc error\n"); + pr_info("2 : Force sdio data crc error\n"); + pr_info("9 : Force sdio irq timeout error\n"); + + return 0; +} + +static struct class_attribute sdio_class_attrs[] = { + __ATTR(debug, 0644, show_sdio_debug, sdio_debug_func), + __ATTR_NULL +}; + +static struct amlsd_host *aml_sdio_init_host(struct amlsd_host *host) +{ + if (request_threaded_irq(host->irq, + (irq_handler_t)aml_sdio_irq, + aml_sdio_irq_thread, + IRQF_SHARED, "sdio", (void *)host)) { + sdio_err("Request SDIO Irq Error!\n"); + return NULL; + } + + host->bn_buf = dma_alloc_coherent(NULL, SDIO_BOUNCE_REQ_SIZE, + &host->bn_dma_buf, GFP_KERNEL); + if (host->bn_buf == NULL) { + sdio_err("Dma alloc Fail!\n"); + return NULL; + } + INIT_DELAYED_WORK(&host->timeout, aml_sdio_timeout); + + spin_lock_init(&host->mrq_lock); + mutex_init(&host->pinmux_lock); + host->xfer_step = XFER_INIT; + + INIT_LIST_HEAD(&host->sibling); + + host->version = AML_MMC_VERSION; + /*host->storage_flag = storage_flag;*/ + host->pinctrl = NULL; + + host->init_flag = 1; + +#ifdef CONFIG_MMC_AML_DEBUG + host->req_cnt = 0; + sdio_err("CONFIG_MMC_AML_DEBUG is on!\n"); +#endif + + host->debug.name = + kzalloc(strlen((const char *)AML_SDIO_MAGIC)+1, GFP_KERNEL); + strcpy((char *)(host->debug.name), (const char *)AML_SDIO_MAGIC); + host->debug.class_attrs = sdio_class_attrs; + if (class_register(&host->debug)) + pr_info(" class register nand_class fail!\n"); + + return host; +} + +static int aml_sdio_probe(struct platform_device *pdev) +{ + struct mmc_host *mmc = NULL; + struct amlsd_host *host = NULL; + struct amlsd_platform *pdata; + struct resource *res_mem; + int size; + int ret = 0, i; + + pr_info("%s() begin!\n", __func__); + + host = kzalloc(sizeof(struct amlsd_host), GFP_KERNEL); + if (!host) + return -ENODEV; + + aml_mmc_ver_msg_show(); + + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res_mem) { + pr_info("error to get IORESOURCE\n"); + goto fail_init_host; + } + size = resource_size(res_mem); + + host->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); + pr_info("host->irq = %d\n", host->irq); + host->pinmux_base = ioremap(0xc1108000, 0x200); + host->base = devm_ioremap_nocache(&pdev->dev, res_mem->start, size); + + aml_sdio_init_host(host); + /* if (amlsd_get_reg_base(pdev, host)) + * goto fail_init_host; + */ + + host->pdev = pdev; + host->dev = &pdev->dev; + platform_set_drvdata(pdev, host); + /* init sdio reg here */ + aml_sdio_init_param(host); + + // for (i = 0; i < MMC_MAX_DEVICE; i++) { + for (i = 0; i < 1; i++) { + /*malloc extra amlsd_platform*/ + mmc = mmc_alloc_host(sizeof(struct amlsd_platform), &pdev->dev); + if (!mmc) { + ret = -ENOMEM; + goto probe_free_host; + } + + pdata = mmc_priv(mmc); + memset(pdata, 0, sizeof(struct amlsd_platform)); + if (amlsd_get_platform_data(pdev, pdata, mmc, i)) { + mmc_free_host(mmc); + break; + } +#if 0 + /* if(pdata->parts){ */ + if (pdata->port == PORT_SDIO_C) { + if (is_emmc_exist(host)) { + mmc->is_emmc_port = 1; + /* add_part_table(pdata->parts,*/ + /*pdata->nr_parts);*/ + /* mmc->add_part = add_emmc_partition; */ + } else { /* there is not eMMC/tsd */ + pr_info( + "[%s]:not eMMC/tsd,skip sdio_c dts config!\n", + __func__); + i++; /* skip the port written in the dts */ + memset(pdata, 0, sizeof(struct amlsd_platform)); + if (amlsd_get_platform_data(pdev, + pdata, mmc, i)) { + mmc_free_host(mmc); + break; + } + } + } +#endif + dev_set_name(&mmc->class_dev, "%s", pdata->pinname); + if (pdata->caps & MMC_CAP_NONREMOVABLE) + pdata->is_in = true; + + if (pdata->caps & MMC_PM_KEEP_POWER) + mmc->pm_caps |= MMC_PM_KEEP_POWER; + + if (pdata->caps & MMC_CAP_SDIO_IRQ) + SDIO_IRQ_SUPPORT = 1; + + pdata->host = host; + pdata->mmc = mmc; + pdata->is_fir_init = true; + + /* mmc->index = i;*/ + /* mmc->alldev_claim = &aml_sdio_claim;*/ + mmc->ops = &aml_sdio_ops; + mmc->ios.clock = 400000; + mmc->ios.bus_width = MMC_BUS_WIDTH_1; + mmc->max_blk_count = 4095; + mmc->max_blk_size = 4095; + mmc->max_req_size = pdata->max_req_size; + mmc->max_seg_size = mmc->max_req_size; + mmc->max_segs = 1024; + mmc->ocr_avail = pdata->ocr_avail; + /* mmc->ocr = pdata->ocr_avail;*/ + mmc->caps = pdata->caps; + mmc->caps2 = pdata->caps2; + mmc->f_min = pdata->f_min; + mmc->f_max = pdata->f_max; + + if (aml_card_type_sdio(pdata)) /* if sdio_wifi */ + mmc->rescan_entered = 1; + /* do NOT run mmc_rescan for the first time */ + else + mmc->rescan_entered = 0; + + if (pdata->port_init) + pdata->port_init(pdata); + /* aml_sduart_pre(pdata);*/ + + ret = mmc_add_host(mmc); + if (ret) { /* error */ + sdhc_err("Failed to add mmc host.\n"); + goto probe_free_host; + } else { /* ok */ + if (aml_card_type_sdio(pdata)) { /* if sdio_wifi */ + sdio_host = mmc; + /* mmc->rescan_entered = 1; */ + /* do NOT run mmc_rescan for the first time */ + } + } + + /* aml_sdio_init_debugfs(mmc);*/ + /*Add each mmc host pdata to this controller host list*/ + INIT_LIST_HEAD(&pdata->sibling); + list_add_tail(&pdata->sibling, &host->sibling); + + /*Register card detect irq : plug in & unplug*/ + if (pdata->gpio_cd + && aml_card_type_non_sdio(pdata)) { + pdata->irq_init(pdata); + mutex_init(&pdata->in_out_lock); + ret = request_threaded_irq(pdata->irq_cd, + aml_sd_irq_cd, aml_irq_cd_thread, + IRQF_TRIGGER_RISING + | IRQF_TRIGGER_FALLING + | IRQF_ONESHOT, + "sdio_mmc_cd", pdata); + if (ret) { + sd_emmc_err("Failed to request SD IN detect\n"); + goto probe_free_host; + } + } + } + + print_tmp("%s() success!\n", __func__); + return 0; + +probe_free_host: + list_for_each_entry(pdata, &host->sibling, sibling) { + mmc = pdata->mmc; + mmc_remove_host(mmc); + mmc_free_host(mmc); + } +fail_init_host: + iounmap(host->base); + free_irq(host->irq, host); + dma_free_coherent(NULL, SDIO_BOUNCE_REQ_SIZE, host->bn_buf, + (dma_addr_t)host->bn_dma_buf); + kfree(host); + print_tmp("aml_sdio_probe() fail!\n"); + return ret; + } + + int aml_sdio_remove(struct platform_device *pdev) + { + struct amlsd_host *host = platform_get_drvdata(pdev); + struct mmc_host *mmc; + struct amlsd_platform *pdata; + + dma_free_coherent(NULL, SDIO_BOUNCE_REQ_SIZE, host->bn_buf, + (dma_addr_t)host->bn_dma_buf); + + free_irq(host->irq, host); + iounmap(host->base); + + list_for_each_entry(pdata, &host->sibling, sibling) { + mmc = pdata->mmc; + mmc_remove_host(mmc); + mmc_free_host(mmc); + } + + /* aml_devm_pinctrl_put(host);*/ + + kfree(host); + return 0; + } + + static const struct of_device_id aml_sdio_dt_match[] = { + { + .compatible = "amlogic, aml_sdio", + }, + {}, + }; + + MODULE_DEVICE_TABLE(of, aml_sdio_dt_match); + + static struct platform_driver aml_sdio_driver = { + .probe = aml_sdio_probe, + .remove = aml_sdio_remove, + .suspend = aml_sdio_suspend, + .resume = aml_sdio_resume, + .driver = { + .name = "aml_sdio", + .owner = THIS_MODULE, + .of_match_table = aml_sdio_dt_match, + }, + }; + + static int __init aml_sdio_init(void) + { + return platform_driver_register(&aml_sdio_driver); + } + + static void __exit aml_sdio_cleanup(void) + { + platform_driver_unregister(&aml_sdio_driver); + } + + module_init(aml_sdio_init); + module_exit(aml_sdio_cleanup); + + MODULE_DESCRIPTION("Amlogic SDIO Controller driver"); + MODULE_LICENSE("GPL"); + diff --git a/drivers/amlogic/mmc/amlsd.c b/drivers/amlogic/mmc/amlsd.c index 07c4377fb43f..8eb0e3005fd8 100644 --- a/drivers/amlogic/mmc/amlsd.c +++ b/drivers/amlogic/mmc/amlsd.c @@ -37,8 +37,10 @@ #include #include #include -/* #include */ #include +#ifdef CONFIG_AMLOGIC_M8B_MMC +#include +#endif const u8 tuning_blk_pattern_4bit[64] = { 0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc, @@ -109,9 +111,19 @@ static int aml_is_card_insert(struct amlsd_platform *pdata) return ret; } + +#ifdef CONFIG_AMLOGIC_M8B_MMC +int aml_sd_uart_detect(struct amlsd_platform *pdata) +#else int aml_sd_uart_detect(struct amlsd_host *host) +#endif { +#ifdef CONFIG_AMLOGIC_M8B_MMC + struct mmc_host *mmc = pdata->mmc; +#else struct amlsd_platform *pdata = host->pdata; + struct mmc_host *mmc = host->mmc; +#endif if (aml_is_card_insert(pdata)) { if (pdata->is_in) @@ -119,7 +131,7 @@ int aml_sd_uart_detect(struct amlsd_host *host) pdata->is_in = true; pr_info("normal card in\n"); if (pdata->caps & MMC_CAP_4_BIT_DATA) - host->mmc->caps |= MMC_CAP_4_BIT_DATA; + mmc->caps |= MMC_CAP_4_BIT_DATA; } else { if (!pdata->is_in) return 1; @@ -127,14 +139,14 @@ int aml_sd_uart_detect(struct amlsd_host *host) pr_info("card out\n"); pdata->is_tuned = false; - if (host->mmc && host->mmc->card) - mmc_card_set_removed(host->mmc->card); + if (mmc && mmc->card) + mmc_card_set_removed(mmc->card); /* switch to 3.3V */ - aml_sd_voltage_switch(host->mmc, + aml_sd_voltage_switch(mmc, MMC_SIGNAL_VOLTAGE_330); if (pdata->caps & MMC_CAP_4_BIT_DATA) - host->mmc->caps |= MMC_CAP_4_BIT_DATA; + mmc->caps |= MMC_CAP_4_BIT_DATA; } return 0; } @@ -142,8 +154,15 @@ int aml_sd_uart_detect(struct amlsd_host *host) static int card_dealed; irqreturn_t aml_irq_cd_thread(int irq, void *data) { +#ifdef CONFIG_AMLOGIC_M8B_MMC + struct amlsd_platform *pdata = (struct amlsd_platform *)data; + struct mmc_host *mmc = pdata->mmc; + struct amlsd_host *host = pdata->host; +#else struct amlsd_host *host = (struct amlsd_host *)data; struct amlsd_platform *pdata = host->pdata; + struct mmc_host *mmc = host->mmc; +#endif int ret = 0; mutex_lock(&pdata->in_out_lock); @@ -152,7 +171,11 @@ irqreturn_t aml_irq_cd_thread(int irq, void *data) mutex_unlock(&pdata->in_out_lock); return IRQ_HANDLED; } +#ifdef CONFIG_AMLOGIC_M8B_MMC + ret = aml_sd_uart_detect(pdata); +#else ret = aml_sd_uart_detect(host); +#endif if (ret == 1) {/* the same as the last*/ mutex_unlock(&pdata->in_out_lock); return IRQ_HANDLED; @@ -164,9 +187,9 @@ irqreturn_t aml_irq_cd_thread(int irq, void *data) /* mdelay(500); */ if (pdata->is_in) - mmc_detect_change(host->mmc, msecs_to_jiffies(100)); + mmc_detect_change(mmc, msecs_to_jiffies(100)); else - mmc_detect_change(host->mmc, msecs_to_jiffies(0)); + mmc_detect_change(mmc, msecs_to_jiffies(0)); card_dealed = 0; return IRQ_HANDLED; @@ -180,7 +203,12 @@ irqreturn_t aml_sd_irq_cd(int irq, void *dev_id) static int aml_cmd_invalid(struct mmc_host *mmc, struct mmc_request *mrq) { +#ifdef CONFIG_AMLOGIC_M8B_MMC + struct amlsd_platform *pdata = mmc_priv(mmc); + struct amlsd_host *host = pdata->host; +#else struct amlsd_host *host = mmc_priv(mmc); +#endif unsigned long flags; spin_lock_irqsave(&host->mrq_lock, flags); @@ -193,7 +221,12 @@ static int aml_cmd_invalid(struct mmc_host *mmc, struct mmc_request *mrq) static int aml_rpmb_cmd_invalid(struct mmc_host *mmc, struct mmc_request *mrq) { +#ifdef CONFIG_AMLOGIC_M8B_MMC + struct amlsd_platform *pdata = mmc_priv(mmc); + struct amlsd_host *host = pdata->host; +#else struct amlsd_host *host = mmc_priv(mmc); +#endif unsigned long flags; spin_lock_irqsave(&host->mrq_lock, flags); @@ -208,8 +241,12 @@ static int aml_rpmb_cmd_invalid(struct mmc_host *mmc, struct mmc_request *mrq) int aml_check_unsupport_cmd(struct mmc_host *mmc, struct mmc_request *mrq) { +#ifdef CONFIG_AMLOGIC_M8B_MMC + struct amlsd_platform *pdata = mmc_priv(mmc); +#else struct amlsd_host *host = mmc_priv(mmc); struct amlsd_platform *pdata = host->pdata; +#endif u32 opcode, arg; opcode = mrq->cmd->opcode; @@ -276,6 +313,7 @@ int aml_check_unsupport_cmd(struct mmc_host *mmc, struct mmc_request *mrq) int aml_sd_voltage_switch(struct mmc_host *mmc, char signal_voltage) { +#ifndef CONFIG_AMLOGIC_M8B_MMC struct amlsd_host *host = mmc_priv(mmc); struct amlsd_platform *pdata = host->pdata; int ret = 0; @@ -311,12 +349,41 @@ int aml_sd_voltage_switch(struct mmc_host *mmc, char signal_voltage) return -EINVAL; host->sd_sdio_switch_volat_done = 1; +#endif return 0; } /* boot9 here */ void aml_emmc_hw_reset(struct mmc_host *mmc) { +#ifdef CONFIG_AMLOGIC_M8B_MMC + struct amlsd_platform *pdata = mmc_priv(mmc); + void __iomem *hw_ctrl; + + if (!aml_card_type_mmc(pdata)) + return; + hw_ctrl = ioremap(P_PREG_PAD_GPIO3_EN_N, 0x200); + + //boot_9 used as eMMC hw_rst pin here. + + //clr nand ce1 pinmux + aml_clr_reg32_mask((hw_ctrl + (0x19 << 2)), (1<<24)); + + //set out + aml_clr_reg32_mask(hw_ctrl, (1<<9)); + + //high + aml_set_reg32_mask((hw_ctrl + (0x1 << 2)), (1<<9)); + mdelay(1); + + //low + aml_clr_reg32_mask((hw_ctrl + (0x1 << 2)), (1<<9)); + mdelay(2); + + //high + aml_set_reg32_mask((hw_ctrl + (0x1 << 2)), (1<<9)); + mdelay(1); +#else struct amlsd_host *host = mmc_priv(mmc); struct amlsd_platform *pdata = host->pdata; u32 ret; @@ -350,6 +417,7 @@ void aml_emmc_hw_reset(struct mmc_host *mmc) return; } mdelay(2); +#endif } static void sdio_rescan(struct mmc_host *mmc) @@ -397,12 +465,37 @@ int of_amlsd_init(struct amlsd_platform *pdata) if (pdata->gpio_cd) { pr_info("gpio_cd = %x\n", pdata->gpio_cd); ret = gpio_request_one(pdata->gpio_cd, - GPIOF_IN, MODULE_NAME); + GPIOF_IN, MODULE_NAME); CHECK_RET(ret); } +#if 0 + if (pdata->gpio_ro) { + ret = amlogic_gpio_request_one(pdata->gpio_ro, + GPIOF_IN, MODULE_NAME); + if (!ret) { // ok + /* 0:pull down, 1:pull up */ + ret = amlogic_set_pull_up_down(pdata->gpio_ro, + 1, MODULE_NAME); + CHECK_RET(ret); + } else { + sdio_err("request gpio_ro pin fail!\n"); + } + } +#endif + if (pdata->gpio_power) { + if (pdata->power_level) { + ret = gpio_request_one(pdata->gpio_power, + GPIOF_OUT_INIT_LOW, MODULE_NAME); + CHECK_RET(ret); + } else { + ret = gpio_request_one(pdata->gpio_power, + GPIOF_OUT_INIT_HIGH, MODULE_NAME); + CHECK_RET(ret); + } + } /* if(pdata->port == MESON_SDIO_PORT_A) */ - /* wifi_setup_dt(); */ + /* wifi_setup_dt(); */ return 0; } @@ -452,14 +545,24 @@ static struct pinctrl * __must_check aml_devm_pinctrl_get_select( void of_amlsd_xfer_pre(struct mmc_host *mmc) { +#ifdef CONFIG_AMLOGIC_M8B_MMC + struct amlsd_platform *pdata = mmc_priv(mmc); + struct amlsd_host *host = pdata->host; +#else struct amlsd_host *host = mmc_priv(mmc); struct amlsd_platform *pdata = host->pdata; +#endif char pinctrl[30]; char *p = pinctrl; int i, size = 0; struct pinctrl *ppin; size = sizeof(pinctrl); +#ifdef CONFIG_AMLOGIC_M8B_MMC + if (pdata->port > PORT_SDIO_C) // so it should be PORT_SDHC_X + aml_snprint(&p, &size, "sdhc_"); +#endif + if (mmc->ios.chip_select == MMC_CS_DONTCARE) { if ((mmc->caps & MMC_CAP_4_BIT_DATA) || (strcmp(pdata->pinname, "sd")) @@ -542,8 +645,13 @@ void aml_snprint (char **pp, int *left_size, const char *fmt, ...) void aml_cs_high(struct mmc_host *mmc) /* chip select high */ { int ret; +#ifdef CONFIG_AMLOGIC_M8B_MMC + struct amlsd_platform *pdata = mmc_priv(mmc); + struct amlsd_host *host = pdata->host; +#else struct amlsd_host *host = mmc_priv(mmc); struct amlsd_platform *pdata = host->pdata; +#endif if ((mmc->ios.chip_select == MMC_CS_HIGH) && (pdata->gpio_dat3 != 0)) { @@ -560,8 +668,12 @@ void aml_cs_high(struct mmc_host *mmc) /* chip select high */ void aml_cs_dont_care(struct mmc_host *mmc) { +#ifdef CONFIG_AMLOGIC_M8B_MMC + struct amlsd_platform *pdata = mmc_priv(mmc); +#else struct amlsd_host *host = mmc_priv(mmc); struct amlsd_platform *pdata = host->pdata; +#endif if ((mmc->ios.chip_select == MMC_CS_DONTCARE) && (pdata->gpio_dat3 != 0) @@ -585,3 +697,157 @@ void of_amlsd_pwr_off(struct amlsd_platform *pdata) gpio_set_value(pdata->gpio_power, !pdata->power_level); } +#ifdef CONFIG_AMLOGIC_M8B_MMC +/*-----------sg copy buffer------------*/ +/** + * aml_sg_miter_stop - stop mapping iteration for amlogic, + * We don't disable irq in this function + */ +static void aml_sg_miter_stop(struct sg_mapping_iter *miter) +{ + WARN_ON(miter->consumed > miter->length); + + /* drop resources from the last iteration */ + if (miter->addr) { + miter->__offset += miter->consumed; + miter->__remaining -= miter->consumed; + + if (miter->__flags & SG_MITER_TO_SG) + flush_kernel_dcache_page(miter->page); + + if (miter->__flags & SG_MITER_ATOMIC) { + WARN_ON_ONCE(preemptible()); + kunmap_atomic(miter->addr); + } else + kunmap(miter->page); + + miter->page = NULL; + miter->addr = NULL; + miter->length = 0; + miter->consumed = 0; + } +} + +/** + * aml_sg_miter_next - proceed mapping iterator to the next mapping for amlogic, + * We don't disable irq in this function + */ +static bool aml_sg_miter_next(struct sg_mapping_iter *miter) +{ + unsigned long flags; + + sg_miter_stop(miter); + + /* + * Get to the next page if necessary. + * __remaining, __offset is adjusted by sg_miter_stop + */ + if (!miter->__remaining) { + struct scatterlist *sg; + unsigned long pgoffset; + + if (!__sg_page_iter_next(&miter->piter)) + return false; + + sg = miter->piter.sg; + pgoffset = miter->piter.sg_pgoffset; + + miter->__offset = pgoffset ? 0 : sg->offset; + miter->__remaining = sg->offset + sg->length - + (pgoffset << PAGE_SHIFT) - miter->__offset; + miter->__remaining = min_t(unsigned long, miter->__remaining, + PAGE_SIZE - miter->__offset); + } + miter->page = sg_page_iter_page(&miter->piter); + miter->consumed = miter->length = miter->__remaining; + + if (miter->__flags & SG_MITER_ATOMIC) { + /*pr_info(KERN_DEBUG "AML_SDHC miter_next highmem\n"); */ + local_irq_save(flags); + miter->addr = kmap_atomic(miter->page) + miter->__offset; + local_irq_restore(flags); + } else + miter->addr = kmap(miter->page) + miter->__offset; + return true; +} + +/* + * aml_sg_copy_buffer - Copy data between a linear buffer + * and an SG list for amlogic, + * We don't disable irq in this function + */ +EXPORT_SYMBOL(aml_sg_copy_buffer); +size_t aml_sg_copy_buffer(struct scatterlist *sgl, unsigned int nents, + void *buf, size_t buflen, int to_buffer) +{ + unsigned int offset = 0; + struct sg_mapping_iter miter; + unsigned int sg_flags = SG_MITER_ATOMIC; + unsigned long flags; + + if (to_buffer) + sg_flags |= SG_MITER_FROM_SG; + else + sg_flags |= SG_MITER_TO_SG; + + sg_miter_start(&miter, sgl, nents, sg_flags); + local_irq_save(flags); + + while (aml_sg_miter_next(&miter) && offset < buflen) { + unsigned int len; + + len = min(miter.length, buflen - offset); + + if (to_buffer) + memcpy(buf + offset, miter.addr, len); + else + memcpy(miter.addr, buf + offset, len); + + offset += len; + } + + aml_sg_miter_stop(&miter); + local_irq_restore(flags); + + return offset; +} + +/*-------------------eMMC/tSD-------------------*/ +int storage_flag; + +bool is_emmc_exist(struct amlsd_host *host) // is eMMC/tSD exist +{ + print_tmp("host->storage_flag=%d, POR_BOOT_VALUE=%d\n", + host->storage_flag, POR_BOOT_VALUE); + if ((host->storage_flag == EMMC_BOOT_FLAG) + || (host->storage_flag == SPI_EMMC_FLAG) + || (((host->storage_flag == 0) + || (host->storage_flag == -1)) + && (POR_EMMC_BOOT() || POR_SPI_BOOT()))) + return true; + + return false; +} + +/*-------------------debug---------------------*/ + +unsigned long sdhc_debug; // 0xffffffff; +static int __init sdhc_debug_setup(char *str) +{ + ssize_t status = 0; + + status = kstrtol(str, 0, &sdhc_debug); + return 1; +} +__setup("sdhc_debug=", sdhc_debug_setup); + +unsigned long sdio_debug; // 0xffffff; +static int __init sdio_debug_setup(char *str) +{ + ssize_t status = 0; + + status = kstrtol(str, 0, &sdio_debug); + return 1; +} +__setup("sdio_debug=", sdio_debug_setup); +#endif diff --git a/drivers/amlogic/mmc/amlsd_of.c b/drivers/amlogic/mmc/amlsd_of.c index 6667130ba7a5..0282cf0738c6 100644 --- a/drivers/amlogic/mmc/amlsd_of.c +++ b/drivers/amlogic/mmc/amlsd_of.c @@ -25,11 +25,10 @@ #include #include #include -/*#include */ -/*#include */ #include #include #include + unsigned int sd_emmc_debug; static const struct sd_caps host_caps[] = { @@ -119,7 +118,8 @@ static int amlsd_get_host_caps2(struct device_node *of_node, return 0; } -int amlsd_get_platform_data(struct amlsd_platform *pdata, +int amlsd_get_platform_data(struct platform_device *pdev, + struct amlsd_platform *pdata, struct mmc_host *mmc, u32 index) { struct device_node *of_node; @@ -127,10 +127,13 @@ int amlsd_get_platform_data(struct amlsd_platform *pdata, u32 i, prop; const char *str = "none"; - if (!mmc->parent || !mmc->parent->of_node) +#ifdef CONFIG_AMLOGIC_M8B_MMC + of_node = pdev->dev.of_node; +#else + if (!mmc->parent) return 0; - of_node = mmc->parent->of_node; +#endif if (of_node) { child = of_node->child; WARN_ON(!child); @@ -151,8 +154,8 @@ int amlsd_get_platform_data(struct amlsd_platform *pdata, prop, pdata->f_min); SD_PARSE_U32_PROP_DEC(child, "f_max", prop, pdata->f_max); - SD_PARSE_U32_PROP_HEX(child, "max_req_size", prop, - pdata->max_req_size); + SD_PARSE_U32_PROP_HEX(child, "max_req_size", + prop, pdata->max_req_size); SD_PARSE_GPIO_NUM_PROP(child, "gpio_cd", str, pdata->gpio_cd); SD_PARSE_GPIO_NUM_PROP(child, "gpio_ro", @@ -177,20 +180,22 @@ int amlsd_get_platform_data(struct amlsd_platform *pdata, prop, pdata->vol_switch_delay); SD_PARSE_U32_PROP_DEC(child, "card_type", prop, pdata->card_type); - if (aml_card_type_mmc(pdata)) { - /*tx_phase set default value first*/ - if (get_cpu_type() == MESON_CPU_MAJOR_ID_GXTVBB) - pdata->tx_phase = 1; - if (get_cpu_type() == MESON_CPU_MAJOR_ID_TXL) - pdata->tx_delay = 3; - SD_PARSE_U32_PROP_DEC(child, "tx_phase", - prop, pdata->tx_phase); - } - if (aml_card_type_non_sdio(pdata)) { - /*card in default value*/ - pdata->card_in_delay = 0; - SD_PARSE_U32_PROP_DEC(child, "card_in_delay", - prop, pdata->card_in_delay); + if (get_cpu_type() > MESON_CPU_MAJOR_ID_M8B) { + if (aml_card_type_mmc(pdata)) { + /*tx_phase set default value first*/ + if (get_cpu_type() == MESON_CPU_MAJOR_ID_GXTVBB) + pdata->tx_phase = 1; + if (get_cpu_type() == MESON_CPU_MAJOR_ID_TXL) + pdata->tx_delay = 3; + SD_PARSE_U32_PROP_DEC(child, "tx_phase", + prop, pdata->tx_phase); + } + if (aml_card_type_non_sdio(pdata)) { + /*card in default value*/ + pdata->card_in_delay = 0; + SD_PARSE_U32_PROP_DEC(child, "card_in_delay", + prop, pdata->card_in_delay); + } } SD_PARSE_GPIO_NUM_PROP(child, "hw_reset", str, pdata->hw_reset); @@ -209,4 +214,3 @@ int amlsd_get_platform_data(struct amlsd_platform *pdata, return 0; } - diff --git a/include/linux/amlogic/amlsd.h b/include/linux/amlogic/amlsd.h index a1a8748a0d40..9323c5cda6ed 100644 --- a/include/linux/amlogic/amlsd.h +++ b/include/linux/amlogic/amlsd.h @@ -26,8 +26,8 @@ #define AML_MMC_VER_MESSAGE \ "2015-01-21: fix a bug in tuning which caused eMMC data CRC error" -extern unsigned int sdhc_debug; -extern unsigned int sdio_debug; +extern unsigned long sdhc_debug; +extern unsigned long sdio_debug; extern unsigned int sd_emmc_debug; extern const u8 tuning_blk_pattern_4bit[64]; extern const u8 tuning_blk_pattern_8bit[128]; @@ -49,8 +49,8 @@ extern const u8 tuning_blk_pattern_8bit[128]; #define LDO4DAC_REG_1_8_V 0x24 #define LDO4DAC_REG_2_8_V 0x4c #define LDO4DAC_REG_3_3_V 0x60 - #endif + #define AMLSD_DBG_COMMON (1<<0) #define AMLSD_DBG_REQ (1<<1) #define AMLSD_DBG_RESP (1<<2) @@ -73,6 +73,12 @@ extern const u8 tuning_blk_pattern_8bit[128]; #define EMMC_DAT3_PINMUX_CLR 0 #define EMMC_DAT3_PINMUX_SET 1 +#ifdef CONFIG_AMLOGIC_M8B_MMC +#define P_PERIPHS_PIN_MUX_2 (0xc1100000 + (0x202e << 2)) +#define P_PREG_PAD_GPIO3_EN_N (0xc1100000 + (0x2015 << 2)) +#define P_PREG_PAD_GPIO3_O (0xc1100000 + (0x2016 << 2)) +#endif + #define CHECK_RET(ret) { \ if (ret) \ pr_info("[%s] gpio op failed(%d) at line %d\n",\ @@ -171,10 +177,13 @@ extern void aml_sd_emmc_print_reg(struct amlsd_host *host); extern int add_part_table(struct mtd_partition *part, unsigned int nr_part); extern int add_emmc_partition(struct gendisk *disk); +#endif +#ifdef CONFIG_AMLOGIC_M8B_MMC extern size_t aml_sg_copy_buffer(struct scatterlist *sgl, unsigned int nents, void *buf, size_t buflen, int to_buffer); #endif -int amlsd_get_platform_data(struct amlsd_platform *pdata, +int amlsd_get_platform_data(struct platform_device *pdev, + struct amlsd_platform *pdata, struct mmc_host *mmc, u32 index); void of_amlsd_irq_init(struct amlsd_platform *pdata); diff --git a/include/linux/amlogic/cpu_version.h b/include/linux/amlogic/cpu_version.h index 6bf8288aa6fa..476926abf655 100644 --- a/include/linux/amlogic/cpu_version.h +++ b/include/linux/amlogic/cpu_version.h @@ -18,6 +18,7 @@ #ifndef __PLAT_MESON_CPU_H #define __PLAT_MESON_CPU_H +#define MESON_CPU_MAJOR_ID_M8B 0x1B #define MESON_CPU_MAJOR_ID_GXBB 0x1F #define MESON_CPU_MAJOR_ID_GXTVBB 0x20 #define MESON_CPU_MAJOR_ID_GXL 0x21 diff --git a/include/linux/amlogic/sd.h b/include/linux/amlogic/sd.h index 750288df90ad..19a142f294bd 100644 --- a/include/linux/amlogic/sd.h +++ b/include/linux/amlogic/sd.h @@ -159,6 +159,10 @@ struct amlsd_platform { #define PORT_SDHC_B 4 #define PORT_SDHC_C 5 +#ifdef CONFIG_AMLOGIC_M8B_MMC + unsigned int width; + unsigned int tune_phase; /* store tuning result */ +#endif unsigned int caps; unsigned int caps2; unsigned int card_capacity; @@ -291,6 +295,9 @@ struct amlsd_host { unsigned long mux_parent_rate[MUX_CLK_NUM_PARENTS]; struct clk_divider cfg_div; struct clk *cfg_div_clk; +#ifdef CONFIG_AMLOGIC_M8B_MMC + struct clk *div3_clk; +#endif struct resource *mem; struct sd_emmc_regs *sd_emmc_regs;