From a0de710366b96569a70bb9c2d8484ea6a645e41f Mon Sep 17 00:00:00 2001 From: Nan Li Date: Thu, 9 Feb 2017 11:28:31 +0800 Subject: [PATCH] emmc: add sd emmc driver PD#138714: add sd/emmc driver support 1 add emmc hs200/hs400 mode 2 add sd highspeed mode 3 add sd hotplug detect 4 add emmc clock tree supported 5 add sdio get_wifi_inf & sdio_reinit 6 add partitions on dts 7 add sd/emmc pinmux set Change-Id: I449e61517844cb4cb9ad3aaa2f79c911e8658356 Signed-off-by: Nan Li --- MAINTAINERS | 23 + arch/arm64/boot/dts/amlogic/gxl_p212_1g.dts | 220 ++ arch/arm64/boot/dts/amlogic/gxl_p212_2g.dts | 218 ++ arch/arm64/boot/dts/amlogic/gxm_q200_2g.dts | 220 ++ arch/arm64/boot/dts/amlogic/gxm_skt.dts | 255 +- arch/arm64/boot/dts/amlogic/mesongxl.dtsi | 59 +- arch/arm64/boot/dts/amlogic/mesongxm.dtsi | 59 +- arch/arm64/configs/meson64_defconfig | 4 + drivers/amlogic/Kconfig | 2 + drivers/amlogic/Makefile | 3 + drivers/amlogic/mmc/Kconfig | 19 + drivers/amlogic/mmc/Makefile | 5 + drivers/amlogic/mmc/aml_sd_emmc.c | 2921 +++++++++++++++++++ drivers/amlogic/mmc/amlsd.c | 542 ++++ drivers/amlogic/mmc/amlsd_of.c | 210 ++ drivers/amlogic/mmc/emmc_partitions.c | 1011 +++++++ drivers/mmc/card/block.c | 8 + drivers/mmc/core/bus.c | 27 + drivers/mmc/core/core.c | 3 + drivers/mmc/core/mmc.c | 24 + drivers/mmc/core/sdio.c | 40 + include/linux/amlogic/amlsd.h | 229 ++ include/linux/amlogic/sd.h | 1507 ++++++++++ include/linux/mmc/emmc_partitions.h | 78 + include/linux/mmc/host.h | 10 +- 25 files changed, 7641 insertions(+), 56 deletions(-) create mode 100644 drivers/amlogic/mmc/Kconfig create mode 100644 drivers/amlogic/mmc/Makefile create mode 100644 drivers/amlogic/mmc/aml_sd_emmc.c create mode 100644 drivers/amlogic/mmc/amlsd.c create mode 100644 drivers/amlogic/mmc/amlsd_of.c create mode 100644 drivers/amlogic/mmc/emmc_partitions.c create mode 100644 include/linux/amlogic/amlsd.h create mode 100644 include/linux/amlogic/sd.h create mode 100644 include/linux/mmc/emmc_partitions.h diff --git a/MAINTAINERS b/MAINTAINERS index 4174d193e547..6c1397abb5fd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13578,3 +13578,26 @@ F: include/uapi/linux/rc_common.h F: arch/arm64/boot/dts/amlogic/mesongxl.dtsi F: arch/arm64/boot/dts/amlogic/mesongxm.dtsi +AMLOGIC SD/MMC DIRVER SUPPORT +M: Nan Li +F: arch/arm64/boot/dts/amlogic/gxl_p212_1g.dts +F: arch/arm64/boot/dts/amlogic/gxl_p212_2g.dts +F: arch/arm64/boot/dts/amlogic/mesongxl.dtsi +F: arch/arm64/configs/meson64_defconfig +F: drivers/amlogic/Kconfig +F: drivers/amlogic/Makefile +F: drivers/amlogic/mmc/Kconfig +F: drivers/amlogic/mmc/Makefile +F: drivers/amlogic/mmc/aml_sd_emmc.c +F: drivers/amlogic/mmc/emmc_partitions.c +F: drivers/amlogic/mmc/amlsd_of.c +F: drivers/amlogic/mmc/amlsd.c +F: drivers/mmc/card/block.c +F: drivers/mmc/core/bus.c +F: drivers/mmc/core/core.c +F: drivers/mmc/core/mmc.c +F: include/linux/amlogic/amlsd.h +F: include/linux/amlogic/sd.h +F: include/linux/mmc/emmc_partitions.h +F: include/linux/mmc/host.h + diff --git a/arch/arm64/boot/dts/amlogic/gxl_p212_1g.dts b/arch/arm64/boot/dts/amlogic/gxl_p212_1g.dts index 6202f2532022..dbeb224b29fd 100644 --- a/arch/arm64/boot/dts/amlogic/gxl_p212_1g.dts +++ b/arch/arm64/boot/dts/amlogic/gxl_p212_1g.dts @@ -107,11 +107,156 @@ }; }; + sd_emmc_c: emmc@d0074000 { + status = "okay"; + compatible = "amlogic, meson-aml-mmc"; + reg = <0x0 0xd0074000 0x0 0x2000>; + interrupts = <0 218 1>; + pinctrl-names = "emmc_clk_cmd_pins", "emmc_all_pins"; + pinctrl-0 = <&emmc_clk_cmd_pins>; + pinctrl-1 = <&emmc_conf_pull_up &emmc_conf_pull_done>; + clocks = <&clkc CLKID_SD_EMMC_C>, + <&clkc CLKID_SD_EMMC_C_P0_COMP>, + <&clkc CLKID_FCLK_DIV2>; + clock-names = "core", "clkin0", "clkin1"; + + bus-width = <8>; + cap-sd-highspeed; + cap-mmc-highspeed; + mmc-ddr-1_8v; + mmc-hs200-1_8v; + + max-frequency = <200000000>; + non-removable; + disable-wp; + emmc { + status = "disabled"; + pinname = "emmc"; + ocr_avail = <0x200080>; /**VDD voltage 3.3 ~ 3.4 */ + caps = "MMC_CAP_8_BIT_DATA", + "MMC_CAP_MMC_HIGHSPEED", + "MMC_CAP_SD_HIGHSPEED", + "MMC_CAP_NONREMOVABLE", + "MMC_CAP_1_8V_DDR", + "MMC_CAP_HW_RESET", + "MMC_CAP_ERASE", + "MMC_CAP_CMD23"; + caps2 = "MMC_CAP2_HS200", "MMC_CAP2_HS400"; + f_min = <300000>; + f_max = <100000000>; + max_req_size = <0x20000>; /**128KB*/ + hw_reset = <&gpio BOOT_9 GPIO_ACTIVE_HIGH>; + card_type = <1>; + /* 1:mmc card(include eMMC), + * 2:sd card(include tSD) + */ + }; + }; + + sd_emmc_b:sd@d0072000 { + status = "okay"; + compatible = "amlogic, meson-aml-mmc"; + reg = <0x0 0xd0072000 0x0 0x2000>; + interrupts = <0 217 1>; + pinctrl-names = "sd_all_pins", + "sd_clk_cmd_pins", + "sd_clk_cmd_uart_pins", + "sd_to_ao_uart_pins", + "ao_to_sd_uart_pins"; + pinctrl-0 = <&sd_all_pins>; + pinctrl-1 = <&sd_clk_cmd_pins>; + pinctrl-2 = <&sd_clk_cmd_pins &ao_to_sd_uart_pins>; + pinctrl-3 = <&sd_to_ao_uart_pins>; + pinctrl-4 = <&ao_to_sd_uart_pins>; + clocks = <&clkc CLKID_SD_EMMC_B>, + <&clkc CLKID_SD_EMMC_B_P0_COMP>, + <&clkc CLKID_FCLK_DIV2>; + clock-names = "core", "clkin0", "clkin1"; + + bus-width = <4>; + cap-sd-highspeed; + max-frequency = <100000000>; + disable-wp; + sd { + status = "disabled"; + pinname = "sd"; + ocr_avail = <0x200080>; /**VDD voltage 3.3 ~ 3.4 */ + caps = "MMC_CAP_4_BIT_DATA", + "MMC_CAP_MMC_HIGHSPEED", + "MMC_CAP_SD_HIGHSPEED"; + /* "MMC_CAP_UHS_SDR12", + * "MMC_CAP_UHS_SDR25", + * "MMC_CAP_UHS_SDR50", + * "MMC_CAP_UHS_SDR104"; + */ + f_min = <400000>; + f_max = <100000000>; + 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>; + 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 + */ + }; + }; + + sd_emmc_a:sdio@d0070000 { + status = "okay"; + compatible = "amlogic, meson-aml-mmc"; + reg = <0x0 0xd0070000 0x0 0x2000>; + interrupts = <0 216 4>; + pinctrl-names = "sdio_clk_cmd_pins", "sdio_all_pins"; + pinctrl-0 = <&sdio_clk_cmd_pins>; + pinctrl-1 = <&sdio_all_pins>; + clocks = <&clkc CLKID_SD_EMMC_A>, + <&clkc CLKID_SD_EMMC_A_P0_COMP>, + <&clkc CLKID_FCLK_DIV2>; + clock-names = "core", "clkin0", "clkin1"; + + bus-width = <4>; + cap-sd-highspeed; + cap-mmc-highspeed; + max-frequency = <100000000>; + non-removable; + disable-wp; + sdio { + status = "disabled"; + pinname = "sdio"; + ocr_avail = <0x200080>; /**VDD voltage 3.3 ~ 3.4 */ + 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", + "MMC_CAP_SDIO_IRQ"; + f_min = <400000>; + f_max = <200000000>; + max_req_size = <0x20000>; /**128KB*/ + card_type = <3>; + /* 3:sdio device(ie:sdio-wifi), + * 4:SD combo (IO+mem) card + */ + }; + }; + uart_AO: serial@c81004c0 { compatible = "amlogic, meson-uart"; reg = <0x0 0xc81004c0 0x0 0x18>; interrupts = <0 193 1>; status = "okay"; + clocks = <&xtal>; + clock-names = "clk_uart"; xtal_tick_en = <1>; fifosize = < 64 >; pinctrl-names = "default"; @@ -364,6 +509,81 @@ interrupts = <0 89 1>; interrupt-names = "rdma"; }; + + partitions: partitions{ + parts = <11>; + part-0 = <&logo>; + part-1 = <&recovery>; + part-2 = <&rsv>; + part-3 = <&tee>; + part-4 = <&crypt>; + part-5 = <&misc>; + part-6 = <&instaboot>; + part-7 = <&boot>; + part-8 = <&system>; + part-9 = <&cache>; + part-10 = <&data>; + + logo:logo{ + pname = "logo"; + size = <0x0 0x2000000>; + mask = <1>; + }; + recovery:recovery{ + pname = "recovery"; + size = <0x0 0x2000000>; + mask = <1>; + }; + rsv:rsv{ + pname = "rsv"; + size = <0x0 0x800000>; + mask = <1>; + }; + tee:tee{ + pname = "tee"; + size = <0x0 0x800000>; + mask = <1>; + }; + crypt:crypt{ + pname = "crypt"; + size = <0x0 0x2000000>; + mask = <1>; + }; + misc:misc{ + pname = "misc"; + size = <0x0 0x2000000>; + mask = <1>; + }; + instaboot:instaboot{ + pname = "instaboot"; + size = <0x0 0x400000>; + mask = <1>; + }; + boot:boot + { + pname = "boot"; + size = <0x0 0x2000000>; + mask = <1>; + }; + system:system + { + pname = "system"; + size = <0x0 0x80000000>; + mask = <1>; + }; + cache:cache + { + pname = "cache"; + size = <0x0 0x20000000>; + mask = <2>; + }; + data:data + { + pname = "data"; + size = <0xffffffff 0xffffffff>; + mask = <4>; + }; + }; }; &efuse { status = "ok"; diff --git a/arch/arm64/boot/dts/amlogic/gxl_p212_2g.dts b/arch/arm64/boot/dts/amlogic/gxl_p212_2g.dts index c86846170cdf..b0d79816e2a3 100644 --- a/arch/arm64/boot/dts/amlogic/gxl_p212_2g.dts +++ b/arch/arm64/boot/dts/amlogic/gxl_p212_2g.dts @@ -108,6 +108,149 @@ }; }; + sd_emmc_c: emmc@d0074000 { + status = "okay"; + compatible = "amlogic, meson-aml-mmc"; + reg = <0x0 0xd0074000 0x0 0x2000>; + interrupts = <0 218 1>; + pinctrl-names = "emmc_clk_cmd_pins", "emmc_all_pins"; + pinctrl-0 = <&emmc_clk_cmd_pins>; + pinctrl-1 = <&emmc_conf_pull_up &emmc_conf_pull_done>; + clocks = <&clkc CLKID_SD_EMMC_C>, + <&clkc CLKID_SD_EMMC_C_P0_COMP>, + <&clkc CLKID_FCLK_DIV2>; + clock-names = "core", "clkin0", "clkin1"; + + bus-width = <8>; + cap-sd-highspeed; + cap-mmc-highspeed; + mmc-ddr-1_8v; + mmc-hs200-1_8v; + + max-frequency = <200000000>; + non-removable; + disable-wp; + emmc { + status = "disabled"; + pinname = "emmc"; + ocr_avail = <0x200080>; /**VDD voltage 3.3 ~ 3.4 */ + caps = "MMC_CAP_8_BIT_DATA", + "MMC_CAP_MMC_HIGHSPEED", + "MMC_CAP_SD_HIGHSPEED", + "MMC_CAP_NONREMOVABLE", + "MMC_CAP_1_8V_DDR", + "MMC_CAP_HW_RESET", + "MMC_CAP_ERASE", + "MMC_CAP_CMD23"; + caps2 = "MMC_CAP2_HS200", "MMC_CAP2_HS400"; + f_min = <300000>; + f_max = <100000000>; + max_req_size = <0x20000>; /**128KB*/ + hw_reset = <&gpio BOOT_9 GPIO_ACTIVE_HIGH>; + card_type = <1>; + /* 1:mmc card(include eMMC), + * 2:sd card(include tSD) + */ + }; + }; + + sd_emmc_b:sd@d0072000 { + status = "okay"; + compatible = "amlogic, meson-aml-mmc"; + reg = <0x0 0xd0072000 0x0 0x2000>; + interrupts = <0 217 1>; + pinctrl-names = "sd_all_pins", + "sd_clk_cmd_pins", + "sd_clk_cmd_uart_pins", + "sd_to_ao_uart_pins", + "ao_to_sd_uart_pins"; + pinctrl-0 = <&sd_all_pins>; + pinctrl-1 = <&sd_clk_cmd_pins>; + pinctrl-2 = <&sd_clk_cmd_pins &ao_to_sd_uart_pins>; + pinctrl-3 = <&sd_to_ao_uart_pins>; + pinctrl-4 = <&ao_to_sd_uart_pins>; + clocks = <&clkc CLKID_SD_EMMC_B>, + <&clkc CLKID_SD_EMMC_B_P0_COMP>, + <&clkc CLKID_FCLK_DIV2>; + clock-names = "core", "clkin0", "clkin1"; + + bus-width = <4>; + cap-sd-highspeed; + max-frequency = <100000000>; + disable-wp; + sd { + status = "disabled"; + pinname = "sd"; + ocr_avail = <0x200080>; /**VDD voltage 3.3 ~ 3.4 */ + caps = "MMC_CAP_4_BIT_DATA", + "MMC_CAP_MMC_HIGHSPEED", + "MMC_CAP_SD_HIGHSPEED"; + /* "MMC_CAP_UHS_SDR12", + * "MMC_CAP_UHS_SDR25", + * "MMC_CAP_UHS_SDR50", + * "MMC_CAP_UHS_SDR104"; + */ + f_min = <400000>; + f_max = <100000000>; + 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>; + 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 + */ + }; + }; + + sd_emmc_a:sdio@d0070000 { + status = "okay"; + compatible = "amlogic, meson-aml-mmc"; + reg = <0x0 0xd0070000 0x0 0x2000>; + interrupts = <0 216 4>; + pinctrl-names = "sdio_clk_cmd_pins", "sdio_all_pins"; + pinctrl-0 = <&sdio_clk_cmd_pins>; + pinctrl-1 = <&sdio_all_pins>; + clocks = <&clkc CLKID_SD_EMMC_A>, + <&clkc CLKID_SD_EMMC_A_P0_COMP>, + <&clkc CLKID_FCLK_DIV2>; + clock-names = "core", "clkin0", "clkin1"; + + bus-width = <4>; + cap-sd-highspeed; + cap-mmc-highspeed; + max-frequency = <100000000>; + non-removable; + disable-wp; + sdio { + status = "disabled"; + pinname = "sdio"; + ocr_avail = <0x200080>; /**VDD voltage 3.3 ~ 3.4 */ + 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", + "MMC_CAP_SDIO_IRQ"; + f_min = <400000>; + f_max = <200000000>; + max_req_size = <0x20000>; /**128KB*/ + card_type = <3>; + /* 3:sdio device(ie:sdio-wifi), + * 4:SD combo (IO+mem) card + */ + }; + }; + uart_AO: serial@c81004c0 { compatible = "amlogic, meson-uart"; reg = <0x0 0xc81004c0 0x0 0x18>; @@ -364,6 +507,81 @@ interrupts = <0 89 1>; interrupt-names = "rdma"; }; + + partitions: partitions{ + parts = <11>; + part-0 = <&logo>; + part-1 = <&recovery>; + part-2 = <&rsv>; + part-3 = <&tee>; + part-4 = <&crypt>; + part-5 = <&misc>; + part-6 = <&instaboot>; + part-7 = <&boot>; + part-8 = <&system>; + part-9 = <&cache>; + part-10 = <&data>; + + logo:logo{ + pname = "logo"; + size = <0x0 0x2000000>; + mask = <1>; + }; + recovery:recovery{ + pname = "recovery"; + size = <0x0 0x2000000>; + mask = <1>; + }; + rsv:rsv{ + pname = "rsv"; + size = <0x0 0x800000>; + mask = <1>; + }; + tee:tee{ + pname = "tee"; + size = <0x0 0x800000>; + mask = <1>; + }; + crypt:crypt{ + pname = "crypt"; + size = <0x0 0x2000000>; + mask = <1>; + }; + misc:misc{ + pname = "misc"; + size = <0x0 0x2000000>; + mask = <1>; + }; + instaboot:instaboot{ + pname = "instaboot"; + size = <0x0 0x400000>; + mask = <1>; + }; + boot:boot + { + pname = "boot"; + size = <0x0 0x2000000>; + mask = <1>; + }; + system:system + { + pname = "system"; + size = <0x0 0x80000000>; + mask = <1>; + }; + cache:cache + { + pname = "cache"; + size = <0x0 0x20000000>; + mask = <2>; + }; + data:data + { + pname = "data"; + size = <0xffffffff 0xffffffff>; + mask = <4>; + }; + }; }; &efuse { status = "ok"; diff --git a/arch/arm64/boot/dts/amlogic/gxm_q200_2g.dts b/arch/arm64/boot/dts/amlogic/gxm_q200_2g.dts index a87c39f55a78..5b381db7e815 100644 --- a/arch/arm64/boot/dts/amlogic/gxm_q200_2g.dts +++ b/arch/arm64/boot/dts/amlogic/gxm_q200_2g.dts @@ -107,11 +107,156 @@ }; }; + sd_emmc_c: emmc@d0074000 { + status = "okay"; + compatible = "amlogic, meson-aml-mmc"; + reg = <0x0 0xd0074000 0x0 0x2000>; + interrupts = <0 218 1>; + pinctrl-names = "emmc_clk_cmd_pins", "emmc_all_pins"; + pinctrl-0 = <&emmc_clk_cmd_pins>; + pinctrl-1 = <&emmc_conf_pull_up &emmc_conf_pull_done>; + clocks = <&clkc CLKID_SD_EMMC_C>, + <&clkc CLKID_SD_EMMC_C_P0_COMP>, + <&clkc CLKID_FCLK_DIV2>; + clock-names = "core", "clkin0", "clkin1"; + + bus-width = <8>; + cap-sd-highspeed; + cap-mmc-highspeed; + mmc-ddr-1_8v; + mmc-hs200-1_8v; + + max-frequency = <200000000>; + non-removable; + disable-wp; + emmc { + status = "disabled"; + pinname = "emmc"; + ocr_avail = <0x200080>; /**VDD voltage 3.3 ~ 3.4 */ + caps = "MMC_CAP_8_BIT_DATA", + "MMC_CAP_MMC_HIGHSPEED", + "MMC_CAP_SD_HIGHSPEED", + "MMC_CAP_NONREMOVABLE", + "MMC_CAP_1_8V_DDR", + "MMC_CAP_HW_RESET", + "MMC_CAP_ERASE", + "MMC_CAP_CMD23"; + caps2 = "MMC_CAP2_HS200", "MMC_CAP2_HS400"; + f_min = <300000>; + f_max = <100000000>; + max_req_size = <0x20000>; /**128KB*/ + hw_reset = <&gpio BOOT_9 GPIO_ACTIVE_HIGH>; + card_type = <1>; + /* 1:mmc card(include eMMC), + * 2:sd card(include tSD) + */ + }; + }; + + sd_emmc_b:sd@d0072000 { + status = "okay"; + compatible = "amlogic, meson-aml-mmc"; + reg = <0x0 0xd0072000 0x0 0x2000>; + interrupts = <0 217 1>; + pinctrl-names = "sd_all_pins", + "sd_clk_cmd_pins", + "sd_clk_cmd_uart_pins", + "sd_to_ao_uart_pins", + "ao_to_sd_uart_pins"; + pinctrl-0 = <&sd_all_pins>; + pinctrl-1 = <&sd_clk_cmd_pins>; + pinctrl-2 = <&sd_clk_cmd_pins &ao_to_sd_uart_pins>; + pinctrl-3 = <&sd_to_ao_uart_pins>; + pinctrl-4 = <&ao_to_sd_uart_pins>; + clocks = <&clkc CLKID_SD_EMMC_B>, + <&clkc CLKID_SD_EMMC_B_P0_COMP>, + <&clkc CLKID_FCLK_DIV2>; + clock-names = "core", "clkin0", "clkin1"; + + bus-width = <4>; + cap-sd-highspeed; + max-frequency = <100000000>; + disable-wp; + sd { + status = "disabled"; + pinname = "sd"; + ocr_avail = <0x200080>; /**VDD voltage 3.3 ~ 3.4 */ + caps = "MMC_CAP_4_BIT_DATA", + "MMC_CAP_MMC_HIGHSPEED", + "MMC_CAP_SD_HIGHSPEED"; + /* "MMC_CAP_UHS_SDR12", + * "MMC_CAP_UHS_SDR25", + * "MMC_CAP_UHS_SDR50", + * "MMC_CAP_UHS_SDR104"; + */ + f_min = <400000>; + f_max = <100000000>; + 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>; + 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 + */ + }; + }; + + sd_emmc_a:sdio@d0070000 { + status = "okay"; + compatible = "amlogic, meson-aml-mmc"; + reg = <0x0 0xd0070000 0x0 0x2000>; + interrupts = <0 216 4>; + pinctrl-names = "sdio_clk_cmd_pins", "sdio_all_pins"; + pinctrl-0 = <&sdio_clk_cmd_pins>; + pinctrl-1 = <&sdio_all_pins>; + clocks = <&clkc CLKID_SD_EMMC_A>, + <&clkc CLKID_SD_EMMC_A_P0_COMP>, + <&clkc CLKID_FCLK_DIV2>; + clock-names = "core", "clkin0", "clkin1"; + + bus-width = <4>; + cap-sd-highspeed; + cap-mmc-highspeed; + max-frequency = <100000000>; + non-removable; + disable-wp; + sdio { + status = "disabled"; + pinname = "sdio"; + ocr_avail = <0x200080>; /**VDD voltage 3.3 ~ 3.4 */ + 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", + "MMC_CAP_SDIO_IRQ"; + f_min = <400000>; + f_max = <200000000>; + max_req_size = <0x20000>; /**128KB*/ + card_type = <3>; + /* 3:sdio device(ie:sdio-wifi), + * 4:SD combo (IO+mem) card + */ + }; + }; + uart_AO: serial@c81004c0 { compatible = "amlogic, meson-uart"; reg = <0x0 0xc81004c0 0x0 0x18>; interrupts = <0 193 1>; status = "okay"; + clocks = <&xtal>; + clock-names = "clk_uart"; xtal_tick_en = <1>; fifosize = < 64 >; pinctrl-names = "default"; @@ -363,6 +508,81 @@ interrupts = <0 89 1>; interrupt-names = "rdma"; }; + + partitions: partitions{ + parts = <11>; + part-0 = <&logo>; + part-1 = <&recovery>; + part-2 = <&rsv>; + part-3 = <&tee>; + part-4 = <&crypt>; + part-5 = <&misc>; + part-6 = <&instaboot>; + part-7 = <&boot>; + part-8 = <&system>; + part-9 = <&cache>; + part-10 = <&data>; + + logo:logo{ + pname = "logo"; + size = <0x0 0x2000000>; + mask = <1>; + }; + recovery:recovery{ + pname = "recovery"; + size = <0x0 0x2000000>; + mask = <1>; + }; + rsv:rsv{ + pname = "rsv"; + size = <0x0 0x800000>; + mask = <1>; + }; + tee:tee{ + pname = "tee"; + size = <0x0 0x800000>; + mask = <1>; + }; + crypt:crypt{ + pname = "crypt"; + size = <0x0 0x2000000>; + mask = <1>; + }; + misc:misc{ + pname = "misc"; + size = <0x0 0x2000000>; + mask = <1>; + }; + instaboot:instaboot{ + pname = "instaboot"; + size = <0x0 0x400000>; + mask = <1>; + }; + boot:boot + { + pname = "boot"; + size = <0x0 0x2000000>; + mask = <1>; + }; + system:system + { + pname = "system"; + size = <0x0 0x80000000>; + mask = <1>; + }; + cache:cache + { + pname = "cache"; + size = <0x0 0x20000000>; + mask = <2>; + }; + data:data + { + pname = "data"; + size = <0xffffffff 0xffffffff>; + mask = <4>; + }; + }; }; &efuse { status = "ok"; diff --git a/arch/arm64/boot/dts/amlogic/gxm_skt.dts b/arch/arm64/boot/dts/amlogic/gxm_skt.dts index 78811453172e..07ea92d0fc66 100644 --- a/arch/arm64/boot/dts/amlogic/gxm_skt.dts +++ b/arch/arm64/boot/dts/amlogic/gxm_skt.dts @@ -108,6 +108,149 @@ }; }; + sd_emmc_c: emmc@d0074000 { + status = "okay"; + compatible = "amlogic, meson-aml-mmc"; + reg = <0x0 0xd0074000 0x0 0x2000>; + interrupts = <0 218 1>; + pinctrl-names = "emmc_clk_cmd_pins", "emmc_all_pins"; + pinctrl-0 = <&emmc_clk_cmd_pins>; + pinctrl-1 = <&emmc_conf_pull_up &emmc_conf_pull_done>; + clocks = <&clkc CLKID_SD_EMMC_C>, + <&clkc CLKID_SD_EMMC_C_P0_COMP>, + <&clkc CLKID_FCLK_DIV2>; + clock-names = "core", "clkin0", "clkin1"; + + bus-width = <8>; + cap-sd-highspeed; + cap-mmc-highspeed; + mmc-ddr-1_8v; + mmc-hs200-1_8v; + + max-frequency = <200000000>; + non-removable; + disable-wp; + emmc { + status = "disabled"; + pinname = "emmc"; + ocr_avail = <0x200080>; /**VDD voltage 3.3 ~ 3.4 */ + caps = "MMC_CAP_8_BIT_DATA", + "MMC_CAP_MMC_HIGHSPEED", + "MMC_CAP_SD_HIGHSPEED", + "MMC_CAP_NONREMOVABLE", + "MMC_CAP_1_8V_DDR", + "MMC_CAP_HW_RESET", + "MMC_CAP_ERASE", + "MMC_CAP_CMD23"; + caps2 = "MMC_CAP2_HS200", "MMC_CAP2_HS400"; + f_min = <300000>; + f_max = <100000000>; + max_req_size = <0x20000>; /**128KB*/ + hw_reset = <&gpio BOOT_9 GPIO_ACTIVE_HIGH>; + card_type = <1>; + /* 1:mmc card(include eMMC), + * 2:sd card(include tSD) + */ + }; + }; + + sd_emmc_b:sd@d0072000 { + status = "okay"; + compatible = "amlogic, meson-aml-mmc"; + reg = <0x0 0xd0072000 0x0 0x2000>; + interrupts = <0 217 1>; + pinctrl-names = "sd_all_pins", + "sd_clk_cmd_pins", + "sd_clk_cmd_uart_pins", + "sd_to_ao_uart_pins", + "ao_to_sd_uart_pins"; + pinctrl-0 = <&sd_all_pins>; + pinctrl-1 = <&sd_clk_cmd_pins>; + pinctrl-2 = <&sd_clk_cmd_pins &ao_to_sd_uart_pins>; + pinctrl-3 = <&sd_to_ao_uart_pins>; + pinctrl-4 = <&ao_to_sd_uart_pins>; + clocks = <&clkc CLKID_SD_EMMC_B>, + <&clkc CLKID_SD_EMMC_B_P0_COMP>, + <&clkc CLKID_FCLK_DIV2>; + clock-names = "core", "clkin0", "clkin1"; + + bus-width = <4>; + cap-sd-highspeed; + max-frequency = <100000000>; + disable-wp; + sd { + status = "disabled"; + pinname = "sd"; + ocr_avail = <0x200080>; /**VDD voltage 3.3 ~ 3.4 */ + caps = "MMC_CAP_4_BIT_DATA", + "MMC_CAP_MMC_HIGHSPEED", + "MMC_CAP_SD_HIGHSPEED"; + /* "MMC_CAP_UHS_SDR12", + * "MMC_CAP_UHS_SDR25", + * "MMC_CAP_UHS_SDR50", + * "MMC_CAP_UHS_SDR104"; + */ + f_min = <400000>; + f_max = <100000000>; + 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>; + 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 + */ + }; + }; + + sd_emmc_a:sdio@d0070000 { + status = "okay"; + compatible = "amlogic, meson-aml-mmc"; + reg = <0x0 0xd0070000 0x0 0x2000>; + interrupts = <0 216 4>; + pinctrl-names = "sdio_clk_cmd_pins", "sdio_all_pins"; + pinctrl-0 = <&sdio_clk_cmd_pins>; + pinctrl-1 = <&sdio_all_pins>; + clocks = <&clkc CLKID_SD_EMMC_A>, + <&clkc CLKID_SD_EMMC_A_P0_COMP>, + <&clkc CLKID_FCLK_DIV2>; + clock-names = "core", "clkin0", "clkin1"; + + bus-width = <4>; + cap-sd-highspeed; + cap-mmc-highspeed; + max-frequency = <100000000>; + non-removable; + disable-wp; + sdio { + status = "disabled"; + pinname = "sdio"; + ocr_avail = <0x200080>; /**VDD voltage 3.3 ~ 3.4 */ + 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", + "MMC_CAP_SDIO_IRQ"; + f_min = <400000>; + f_max = <200000000>; + max_req_size = <0x20000>; /**128KB*/ + card_type = <3>; + /* 3:sdio device(ie:sdio-wifi), + * 4:SD combo (IO+mem) card + */ + }; + }; + uart_AO: serial@c81004c0 { compatible = "amlogic, meson-uart"; reg = <0x0 0xc81004c0 0x0 0x18>; @@ -115,6 +258,8 @@ status = "okay"; xtal_tick_en = <1>; fifosize = < 64 >; + pinctrl-names = "default"; + pinctrl-0 = <&ao_uart_pins>; support-sysrq = <0>; /* 0 not support , 1 support */ }; @@ -208,6 +353,25 @@ status = "okay"; }; + amhdmitx: amhdmitx{ + compatible = "amlogic, amhdmitx"; + dev_name = "amhdmitx"; + status = "okay"; + vend-data = <&vend_data>; + pinctrl-names="hdmitx_hpd", "hdmitx_ddc"; + pinctrl-0=<&hdmitx_hpd>; + pinctrl-1=<&hdmitx_ddc>; + /* HPD, 57 + 32 = 89; CEC, 151 + 32 = 183*/ + interrupts = <0 57 1>; + interrupt-names = "hdmitx_hpd"; + vend_data: vend_data{ /* Should modified by Customer */ + vendor_name = "Amlogic"; /* Max Chars: 8 */ + /* standards.ieee.org/develop/regauth/oui/oui.txt */ + vendor_id = <0x000000>; + product_desc = "MBox Meson Ref"; /* Max Chars: 16 */ + }; + }; + meson-fb { compatible = "amlogic, meson-fb"; /* memory-region = <&fb_reserved>; */ @@ -245,27 +409,82 @@ interrupts = <0 89 1>; interrupt-names = "rdma"; }; - amhdmitx: amhdmitx{ - compatible = "amlogic, amhdmitx"; - dev_name = "amhdmitx"; - status = "okay"; - vend-data = <&vend_data>; - pinctrl-names="hdmitx_hpd", "hdmitx_ddc"; - pinctrl-0=<&hdmitx_hpd>; - pinctrl-1=<&hdmitx_ddc>; - /* HPD, 57 + 32 = 89; CEC, 151 + 32 = 183*/ - interrupts = <0 57 1>; - interrupt-names = "hdmitx_hpd"; - ranges; - vend_data: vend_data{ /* Should modified by Customer */ - vendor_name = "Amlogic"; /* Max Chars: 8 */ - /* standards.ieee.org/develop/regauth/oui/oui.txt */ - vendor_id = <0x000000>; - product_desc = "MBox Meson Ref"; /* Max Chars: 16 */ + + partitions: partitions{ + parts = <11>; + part-0 = <&logo>; + part-1 = <&recovery>; + part-2 = <&rsv>; + part-3 = <&tee>; + part-4 = <&crypt>; + part-5 = <&misc>; + part-6 = <&instaboot>; + part-7 = <&boot>; + part-8 = <&system>; + part-9 = <&cache>; + part-10 = <&data>; + + logo:logo{ + pname = "logo"; + size = <0x0 0x2000000>; + mask = <1>; + }; + recovery:recovery{ + pname = "recovery"; + size = <0x0 0x2000000>; + mask = <1>; + }; + rsv:rsv{ + pname = "rsv"; + size = <0x0 0x800000>; + mask = <1>; + }; + tee:tee{ + pname = "tee"; + size = <0x0 0x800000>; + mask = <1>; + }; + crypt:crypt{ + pname = "crypt"; + size = <0x0 0x2000000>; + mask = <1>; + }; + misc:misc{ + pname = "misc"; + size = <0x0 0x2000000>; + mask = <1>; + }; + instaboot:instaboot{ + pname = "instaboot"; + size = <0x0 0x400000>; + mask = <1>; + }; + boot:boot + { + pname = "boot"; + size = <0x0 0x2000000>; + mask = <1>; + }; + system:system + { + pname = "system"; + size = <0x0 0x80000000>; + mask = <1>; + }; + cache:cache + { + pname = "cache"; + size = <0x0 0x20000000>; + mask = <2>; + }; + data:data + { + pname = "data"; + size = <0xffffffff 0xffffffff>; + mask = <4>; }; }; }; - &efuse { status = "ok"; }; diff --git a/arch/arm64/boot/dts/amlogic/mesongxl.dtsi b/arch/arm64/boot/dts/amlogic/mesongxl.dtsi index e139d2e0cdde..b5c9a444ba97 100644 --- a/arch/arm64/boot/dts/amlogic/mesongxl.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesongxl.dtsi @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -396,6 +397,15 @@ function = "remote"; }; }; + sd_to_ao_uart_pins:sd_to_ao_uart_pins { + mux { + groups = "uart_tx_ao_a_0", + "uart_rx_ao_a_0"; + function = "uart_ao"; + bias-pull-up; + input-enable; + }; + }; ao_uart_pins:ao_uart { mux { @@ -552,32 +562,29 @@ * }; * sd_1bit_uart_pins:sd_1bit_uart_pins { * }; - * sd_to_ao_uart_pins:sd_to_ao_uart_pins { - * }; - * ao_to_sd_uart_pins:ao_to_sd_uart_pins { - * }; * sd_to_ao_jtag_pins:sd_to_ao_jtag_pins{ * }; */ + ao_to_sd_uart_pins:ao_to_sd_uart_pins { + mux { + groups = "uart_tx_ao_a_1", + "uart_rx_ao_a_1"; + function = "uart_ao_a_1"; + bias-pull-up; + input-enable; + }; + }; emmc_clk_cmd_pins:emmc_clk_cmd_pins { mux { groups = "emmc_cmd", "emmc_clk"; function = "emmc"; - bias-pull-up; input-enable; + bias-pull-up; }; }; - emmc_pins: emmc { - mux { - groups = "emmc_nand_d07", - "emmc_cmd", - "emmc_clk"; - function = "emmc"; - }; - }; emmc_conf_pull_up:emmc_conf_pull_up { mux { @@ -585,6 +592,7 @@ "emmc_clk", "emmc_cmd"; function = "emmc"; + input-enable; bias-pull-up; }; }; @@ -593,17 +601,32 @@ mux { groups = "emmc_ds"; function = "emmc"; + input-enable; bias-pull-down; }; }; - emmc_all_pins:emmc_all_pins { + sd_clk_cmd_pins:sd_clk_cmd_pins{ mux { - groups = "emmc_nand_d07", - "emmc_clk", - "emmc_cmd"; - function = "emmc"; + groups = "sdcard_cmd", + "sdcard_clk"; + function = "sdcard"; input-enable; + bias-pull-up; + }; + }; + + sd_all_pins:sd_all_pins{ + mux { + groups = "sdcard_d0", + "sdcard_d1", + "sdcard_d2", + "sdcard_d3", + "sdcard_cmd", + "sdcard_clk"; + function = "sdcard"; + input-enable; + bias-pull-up; }; }; diff --git a/arch/arm64/boot/dts/amlogic/mesongxm.dtsi b/arch/arm64/boot/dts/amlogic/mesongxm.dtsi index b1f9ad232990..9f20a26bf3fc 100644 --- a/arch/arm64/boot/dts/amlogic/mesongxm.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesongxm.dtsi @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -448,6 +449,15 @@ function = "remote"; }; }; + sd_to_ao_uart_pins:sd_to_ao_uart_pins { + mux { + groups = "uart_tx_ao_a_0", + "uart_rx_ao_a_0"; + function = "uart_ao"; + bias-pull-up; + input-enable; + }; + }; ao_uart_pins:ao_uart { mux { @@ -604,32 +614,29 @@ * }; * sd_1bit_uart_pins:sd_1bit_uart_pins { * }; - * sd_to_ao_uart_pins:sd_to_ao_uart_pins { - * }; - * ao_to_sd_uart_pins:ao_to_sd_uart_pins { - * }; * sd_to_ao_jtag_pins:sd_to_ao_jtag_pins{ * }; */ + ao_to_sd_uart_pins:ao_to_sd_uart_pins { + mux { + groups = "uart_tx_ao_a_1", + "uart_rx_ao_a_1"; + function = "uart_ao_a_1"; + bias-pull-up; + input-enable; + }; + }; emmc_clk_cmd_pins:emmc_clk_cmd_pins { mux { groups = "emmc_cmd", "emmc_clk"; function = "emmc"; - bias-pull-up; input-enable; + bias-pull-up; }; }; - emmc_pins: emmc { - mux { - groups = "emmc_nand_d07", - "emmc_cmd", - "emmc_clk"; - function = "emmc"; - }; - }; emmc_conf_pull_up:emmc_conf_pull_up { mux { @@ -637,6 +644,7 @@ "emmc_clk", "emmc_cmd"; function = "emmc"; + input-enable; bias-pull-up; }; }; @@ -645,17 +653,32 @@ mux { groups = "emmc_ds"; function = "emmc"; + input-enable; bias-pull-down; }; }; - emmc_all_pins:emmc_all_pins { + sd_clk_cmd_pins:sd_clk_cmd_pins{ mux { - groups = "emmc_nand_d07", - "emmc_clk", - "emmc_cmd"; - function = "emmc"; + groups = "sdcard_cmd", + "sdcard_clk"; + function = "sdcard"; input-enable; + bias-pull-up; + }; + }; + + sd_all_pins:sd_all_pins{ + mux { + groups = "sdcard_d0", + "sdcard_d1", + "sdcard_d2", + "sdcard_d3", + "sdcard_cmd", + "sdcard_clk"; + function = "sdcard"; + input-enable; + bias-pull-up; }; }; diff --git a/arch/arm64/configs/meson64_defconfig b/arch/arm64/configs/meson64_defconfig index f468df84749d..cb7db68bb583 100644 --- a/arch/arm64/configs/meson64_defconfig +++ b/arch/arm64/configs/meson64_defconfig @@ -209,6 +209,7 @@ CONFIG_AMLOGIC_MEDIA_FB_OSD_VSYNC_RDMA=y CONFIG_AMLOGIC_MEDIA_FB_OSD2_ENABLE=y CONFIG_AMLOGIC_MEDIA_FB_OSD2_CURSOR=y CONFIG_AMLOGIC_HDMITX=y +CONFIG_AMLOGIC_MMC=y CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS_MOUNT=y @@ -287,6 +288,9 @@ CONFIG_USB_ISP1301=y CONFIG_USB_GADGET=y CONFIG_USB_CONFIGFS=y CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_MMC=y +CONFIG_MMC_BLOCK_MINORS=32 +CONFIG_MMC_ARMMMCI=y CONFIG_NEW_LEDS=y CONFIG_LEDS_CLASS=y CONFIG_LEDS_TRIGGERS=y diff --git a/drivers/amlogic/Kconfig b/drivers/amlogic/Kconfig index 985d9735e800..00426eb9f098 100644 --- a/drivers/amlogic/Kconfig +++ b/drivers/amlogic/Kconfig @@ -51,5 +51,7 @@ source "drivers/amlogic/pwm/Kconfig" source "drivers/amlogic/media/Kconfig" +source "drivers/amlogic/mmc/Kconfig" + endmenu endif diff --git a/drivers/amlogic/Makefile b/drivers/amlogic/Makefile index c35358617bce..baf4d779abc2 100644 --- a/drivers/amlogic/Makefile +++ b/drivers/amlogic/Makefile @@ -47,3 +47,6 @@ obj-$(CONFIG_AMLOGIC_PWM) += pwm/ obj-$(CONFIG_AMLOGIC_MEDIA_ENABLE) += media/ + +obj-$(CONFIG_AMLOGIC_MMC) += mmc/ + diff --git a/drivers/amlogic/mmc/Kconfig b/drivers/amlogic/mmc/Kconfig new file mode 100644 index 000000000000..302608a84f4d --- /dev/null +++ b/drivers/amlogic/mmc/Kconfig @@ -0,0 +1,19 @@ +# Amlogic MMC driver + +comment "MMC/SD/SDIO Host Controller Drivers" + +menu "Multimedia Card support" + +config AMLOGIC_MMC + bool "Amlogic Multimedia Card support" + depends on MMC + depends on MMC_BLOCK + depends on MMC_BLOCK_BOUNCE + default n + help + This selects support for the Amlogic SD/MMC Host Controller + found on the S912/GXM 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 new file mode 100644 index 000000000000..759fdb33bd7d --- /dev/null +++ b/drivers/amlogic/mmc/Makefile @@ -0,0 +1,5 @@ +# +# Amlogic MMC specific Makefile +# + +obj-$(CONFIG_AMLOGIC_MMC) += amlsd.o amlsd_of.o emmc_partitions.o aml_sd_emmc.o diff --git a/drivers/amlogic/mmc/aml_sd_emmc.c b/drivers/amlogic/mmc/aml_sd_emmc.c new file mode 100644 index 000000000000..084d9ee04e74 --- /dev/null +++ b/drivers/amlogic/mmc/aml_sd_emmc.c @@ -0,0 +1,2921 @@ +/* + * drivers/amlogic/mmc/aml_sd_emmc.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 + +struct mmc_host *sdio_host; + +static unsigned int log2i(unsigned int val) +{ + unsigned int ret = -1; + + while (val != 0) { + val >>= 1; + ret++; + } + return ret; +} + +#ifdef AML_CALIBRATION +u32 checksum_cali(u32 *buffer, u32 length) +{ + u32 sum = 0, *i = buffer; + + buffer += length; + for (; i < buffer; sum += *(i++)) + ; + return sum; +} + +static int is_larger(u8 value, u8 base, u8 wrap) +{ + int ret = 0; + + if ((value > base) || ((value < base) && (base == wrap))) + ret = 1; + return ret; +} + +static void find_base(struct amlsd_platform *pdata, u8 *is_base_index, + u8 fir_base[2][2], u8 *first_base_temp_num, u32 base_index_val, + u8 *calout_cmp_num) +{ + u8 first_base_temp, line_x, dly_tmp, cal_time, max_index; + + line_x = pdata->c_ctrl.line_x; + dly_tmp = pdata->c_ctrl.dly_tmp; + cal_time = pdata->c_ctrl.cal_time; + max_index = pdata->c_ctrl.max_index; + + if (pdata->calout[dly_tmp][cal_time] != 0xFF) { + /* calculate base index! */ + if (*is_base_index == 1) { + first_base_temp = pdata->calout[dly_tmp][cal_time]; + *first_base_temp_num = *first_base_temp_num + 1; + if (*first_base_temp_num == 1) { + fir_base[0][0] = first_base_temp; + fir_base[0][1] = fir_base[0][1] + 1; + } else { + if (first_base_temp == fir_base[0][0]) + fir_base[0][1]++; + else { + fir_base[1][0] = + first_base_temp; + fir_base[1][1]++; + } + } + /* get a higher index, add the counter! */ + } else if (is_larger(pdata->calout[dly_tmp][cal_time], + base_index_val, max_index)) + *calout_cmp_num = *calout_cmp_num + 1; + } else { + /* todo, if we do not capture a valid value, + * HIGHLIGHT(cal_time = 0) may cause error!!! + */ + pr_err("!!!Do not capture a valid index"); + pr_err("@ line %d on capture %d\n", + line_x, cal_time); + } +} + +static int aml_sd_emmc_cali_transfer(struct mmc_host *mmc, + u8 opcode, u8 *blk_test, u32 blksz) +{ + struct amlsd_host *host = mmc_priv(mmc); + 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 = CALI_PATTERN_OFFSET; + 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; + if (opcode == 18) + data.blocks = CALI_BLK_CNT; + else + data.blocks = 1; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; + + memset(blk_test, 0, blksz * data.blocks); + sg_init_one(&sg, blk_test, blksz * data.blocks); + + mrq.cmd = &cmd; + mrq.stop = &stop; + mrq.data = &data; + host->mrq = &mrq; + mmc_wait_for_req(mmc, &mrq); + return data.error | cmd.error; +} + +static int aml_cali_auto(struct mmc_host *mmc, struct cali_data *c_data) +{ + struct amlsd_host *host = mmc_priv(mmc); + struct amlsd_platform *pdata = host->pdata; + u8 i, max_cali_i = 0; + u32 max_cali_count = 0; + u32 cali_tmp[4] = {0}; + u32 line_delay = 0; + u32 base_index_val = 0; + u32 adjust; + u8 is_base_index, max_index, line_x, dly_tmp; + u8 bus_width = 8; + struct sd_emmc_adjust *gadjust = (struct sd_emmc_adjust *)&adjust; + u32 blksz = 512; + u8 *blk_test; + int ret = 0; + + blk_test = kmalloc(blksz * CALI_BLK_CNT, GFP_KERNEL); + if (!blk_test) + return -ENOMEM; + + if (mmc->ios.bus_width == 0) + bus_width = 1; + else if (mmc->ios.bus_width == 2) + bus_width = 4; + else + bus_width = 8; + + max_index = pdata->c_ctrl.max_index; + /* for each line */ + for (line_x = 0; line_x < bus_width; line_x++) { + base_index_val = 0; + is_base_index = 1; + memset(pdata->calout, 0xFF, 20 * 20); + pdata->c_ctrl.line_x = line_x; + /* for each delay index! */ + for (dly_tmp = 0; dly_tmp < MAX_DELAY_CNT; dly_tmp++) { + line_delay = dly_tmp << (4 * line_x); + writel(line_delay, host->base + SD_EMMC_DELAY); + pdata->caling = 1; + aml_sd_emmc_cali_transfer(mmc, + MMC_READ_MULTIPLE_BLOCK, + blk_test, blksz); + for (i = 0; i < 4; i++) { + cali_tmp[i] = readl(host->base + + SD_EMMC_CALOUT + i*4); + if (max_cali_count < (cali_tmp[i] & 0xffffff)) { + max_cali_count + = (cali_tmp[i] & 0xffffff); + max_cali_i = i; + } + } + pdata->calout[dly_tmp][line_x] + = (cali_tmp[max_cali_i] >> 24) & 0x3f; +#ifdef CHOICE_DEBUG + for (i = 0; i < 4; i++) + pr_info("cali_index[%d] =0x%x, cali_count[%d] = %d\n", + i, cali_tmp[i] >> 24, i, + cali_tmp[i] & 0xffffff); +#endif + pdata->caling = 0; + adjust = readl(host->base + SD_EMMC_ADJUST); + gadjust->cali_enable = 0; + gadjust->cali_sel = 0; + writel(adjust, host->base + SD_EMMC_ADJUST); + if (is_base_index == 1) { + is_base_index = 0; + c_data->base_index[line_x] = + pdata->calout[dly_tmp][line_x]; + if (c_data->base_index[line_x] + < c_data->base_index_min) + c_data->base_index_min + = c_data->base_index[line_x]; + if (c_data->base_index[line_x] + > c_data->base_index_max) + c_data->base_index_max + = c_data->base_index[line_x]; + } + if (is_larger(pdata->calout[dly_tmp][line_x], + c_data->base_index[line_x], + max_index)) + break; + } /* endof dly_tmp loop... */ + /* get a valid index on current line! */ + if (dly_tmp == MAX_DELAY_CNT) + ret = -1; + else + c_data->ln_delay[line_x] = dly_tmp; + if (ret) + break; +#ifdef CHOICE_DEBUG + for (i = 0; i < 16; i++) + pr_info("%02x, ", pdata->calout[i][line_x]); + pr_info("\n"); +#endif + } + kfree(blk_test); + blk_test = NULL; + return ret; +} + +static int aml_cali_index(struct mmc_host *mmc, struct cali_data *c_data) +{ + struct amlsd_host *host = mmc_priv(mmc); + struct amlsd_platform *pdata = host->pdata; + u32 line_delay = 0; + u8 calout_cmp_num = 0; + u32 base_index_val = 0; + u32 adjust; + u8 bus_width = 8; + u8 line_x, dly_tmp, cal_time; + u8 is_base_index; + u8 fir_base[2][2] = { {0} }; + u8 first_base_temp_num = 0; + u8 cal_per_line_num = 8; + struct sd_emmc_adjust *gadjust = (struct sd_emmc_adjust *)&adjust; + u32 blksz = 512; + u8 *blk_test; + int ret = 0; + + blk_test = kmalloc(blksz * CALI_BLK_CNT, GFP_KERNEL); + if (!blk_test) + return -ENOMEM; + + if (mmc->ios.bus_width == 0) + bus_width = 1; + else if (mmc->ios.bus_width == 2) + bus_width = 4; + else + bus_width = 8; + + for (line_x = 0; line_x < bus_width; line_x++) { + base_index_val = 0; + is_base_index = 1; + memset(pdata->calout, 0xFF, 20 * 20); + first_base_temp_num = 0; + fir_base[0][0] = 0; + fir_base[0][1] = 0; + fir_base[1][0] = 0; + fir_base[1][1] = 0; + pdata->c_ctrl.line_x = line_x; + /* for each delay index! */ + for (dly_tmp = 0; dly_tmp < MAX_DELAY_CNT; dly_tmp++) { + line_delay = 0; + line_delay = dly_tmp << (4 * line_x); + writel(line_delay, host->base + SD_EMMC_DELAY); + calout_cmp_num = 0; + pdata->c_ctrl.dly_tmp = dly_tmp; + /* cal_time */ + for (cal_time = 0; cal_time < cal_per_line_num; + cal_time++) { + /* send read cmd. */ + pdata->caling = 1; + aml_sd_emmc_cali_transfer(mmc, + MMC_READ_MULTIPLE_BLOCK, + blk_test, blksz); + pdata->calout[dly_tmp][cal_time] + = readl(host->base + SD_EMMC_CALOUT) + & 0x3f; + pdata->caling = 0; + adjust = readl(host->base + SD_EMMC_ADJUST); + gadjust->cali_enable = 0; + gadjust->cali_sel = 0; + writel(adjust, host->base + SD_EMMC_ADJUST); + /*get a valid index*/ + pdata->c_ctrl.cal_time = cal_time; + find_base(pdata, &is_base_index, + fir_base, &first_base_temp_num, + base_index_val, &calout_cmp_num); + } /* endof cal_time loop... */ + /* get base index value */ + /* if ((base_index_val > 0) && (is_base_index == 1)) {*/ + if (is_base_index == 1) { + is_base_index = 0; + if (fir_base[1][1] > fir_base[0][1]) + base_index_val = fir_base[1][0]; + else + base_index_val = fir_base[0][0]; + /*base_index_val = valid_base_index; */ + if (base_index_val < c_data->base_index_min) + c_data->base_index_min = base_index_val; + if (base_index_val > c_data->base_index_max) + c_data->base_index_max = base_index_val; + c_data->base_index[line_x] = base_index_val; + /* pr_err("get base index %d + * value @ line (%d)\n", + * base_index_val, line_x); + */ + } else if (calout_cmp_num == cal_per_line_num) { + break; + } + } /* endof dly_tmp loop... */ + /* get a valid index on current line! */ + if ((dly_tmp == MAX_DELAY_CNT) + && (calout_cmp_num != cal_per_line_num)) + ret = -1; + else + c_data->ln_delay[line_x] = dly_tmp; + if (ret) + break; +#ifdef CHOICE_DEBUG + for (i = 0; i < 16; i++) { + pr_info("%02x %02x %02x %02x %02x %02x %02x %02x\n", + pdata->calout[i][0], + pdata->calout[i][1], + pdata->calout[i][2], + pdata->calout[i][3], + pdata->calout[i][4], + pdata->calout[i][5], + pdata->calout[i][6], + pdata->calout[i][7]); + } +#endif + } + kfree(blk_test); + blk_test = NULL; + return ret; +} + +static int aml_cali_find(struct mmc_host *mmc, struct cali_data *c_data) +{ + struct amlsd_host *host = mmc_priv(mmc); + struct amlsd_platform *pdata = host->pdata; + u32 line_delay; + struct sd_emmc_delay *line_dly = (struct sd_emmc_delay *)&line_delay; + u32 max_cal_result = 0; + u32 min_cal_result = 10000; + u32 cal_result[8]; + u8 delay_step, max_index, bus_width = 8, line_x = 8; + + if (get_cpu_type() == MESON_CPU_MAJOR_ID_GXBB) + delay_step = 125; + else + delay_step = 200; + if (mmc->ios.bus_width == 0) + bus_width = 1; + else if (mmc->ios.bus_width == 2) + bus_width = 4; + else + bus_width = 8; + + max_index = pdata->c_ctrl.max_index; + /* if base index wrap, fix */ + for (line_x = 0; line_x < bus_width; line_x++) { + /* 1000 means index is 1ns */ + /* make sure no neg-value for ln_delay*/ + if (c_data->ln_delay[line_x]*delay_step > 1000) + c_data->ln_delay[line_x] = 1000 / delay_step; + + if (c_data->base_index[line_x] == max_index) { + cal_result[line_x] = ((max_index+1))*1000 - + c_data->ln_delay[line_x]*delay_step; + } else if ((c_data->base_index_max == max_index) && + (c_data->base_index[line_x] != (max_index - 1)) && + (c_data->base_index[line_x] != (max_index - 2))) { + cal_result[line_x] = ((c_data->base_index[line_x]+1)% + (max_index+1) + (max_index+1))*1000 - + c_data->ln_delay[line_x]*delay_step; + } else { + cal_result[line_x] = (c_data->base_index[line_x]+1)% + (max_index+1) * 1000 - + c_data->ln_delay[line_x]*delay_step; + } + max_cal_result = (max_cal_result < cal_result[line_x]) + ? cal_result[line_x] : max_cal_result; + min_cal_result = (min_cal_result > cal_result[line_x]) + ? cal_result[line_x] : min_cal_result; + pr_info("%s: delay[%d]=%5d padding=%2d, bidx=%d\n", + mmc_hostname(mmc), line_x, cal_result[line_x], + c_data->ln_delay[line_x], + c_data->base_index[line_x]); + } + pr_info("%s: calibration result : max(%d), min(%d)\n", + mmc_hostname(mmc), max_cal_result, min_cal_result); + /* retry cali here! */ + if ((max_cal_result - min_cal_result) >= 2000) + return -1; + + /* swap base_index_max */ + if ((c_data->base_index_max == max_index) + && (c_data->base_index_min == 0)) + c_data->base_index_max = 0; + if (max_cal_result < (c_data->base_index_max * 1000)) + max_cal_result = (c_data->base_index_max * 1000); + /* calculate each line delay we should use! */ + line_delay = readl(host->base + SD_EMMC_DELAY); + line_dly->dat0 = (((max_cal_result - cal_result[0]) / delay_step) + > 15) ? 15 : + ((max_cal_result - cal_result[0]) / delay_step); + line_dly->dat1 = (((max_cal_result - cal_result[1]) / delay_step) + > 15) ? 15 : + ((max_cal_result - cal_result[1]) / delay_step); + line_dly->dat2 = (((max_cal_result - cal_result[2]) / delay_step) + > 15) ? 15 : + ((max_cal_result - cal_result[2]) / delay_step); + line_dly->dat3 = (((max_cal_result - cal_result[3]) / delay_step) + > 15) ? 15 : + ((max_cal_result - cal_result[3]) / delay_step); + line_dly->dat4 = (((max_cal_result - cal_result[4]) / delay_step) + > 15) ? 15 : + ((max_cal_result - cal_result[4]) / delay_step); + line_dly->dat5 = (((max_cal_result - cal_result[5]) / delay_step) + > 15) ? 15 : + ((max_cal_result - cal_result[5]) / delay_step); + line_dly->dat6 = (((max_cal_result - cal_result[6]) / delay_step) + > 15) ? 15 : + ((max_cal_result - cal_result[6]) / delay_step); + line_dly->dat7 = (((max_cal_result - cal_result[7]) / delay_step) + > 15) ? 15 : + ((max_cal_result - cal_result[7]) / delay_step); + + pr_info("%s: line_delay =0x%x, max_cal_result =%d\n", + mmc_hostname(mmc), line_delay, max_cal_result); + /* set delay count into reg*/ + writel(line_delay, host->base + SD_EMMC_DELAY); + return 0; +} + +static int aml_sd_emmc_execute_calibration(struct mmc_host *mmc, + u32 *adj_win_start, u32 type) +{ + struct amlsd_host *host = mmc_priv(mmc); + struct amlsd_platform *pdata = host->pdata; + u32 adjust; + struct sd_emmc_adjust *gadjust = (struct sd_emmc_adjust *)&adjust; + u32 vclk, cali_retry = 0; +#ifdef SD_EMMC_CLK_CTRL + struct sd_emmc_clock *clkc = (struct sd_emmc_clock *)&(vclk); + u8 clk_div_tmp; +#else + unsigned long clk_tmp; +#endif + struct cali_data c_data; + int ret = 0; +#ifdef CHOICE_DEBUG + u8 i; +#endif + + memset(&c_data, 0, sizeof(struct cali_data)); +#ifdef SD_EMMC_CLK_CTRL + vclk = readl(host->base + SD_EMMC_CLOCK); + clk_div_tmp = clkc->div; + if (type) + clkc->div = 5; + writel(vclk, host->base + SD_EMMC_CLOCK); +#else + clk_tmp = clk_get_rate(host->cfg_div_clk); + if (type) + clk_set_rate(host->cfg_div_clk, 200000000); + vclk = readl(host->base + SD_EMMC_CLOCK); +#endif + pdata->clkc = vclk; + pdata->c_ctrl.max_index = (vclk & 0x3f) - 1; + +_cali_retry: + c_data.base_index_min = pdata->c_ctrl.max_index + 1; + c_data.base_index_max = 0; + pr_info("%s: trying cali %d-th time(s)\n", + mmc_hostname(mmc), cali_retry); + host->is_tunning = 1; + /* for each line */ + if (type) + ret = aml_cali_auto(mmc, &c_data); + else + ret = aml_cali_index(mmc, &c_data); + if (ret) { + /* Do not get a valid line delay index value! */ + if (cali_retry < MAX_CALI_RETRY) { + pr_err("Do't get valid ln_delay @ line %d, try\n", + pdata->c_ctrl.line_x); + cali_retry++; + goto _cali_retry; + } else { + pr_info("%s: calibration failed, use default\n", + mmc_hostname(host->mmc)); + return -1; + } + } + host->is_tunning = 0; + + ret = aml_cali_find(mmc, &c_data); + /* retry cali here! */ + if (ret) { + if (cali_retry < MAX_CALI_RETRY) { + cali_retry++; + goto _cali_retry; + } else { + pr_info("%s: calibration failed, use default\n", + mmc_hostname(host->mmc)); + return -1; + } + } + pr_info("calibration @%d times ok\n", cali_retry); + + /* restore original clk setting */ +#ifdef SD_EMMC_CLK_CTRL + vclk = readl(host->base + SD_EMMC_CLOCK); + clkc->div = clk_div_tmp; + writel(vclk, host->base + SD_EMMC_CLOCK); +#else + clk_set_rate(host->cfg_div_clk, clk_tmp); + vclk = readl(host->base + SD_EMMC_CLOCK); +#endif + pdata->clkc = vclk; + if (!type) { + /* set default cmd delay*/ + adjust = readl(host->base + SD_EMMC_ADJUST); + if (get_cpu_type() == MESON_CPU_MAJOR_ID_GXBB) + gadjust->cmd_delay = 7; + writel(adjust, host->base + SD_EMMC_ADJUST); + } + + pr_info("%s: base_index_max %d, base_index_min %d\n", + mmc_hostname(mmc), c_data.base_index_max, + c_data.base_index_min); + + /* get adjust point! */ + *adj_win_start = c_data.base_index_max + 2; + + return 0; +} +#endif + +static u32 aml_sd_emmc_tuning_transfer(struct mmc_host *mmc, + u32 opcode, const u8 *blk_pattern, u8 *blk_test, u32 blksz) +{ + struct amlsd_host *host = mmc_priv(mmc); + u32 vctrl = readl(host->base + SD_EMMC_CFG); + struct sd_emmc_config *ctrl = (struct sd_emmc_config *)&vctrl; + u32 tuning_err = 0; + u32 n, nmatch; + /* try ntries */ + for (n = 0, nmatch = 0; n < TUNING_NUM_PER_POINT; n++) { + tuning_err = aml_sd_emmc_cali_transfer(mmc, + opcode, blk_test, blksz); + if (!tuning_err) { + if (ctrl->ddr == 1) + nmatch++; + else if (!memcmp(blk_pattern, blk_test, blksz)) + nmatch++; + else { + sd_emmc_dbg(AMLSD_DBG_TUNING, + "nmatch=%d\n", nmatch); + break; + } + } else { + sd_emmc_dbg(AMLSD_DBG_TUNING, + "Tuning transfer error:"); + sd_emmc_dbg(AMLSD_DBG_TUNING, + "nmatch=%d\n", nmatch); + break; + } + } + return nmatch; +} + +static int aml_tuning_adj(struct mmc_host *mmc, u32 opcode, + struct aml_tuning_data *tuning_data, + int *best_start, int *best_size) +{ + struct amlsd_host *host = mmc_priv(mmc); + u32 adjust; + struct sd_emmc_adjust *gadjust = (struct sd_emmc_adjust *)&adjust; + u32 vclk; + struct sd_emmc_clock *clkc = (struct sd_emmc_clock *)&(vclk); + u32 vctrl; + struct sd_emmc_config *ctrl = (struct sd_emmc_config *)&vctrl; + u32 clk_rate = 1000000000, clock, clk_div, nmatch = 0; + int adj_delay = 0; + const u8 *blk_pattern = tuning_data->blk_pattern; + unsigned int blksz = tuning_data->blksz; + u8 *blk_test; + int wrap_win_start = -1, wrap_win_size = 0; + int best_win_start = -1, best_win_size = 0; + int curr_win_start = -1, curr_win_size = 0; + + vclk = readl(host->base + SD_EMMC_CLOCK); + vctrl = readl(host->base + SD_EMMC_CFG); + clk_div = clkc->div; + clock = clk_rate / clk_div;/*200MHz, bus_clk */ + mmc->actual_clock = ctrl->ddr ? + (clock / 2) : clock;/*100MHz in ddr */ + if (ctrl->ddr == 1) { + blksz = 512; + opcode = 17; + } + blk_test = kmalloc(blksz, GFP_KERNEL); + if (!blk_test) + return -ENOMEM; + + pr_info("%s: clk %d %s tuning start\n", + mmc_hostname(mmc), (ctrl->ddr ? (clock / 2) : clock), + (ctrl->ddr ? "DDR mode" : "SDR mode")); + for (adj_delay = 0; adj_delay < clk_div; adj_delay++) { + adjust = readl(host->base + SD_EMMC_ADJUST); + gadjust->adj_delay = adj_delay; + gadjust->adj_enable = 1; + gadjust->cali_enable = 0; + gadjust->cali_rise = 0; + writel(adjust, host->base + SD_EMMC_ADJUST); + nmatch = aml_sd_emmc_tuning_transfer(mmc, opcode, + blk_pattern, blk_test, blksz); + /*get a ok adjust point!*/ + if (nmatch == TUNING_NUM_PER_POINT) { + if (adj_delay == 0) + wrap_win_start = adj_delay; + if (wrap_win_start >= 0) + wrap_win_size++; + if (curr_win_start < 0) + curr_win_start = adj_delay; + curr_win_size++; + pr_info("%s: rx_tuning_result[%d] = %d\n", + mmc_hostname(host->mmc), adj_delay, nmatch); + } 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; + } + } + } + /* last point is ok! */ + 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; + } + *best_start = best_win_start; + *best_size = best_win_size; + kfree(blk_test); + return 0; +} + +/* TODO....., based on new tuning function */ +static int aml_sd_emmc_execute_tuning_(struct mmc_host *mmc, u32 opcode, + struct aml_tuning_data *tuning_data, + u32 adj_win_start) +{ + struct amlsd_host *host = mmc_priv(mmc); + struct amlsd_platform *pdata = host->pdata; + u32 vclk; + struct sd_emmc_clock *clkc = (struct sd_emmc_clock *)&(vclk); + u32 adjust; + struct sd_emmc_adjust *gadjust = (struct sd_emmc_adjust *)&adjust; + u32 clk_rate = 1000000000; + unsigned long flags; + int ret = 0; + struct aml_emmc_adjust *emmc_adj = &host->emmc_adj; + u8 tuning_num = 0; + u32 clock, clk_div; + u32 adj_delay_find; + int best_win_start = -1, best_win_size = 0; + + writel(0, host->base + SD_EMMC_ADJUST); + +tunning: + spin_lock_irqsave(&host->mrq_lock, flags); + pdata->need_retuning = false; + spin_unlock_irqrestore(&host->mrq_lock, flags); + vclk = readl(host->base + SD_EMMC_CLOCK); + clk_div = clkc->div; + clock = clk_rate / clk_div;/*200MHz, bus_clk */ + + host->is_tunning = 1; + ret = aml_tuning_adj(mmc, opcode, + tuning_data, &best_win_start, &best_win_size); + if (ret) + return -ENOMEM; + if (best_win_size <= 0) { + if ((tuning_num++ > MAX_TUNING_RETRY) + || (clkc->div >= 10)) { + pr_info("%s: final result of tuning failed\n", + mmc_hostname(host->mmc)); + return -1; + } + clkc->div += 1; + writel(vclk, host->base + SD_EMMC_CLOCK); + mmc->actual_clock = clk_rate / clkc->div; + pdata->clkc = vclk; + pr_info("%s: tuning failed, reduce freq and retuning\n", + mmc_hostname(host->mmc)); + goto tunning; + } else { + pr_info("%s: best_win_start =%d, best_win_size =%d\n", + mmc_hostname(host->mmc), best_win_start, best_win_size); + } + + if ((best_win_size != clk_div) + || (aml_card_type_sdio(pdata) + && (get_cpu_type() == MESON_CPU_MAJOR_ID_GXM))) { + adj_delay_find = best_win_start + (best_win_size - 1) / 2 + + (best_win_size - 1) % 2; + adj_delay_find = adj_delay_find % clk_div; + } else + adj_delay_find = 0; + + /* fixme, for retry debug. */ + if (aml_card_type_mmc(pdata) + && (clk_div <= 5) && (adj_win_start != 100) + && (get_cpu_type() == MESON_CPU_MAJOR_ID_GXBB)) { + pr_info("%s: adj_win_start %d\n", + mmc_hostname(host->mmc), adj_win_start); + adj_delay_find = adj_win_start % clk_div; + } + adjust = readl(host->base + SD_EMMC_ADJUST); + gadjust->adj_delay = adj_delay_find; + gadjust->adj_enable = 1; + gadjust->cali_enable = 0; + gadjust->cali_rise = 0; + writel(adjust, host->base + SD_EMMC_ADJUST); + host->is_tunning = 0; + + /* fixme, yyh for retry flow. */ + emmc_adj->adj_win_start = best_win_start; + emmc_adj->adj_win_len = best_win_size; + emmc_adj->adj_point = adj_delay_find; + emmc_adj->clk_div = clk_div; + pr_info("%s: clock=0x%x, adjust=0x%x\n", + mmc_hostname(host->mmc), + readl(host->base + SD_EMMC_CLOCK), + readl(host->base + SD_EMMC_ADJUST)); + return ret; +} + +static int aml_sd_emmc_rxclk_find(struct mmc_host *mmc, + u8 *rx_tuning_result, u8 total_point, + int *best_win_start, int *best_win_size) +{ + struct amlsd_host *host = mmc_priv(mmc); + int wrap_win_start = -1, wrap_win_size = 0; + int curr_win_start = -1, curr_win_size = 0; + u8 n; + int rxclk_find; + + for (n = 0; n < total_point; n++) { + if (rx_tuning_result[n] == TUNING_NUM_PER_POINT) { + if (n == 0) + wrap_win_start = n; + if (wrap_win_start >= 0) + wrap_win_size++; + if (curr_win_start < 0) + curr_win_start = n; + curr_win_size++; + pr_info("%s: rx_tuning_result[%d] = %d\n", + mmc_hostname(host->mmc), n, + TUNING_NUM_PER_POINT); + } 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; + } + + } + } + /* last point is ok! */ + 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_size <= 0) { + rxclk_find = -1; + } else { + pr_info("%s: best_win_start =%d, best_win_size =%d\n", + mmc_hostname(host->mmc), *best_win_start, + *best_win_size); + rxclk_find = *best_win_start + (*best_win_size + 1) / 2; + rxclk_find %= total_point; + } + + return rxclk_find; +} + +static int aml_sd_emmc_execute_tuning_rxclk(struct mmc_host *mmc, u32 opcode, + struct aml_tuning_data *tuning_data) +{ + /* need finish later */ + struct amlsd_host *host = mmc_priv(mmc); + struct amlsd_platform *pdata = host->pdata; + u32 vclk; + struct sd_emmc_clock *clkc = (struct sd_emmc_clock *)&(vclk); + u32 vctrl; + struct sd_emmc_config *ctrl = (struct sd_emmc_config *)&vctrl; + u32 clk_rate = 1000000000; + const u8 *blk_pattern = tuning_data->blk_pattern; + unsigned int blksz = tuning_data->blksz; + unsigned long flags; + u8 steps, nmatch; + u8 rx_phase, rx_delay; + struct aml_emmc_rxclk *emmc_rxclk = &host->emmc_rxclk; + u8 *blk_test; + u32 clock; + int rxclk_find; + u8 rx_tuning_result[25] = {0}; + u8 total_point = 25; + int best_win_start = -1, best_win_size = 0; + + spin_lock_irqsave(&host->mrq_lock, flags); + pdata->need_retuning = false; + spin_unlock_irqrestore(&host->mrq_lock, flags); + vclk = readl(host->base + SD_EMMC_CLOCK); + vctrl = readl(host->base + SD_EMMC_CFG); + + clock = clk_rate / clkc->div;/*200mhz, bus_clk */ + mmc->actual_clock = ctrl->ddr ? + (clock / 2) : clock;/*100mhz in ddr */ + + if (ctrl->ddr == 1) { + blksz = 512; + opcode = 17; + } + blk_test = kmalloc(blksz, GFP_KERNEL); + if (!blk_test) + return -ENOMEM; + + host->is_tunning = 1; + pr_info("%s: clk %d %s tuning start\n", + mmc_hostname(mmc), (ctrl->ddr ? (clock / 2) : clock), + (ctrl->ddr ? "DDR mode" : "SDR mode")); + for (rx_phase = 0; rx_phase < 4; rx_phase += 2) { + if (rx_phase == 0) + steps = 10; + else + steps = 15; + for (rx_delay = 0; rx_delay < steps; rx_delay++) { + vclk = readl(host->base + SD_EMMC_CLOCK); + clkc->rx_delay = rx_delay; + clkc->rx_phase = rx_phase; + writel(vclk, host->base + SD_EMMC_CLOCK); + pdata->clkc = vclk; + nmatch = aml_sd_emmc_tuning_transfer(mmc, opcode, + blk_pattern, blk_test, blksz); + rx_tuning_result[rx_phase * 5 + rx_delay] = nmatch; + } + } + host->is_tunning = 0; + rxclk_find = aml_sd_emmc_rxclk_find(mmc, + rx_tuning_result, total_point, + &best_win_start, &best_win_size); + + if (rxclk_find < 0) { + pr_info("%s: tuning failed\n", mmc_hostname(host->mmc)); + kfree(blk_test); + return -1; + } else if (rxclk_find < 10) { + rx_phase = 0; + rx_delay = rxclk_find; + } else { + rx_phase = 2; + rx_delay = rxclk_find - 10; + } + + pr_info("%s: rx_phase = %d, rx_delay = %d,", + mmc_hostname(host->mmc), rx_phase, rx_delay); + + vclk = readl(host->base + SD_EMMC_CLOCK); + clkc->rx_phase = rx_phase; + clkc->rx_delay = rx_delay; + writel(vclk, host->base + SD_EMMC_CLOCK); + pdata->clkc = vclk; + + emmc_rxclk->rxclk_win_start = best_win_start; + emmc_rxclk->rxclk_win_len = best_win_size; + emmc_rxclk->rxclk_rx_phase = rx_phase; + emmc_rxclk->rxclk_rx_delay = rx_delay; + emmc_rxclk->rxclk_point = rxclk_find; + + kfree(blk_test); + return 0; +} + +static int aml_mmc_execute_tuning(struct mmc_host *mmc, u32 opcode) +{ + int err = 0; + struct amlsd_host *host = mmc_priv(mmc); + struct amlsd_platform *pdata = host->pdata; + u32 adjust; + struct sd_emmc_adjust *gadjust = (struct sd_emmc_adjust *)&adjust; + u32 vclk = readl(host->base + SD_EMMC_CLOCK); + struct sd_emmc_clock *clkc = (struct sd_emmc_clock *)&(vclk); + struct aml_tuning_data tuning_data; + u32 adj_win_start = 100; + + 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 { + sd_emmc_err("Undefined command(%d) for tuning\n", opcode); + return -EINVAL; + } + +#ifdef AML_CALIBRATION + if ((aml_card_type_mmc(pdata)) + && (mmc->ios.timing != MMC_TIMING_MMC_HS400)) { + if (clkc->div <= 10) { + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXL) + err = aml_sd_emmc_execute_calibration(mmc, + &adj_win_start, 1); + else if (clkc->div <= 7) + err = aml_sd_emmc_execute_calibration(mmc, + &adj_win_start, 0); + } + /* if calibration failed, gdelay use default value */ + if (err) { + if (get_cpu_type() == MESON_CPU_MAJOR_ID_GXBB) + writel(0x85854055, host->base + SD_EMMC_DELAY); + else + writel(0x10101331, host->base + SD_EMMC_DELAY); + } + } +#endif + /* execute tuning... */ + if ((clkc->div > 5) + || (get_cpu_type() == MESON_CPU_MAJOR_ID_GXBB)) { + err = aml_sd_emmc_execute_tuning_(mmc, opcode, + &tuning_data, adj_win_start); + if (!err) + host->tuning_mode = ADJ_TUNING_MODE; + } else if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXL) { + if (aml_card_type_sdio(pdata)) { + err = aml_sd_emmc_execute_tuning_(mmc, opcode, + &tuning_data, adj_win_start); + if (!err) + host->tuning_mode = ADJ_TUNING_MODE; + } else { + err = 0; + adjust = readl(host->base + SD_EMMC_ADJUST); + gadjust->cali_enable = 1; + gadjust->adj_auto = 1; + writel(adjust, host->base + SD_EMMC_ADJUST); + host->tuning_mode = AUTO_TUNING_MODE; + } + } else { + err = aml_sd_emmc_execute_tuning_rxclk(mmc, opcode, + &tuning_data); + if (!err) + host->tuning_mode = RX_PHASE_DELAY_TUNING_MODE; + } + + pr_info("%s: clock =0x%x, delay=0x%x, adjust=0x%x\n", + mmc_hostname(mmc), + readl(host->base + SD_EMMC_CLOCK), + readl(host->base + SD_EMMC_DELAY), + readl(host->base + SD_EMMC_ADJUST)); + + return err; + +} + +static void aml_mmc_clk_switch_off(struct amlsd_host *host) +{ + u32 vcfg = 0; + struct sd_emmc_config *conf = (struct sd_emmc_config *)&vcfg; + + if (host->is_gated) { + sd_emmc_dbg(AMLSD_DBG_IOS, "direct return\n"); + return; + } + + /*Turn off Clock, here close whole clk for controller*/ + vcfg = readl(host->base + SD_EMMC_CFG); + conf->stop_clk = 1; + writel(vcfg, host->base + SD_EMMC_CFG); + + host->is_gated = true; + /* sd_emmc_err("clock off\n"); */ +} + +void aml_mmc_clk_switch_on( + struct amlsd_host *host, int clk_div, int clk_src_sel) +{ + u32 vclkc = 0; + struct sd_emmc_clock *clkc = (struct sd_emmc_clock *)&vclkc; + u32 vcfg = 0; + struct sd_emmc_config *conf = (struct sd_emmc_config *)&vcfg; + struct amlsd_platform *pdata = host->pdata; + + WARN_ON(!clk_div); + + vclkc = readl(host->base + SD_EMMC_CLOCK); + /*Set clock divide*/ + clkc->div = clk_div; + clkc->src = clk_src_sel; + writel(vclkc, host->base + SD_EMMC_CLOCK); + pdata->clkc = vclkc; + + /*Turn on Clock*/ + vcfg = readl(host->base + SD_EMMC_CFG); + conf->stop_clk = 0; + writel(vcfg, host->base + SD_EMMC_CFG); + + host->is_gated = false; +} + +static void aml_mmc_clk_switch(struct amlsd_host *host, + int clk_div, int clk_src_sel) +{ + u32 vclkc = 0; + struct sd_emmc_clock *clkc = (struct sd_emmc_clock *)&vclkc; + + vclkc = readl(host->base + SD_EMMC_CLOCK); + if (!host->is_gated && (clkc->div == clk_div) + && (clkc->src == clk_src_sel)) { + /* sd_emmc_err("direct return\n"); */ + return; /* if the same, return directly */ + } + + aml_mmc_clk_switch_off(host); + /* mdelay(1); */ + + WARN_ON(!clk_div); + + aml_mmc_clk_switch_on(host, clk_div, clk_src_sel); +} + +void aml_sd_emmc_set_clkc(struct amlsd_host *host) +{ + u32 vclkc = 0; + struct sd_emmc_clock *clkc = (struct sd_emmc_clock *)&vclkc; + struct amlsd_platform *pdata = host->pdata; + + vclkc = readl(host->base + SD_EMMC_CLOCK); + if (!host->is_gated && (pdata->clkc == vclkc)) + return; + + if (host->is_gated) + aml_mmc_clk_switch(host, clkc->div, clkc->src); + else + writel(pdata->clkc, host->base + SD_EMMC_CLOCK); +} + +static int meson_mmc_clk_set_rate(struct amlsd_host *host, + unsigned long clk_ios) +{ + struct mmc_host *mmc = host->mmc; + int ret = 0; +#ifdef SD_EMMC_CLK_CTRL + u32 clk_rate, clk_div, clk_src_sel; + struct amlsd_platform *pdata = host->pdata; +#else + u32 vcfg = 0; + struct sd_emmc_config *conf = (struct sd_emmc_config *)&vcfg; +#endif + +#ifdef SD_EMMC_CLK_CTRL + if (clk_ios == 0) { + aml_mmc_clk_switch_off(host); + return ret; + } + + clk_src_sel = SD_EMMC_CLOCK_SRC_OSC; + if (clk_ios < 20000000) + clk_src_sel = SD_EMMC_CLOCK_SRC_OSC; + else + clk_src_sel = SD_EMMC_CLOCK_SRC_FCLK_DIV2; +#endif + + if (clk_ios) { + if (WARN_ON(clk_ios > mmc->f_max)) + clk_ios = mmc->f_max; + else if (WARN_ON(clk_ios < mmc->f_min)) + clk_ios = mmc->f_min; + } + +#ifdef SD_EMMC_CLK_CTRL + WARN_ON(clk_src_sel > SD_EMMC_CLOCK_SRC_FCLK_DIV2); + switch (clk_src_sel) { + case SD_EMMC_CLOCK_SRC_OSC: + clk_rate = 24000000; + break; + case SD_EMMC_CLOCK_SRC_FCLK_DIV2: + clk_rate = 1000000000; + break; + default: + pr_err("%s: clock source error: %d\n", + mmc_hostname(host->mmc), clk_src_sel); + ret = -1; + } + clk_div = (clk_rate / clk_ios) + (!!(clk_rate % clk_ios)); + + aml_mmc_clk_switch(host, clk_div, clk_src_sel); + pdata->clkc = readl(host->base + SD_EMMC_CLOCK); + + mmc->actual_clock = clk_rate / clk_div; +#else + if (clk_ios == mmc->actual_clock) + return 0; + + /* stop clock */ + vcfg = readl(host->base + SD_EMMC_CFG); + if (!conf->stop_clk) { + conf->stop_clk = 1; + writel(vcfg, host->base + SD_EMMC_CFG); + } + + dev_dbg(host->dev, "change clock rate %u -> %lu\n", + mmc->actual_clock, clk_ios); + ret = clk_set_rate(host->cfg_div_clk, clk_ios); + if (clk_ios && clk_ios != clk_get_rate(host->cfg_div_clk)) + dev_warn(host->dev, "divider requested rate %lu != actual rate %lu: ret=%d\n", + clk_ios, clk_get_rate(host->cfg_div_clk), ret); + else + mmc->actual_clock = clk_ios; + + /* (re)start clock, if non-zero */ + if (clk_ios) { + vcfg = readl(host->base + SD_EMMC_CFG); + conf->stop_clk = 0; + writel(vcfg, host->base + SD_EMMC_CFG); + } +#endif + + return ret; +} + +static int aml_emmc_clktree_init(struct amlsd_host *host) +{ + int i, ret = 0; + unsigned int f_min = UINT_MAX, mux_parent_count = 0; + const char *mux_parent_names[MUX_CLK_NUM_PARENTS]; + struct clk_init_data init; + char clk_name[32], name[16]; + const char *clk_div_parents[1]; + + host->core_clk = devm_clk_get(host->dev, "core"); + if (IS_ERR(host->core_clk)) { + ret = PTR_ERR(host->core_clk); + return ret; + } + ret = clk_prepare_enable(host->core_clk); + if (ret) + return ret; + + /* get the mux parents */ + for (i = 0; i < MUX_CLK_NUM_PARENTS; i++) { + snprintf(name, sizeof(name), "clkin%d", i); + host->mux_parent[i] = devm_clk_get(host->dev, name); + if (IS_ERR(host->mux_parent[i])) { + ret = PTR_ERR(host->mux_parent[i]); + if (PTR_ERR(host->mux_parent[i]) != -EPROBE_DEFER) + dev_err(host->dev, "Missing clock %s\n", name); + host->mux_parent[i] = NULL; + return ret; + } + host->mux_parent_rate[i] = clk_get_rate(host->mux_parent[i]); + mux_parent_names[i] = __clk_get_name(host->mux_parent[i]); + mux_parent_count++; + if (host->mux_parent_rate[i] < f_min) + f_min = host->mux_parent_rate[i]; + ret = clk_prepare_enable(host->mux_parent[i]); + } + + /* cacluate f_min based on input clocks, and max divider value */ + if (f_min != UINT_MAX) + f_min = DIV_ROUND_UP(CLK_SRC_XTAL_RATE, CLK_DIV_MAX); + else + f_min = 400000; /* default min: 400 KHz */ + host->mmc->f_min = f_min; + + /* create the mux */ + snprintf(clk_name, sizeof(clk_name), "%s#mux", dev_name(host->dev)); + init.name = clk_name; + init.ops = &clk_mux_ops; + init.flags = 0; + init.parent_names = mux_parent_names; + init.num_parents = mux_parent_count; + host->mux.reg = host->base + SD_EMMC_CLOCK; + host->mux.shift = CLK_SRC_SHIFT; + host->mux.mask = CLK_SRC_MASK; + host->mux.flags = 0; + host->mux.table = NULL; + host->mux.hw.init = &init; + host->mux_clk = devm_clk_register(host->dev, &host->mux.hw); + if (WARN_ON(IS_ERR(host->mux_clk))) + return PTR_ERR(host->mux_clk); + + /* create the divider */ + snprintf(clk_name, sizeof(clk_name), "%s#div", dev_name(host->dev)); + init.name = devm_kstrdup(host->dev, clk_name, GFP_KERNEL); + init.ops = &clk_divider_ops; + init.flags = CLK_SET_RATE_PARENT; + clk_div_parents[0] = __clk_get_name(host->mux_clk); + init.parent_names = clk_div_parents; + init.num_parents = ARRAY_SIZE(clk_div_parents); + host->cfg_div.reg = host->base + SD_EMMC_CLOCK; + host->cfg_div.shift = CLK_DIV_SHIFT; + host->cfg_div.width = CLK_DIV_WIDTH; + host->cfg_div.hw.init = &init; + host->cfg_div.flags = CLK_DIVIDER_ONE_BASED | + CLK_DIVIDER_ROUND_CLOSEST | CLK_DIVIDER_ALLOW_ZERO; + host->cfg_div_clk = devm_clk_register(host->dev, &host->cfg_div.hw); + if (WARN_ON(PTR_ERR_OR_ZERO(host->cfg_div_clk))) + return PTR_ERR(host->cfg_div_clk); + + ret = clk_prepare_enable(host->cfg_div_clk); + + return ret; +} + +/* + * The SD/eMMC IP block has an internal mux and divider used for + * generating the MMC clock. Use the clock framework to create and + * manage these clocks. + */ +static int meson_mmc_clk_init(struct amlsd_host *host) +{ + int ret = 0; + u32 vclkc = 0; + struct sd_emmc_clock *pclkc = (struct sd_emmc_clock *)&vclkc; + u32 vconf = 0; + struct sd_emmc_config *pconf = (struct sd_emmc_config *)&vconf; + struct amlsd_platform *pdata = host->pdata; + + ret = aml_emmc_clktree_init(host); + if (ret) + return ret; + + /* init SD_EMMC_CLOCK to sane defaults w/min clock rate */ + vclkc = 0; + pclkc->div = 60; /* 400KHz */ + pclkc->src = 0; /* 0: Crystal 24MHz */ + pclkc->core_phase = 2; /* 2: 180 phase */ + pclkc->rx_phase = 0; + pclkc->tx_phase = 0; + pclkc->always_on = 1; /* Keep clock always on */ + writel(vclkc, host->base + SD_EMMC_CLOCK); + pdata->clkc = vclkc; + + vconf = 0; + /* 1bit mode */ + pconf->bus_width = 0; + /* 512byte block length */ + pconf->bl_len = 9; + /* 64 CLK cycle, here 2^8 = 256 clk cycles */ + pconf->resp_timeout = 8; + /* 1024 CLK cycle, Max. 100mS. */ + pconf->rc_cc = 4; + pconf->err_abort = 0; + pconf->auto_clk = 1; + writel(vconf, host->base + SD_EMMC_CFG); + + writel(0xffff, host->base + SD_EMMC_STATUS); + writel(SD_EMMC_IRQ_ALL, host->base + SD_EMMC_IRQ_EN); + + return ret; +} + +static void aml_sd_emmc_tx_phase_set(struct amlsd_host *host) +{ + struct amlsd_platform *pdata = host->pdata; + u32 vclkc = 0; + struct sd_emmc_clock *pclkc = (struct sd_emmc_clock *)&vclkc; + + vclkc = readl(host->base + SD_EMMC_CLOCK); + pclkc->tx_phase = pdata->tx_phase; + if (pdata->tx_delay) + pclkc->tx_delay = pdata->tx_delay; + + writel(vclkc, host->base + SD_EMMC_CLOCK); +} + +static void aml_sd_emmc_set_timing( + struct amlsd_host *host, u32 timing) +{ + struct amlsd_platform *pdata = host->pdata; + u32 vctrl; + struct sd_emmc_config *ctrl = (struct sd_emmc_config *)&vctrl; + u32 vclkc; + struct sd_emmc_clock *clkc = (struct sd_emmc_clock *)&vclkc; + u32 adjust; + struct sd_emmc_adjust *gadjust = (struct sd_emmc_adjust *)&adjust; + u8 clk_div; + u32 clk_rate = 1000000000; + + vctrl = readl(host->base + SD_EMMC_CFG); + if ((timing == MMC_TIMING_MMC_HS400) || + (timing == MMC_TIMING_MMC_DDR52) || + (timing == MMC_TIMING_UHS_DDR50)) { + if (timing == MMC_TIMING_MMC_HS400) { + ctrl->chk_ds = 1; + if (get_cpu_type() >= MESON_CPU_MAJOR_ID_GXL) { + adjust = readl(host->base + SD_EMMC_ADJUST); + gadjust->ds_enable = 1; + writel(adjust, host->base + SD_EMMC_ADJUST); + host->tuning_mode = AUTO_TUNING_MODE; + } + } + vclkc = readl(host->base + SD_EMMC_CLOCK); + ctrl->ddr = 1; + clk_div = clkc->div; + if (clk_div & 0x01) + clk_div++; + clkc->div = clk_div / 2; + writel(vclkc, host->base + SD_EMMC_CLOCK); + pdata->clkc = vclkc; + host->mmc->actual_clock = clk_rate / clk_div; + pr_info("%s: try set sd/emmc to DDR mode\n", + mmc_hostname(host->mmc)); + } else + ctrl->ddr = 0; + + writel(vctrl, host->base + SD_EMMC_CFG); + sd_emmc_dbg(AMLSD_DBG_IOS, "sd emmc is %s\n", + ctrl->ddr?"DDR mode":"SDR mode"); +} + +/*setup bus width, 1bit, 4bits, 8bits*/ +static void aml_sd_emmc_set_buswidth( + struct amlsd_host *host, u32 busw_ios) +{ + u32 vconf; + struct sd_emmc_config *conf = (struct sd_emmc_config *)&vconf; + 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: + sd_emmc_err("%s: error Data Bus\n", + mmc_hostname(host->mmc)); + break; + } + + if (width != host->bus_width) { + vconf = readl(host->base + SD_EMMC_CFG); + conf->bus_width = width; + writel(vconf, host->base + SD_EMMC_CFG); + host->bus_width = width; + sd_emmc_dbg(AMLSD_DBG_IOS, "Bus Width Ios %d\n", busw_ios); + } +} + +static void meson_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct amlsd_host *host = mmc_priv(mmc); + struct amlsd_platform *pdata = host->pdata; + + if (!pdata->is_in) + return; + + /* Set Clock */ + meson_mmc_clk_set_rate(host, ios->clock); + + /* Set Bus Width */ + aml_sd_emmc_set_buswidth(host, ios->bus_width); + + /* Set Date Mode */ + aml_sd_emmc_set_timing(host, ios->timing); +} + +#ifdef SD_EMMC_REQ_DMA_SGMAP +static char *aml_sd_emmc_kmap_atomic( + struct scatterlist *sg, unsigned long *flags) +{ + local_irq_save(*flags); + return kmap_atomic(sg_page(sg)) + sg->offset; +} + +static void aml_sd_emmc_kunmap_atomic( + void *buffer, unsigned long *flags) +{ + kunmap_atomic(buffer); + local_irq_restore(*flags); +} + +/* + * aml_sg_copy_buffer - Copy data between + * a linear buffer and an SG list for amlogic, + * We don't disable irq in this function + */ +static unsigned int aml_sd_emmc_pre_dma(struct amlsd_host *host, + struct mmc_request *mrq, struct sd_emmc_desc_info *desc) +{ + struct mmc_data *data = NULL; + struct scatterlist *sg; + struct sd_emmc_desc_info *desc_cur = NULL; + struct cmd_cfg *des_cmd_cur = NULL; + dma_addr_t sg_addr = 0; + char *buffer = NULL; + unsigned int desc_cnt = 0, i = 0, data_len = 0; + unsigned int data_size = 0, sg_blocks = 0; + unsigned char direction = 0, data_rw = 0; + unsigned char block_mode = 0, data_num = 0, bl_len = 0; + unsigned long flags; + + data = mrq->cmd->data; + if (data == NULL) { + WARN_ON(1); + goto err_exit; + } + + if (data->flags & MMC_DATA_READ) { + direction = DMA_FROM_DEVICE; + data_rw = 0; + } else{ + direction = DMA_TO_DEVICE; + data_rw = 1; + } + + host->sg_cnt = dma_map_sg(mmc_dev(host->mmc), + data->sg, data->sg_len, direction); + /* + * This only happens when someone fed + * us an invalid request. + */ + if (host->sg_cnt == 0) { + WARN_ON(1); + goto err_exit; + } +#ifdef CHOICE_DEBUG + pr_info("%s %d sg_cnt:%d, direction:%d\n", + __func__, __LINE__, host->sg_cnt, direction); +#endif + + data_size = (mrq->cmd->data->blksz * mrq->cmd->data->blocks); + block_mode = ((mrq->cmd->data->blocks > 1) + || (mrq->cmd->data->blksz >= 512)) ? 1 : 0; + + data_num = 0;/*(data_size > 4) ? 0 : 1;*/ + bl_len = block_mode ? log2i(mrq->cmd->data->blksz) : 0; + host->dma_sts = 0; + if ((data_size & 0x3) && (host->sg_cnt > 1)) { + host->dma_sts = (1<<0); /* */ + pr_info("data:%d and sg_cnt:%d\n", data_size, host->sg_cnt); + } +#ifdef CHOICE_DEBUG + pr_info("%s %d sg_cnt:%d, block_mode:%d,\n", + __func__, __LINE__, host->sg_cnt, block_mode); + pr_info("data_num:%d bl_len:%d, blocks:%d, blksz:%d\n", + data_num, bl_len, mrq->cmd->data->blocks, + mrq->cmd->data->blksz); +#endif + + /* prepare desc for data operation */ + desc_cur = desc; + + for_each_sg(data->sg, sg, data->sg_len, i) { + WARN_ON(sg->length & 0x3); + + des_cmd_cur = (struct cmd_cfg *)&(desc_cur->cmd_info); + if (desc_cnt != 0) { /* for first desc, */ + des_cmd_cur->no_resp = 1; + des_cmd_cur->no_cmd = 1; + } + des_cmd_cur->data_io = 1; + + des_cmd_cur->owner = 1; + des_cmd_cur->timeout = 0xc; + + des_cmd_cur->data_wr = data_rw; + des_cmd_cur->block_mode = block_mode; + des_cmd_cur->data_num = data_num; + + data_len = block_mode ? + (sg_dma_len(sg)>>bl_len) : sg_dma_len(sg); + + if ((data_len > 0x1ff) || (data_len == 0)) { + pr_info("Error block_mode:%d, data_len:%d, bl_len:%d\n", + block_mode, data_len, bl_len); + pr_info("mrq->cmd->data->blocks:%d, mrq->cmd->data->blksz:%d\n", + mrq->cmd->data->blocks, mrq->cmd->data->blksz); + WARN_ON(1); + } + des_cmd_cur->length = data_len; + + sg_blocks += des_cmd_cur->length; + sg_addr = sg_dma_address(sg); + + if (sg_addr & 0x7) { /* for no 64 bit addr alignment mode */ + WARN_ON(host->sg_cnt > 1); + + host->dma_sts |= (1<<1); /* */ + + host->dma_sts |= (1<<3); /* */ + desc_cur->data_addr = host->bn_dma_buf; + + if (data->flags & MMC_DATA_WRITE) { + buffer = aml_sd_emmc_kmap_atomic(sg, &flags); + memcpy(host->bn_buf, buffer, data_size); + aml_sd_emmc_kunmap_atomic(buffer, &flags); + } + } else{ + desc_cur->data_addr = sg_addr; + /* desc_cur->data_addr &= ~(1<<0); //DDR */ + } +#ifdef CHOICE_DEBUG + aml_sd_emmc_desc_print_info(desc_cur); + pr_info("%s %d desc_cur->data_addr : 0x%x\n", + "des_cmd_cur->length:%d, sg->length:%d\n", + "sg_dma_len(sg):%d, bl_len:%d\n", + __func__, __LINE__, desc_cur->data_addr, + des_cmd_cur->length, sg->length, + sg_dma_len(sg), bl_len); +#endif + desc_cur++; + desc_cnt++; + memset(desc_cur, 0, sizeof(struct sd_emmc_desc_info)); + } + + WARN_ON(desc_cnt != host->sg_cnt); + +err_exit: + return host->sg_cnt; +} + +/** + * aml_sg_copy_buffer - Copy data between + *a linear buffer and an SG list for amlogic, + * We don't disable irq in this function + **/ +static int aml_sd_emmc_post_dma(struct amlsd_host *host, + struct mmc_request *mrq) +{ + struct mmc_data *data = NULL; + struct scatterlist *sg; + char *buffer = NULL; + unsigned long flags; + int i, ret = 0; + + + data = mrq->cmd->data; + if (data == NULL) { + WARN_ON(1); + ret = -1; + goto err_exit; + } + + if ((data->flags & MMC_DATA_READ) && (host->dma_sts & (1<<1))) { + dma_sync_sg_for_cpu(mmc_dev(host->mmc), data->sg, + data->sg_len, DMA_FROM_DEVICE); + + for_each_sg(data->sg, sg, host->sg_cnt, i) { + if (sg_dma_address(sg) & 0x7) { + WARN_ON(!(host->dma_sts & (0x3<<2))); + + buffer = aml_sd_emmc_kmap_atomic(sg, &flags); + memcpy(buffer, host->bn_buf, + (mrq->data->blksz * mrq->data->blocks)); + aml_sd_emmc_kunmap_atomic(buffer, &flags); + } + } + } + + dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, + (data->flags & MMC_DATA_READ) ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + +err_exit: + return ret; +} +#endif + +static void aml_sd_emmc_check_sdio_irq(struct amlsd_host *host) +{ + u32 vstat = readl(host->base + SD_EMMC_STATUS); + struct sd_emmc_status *ista = (struct sd_emmc_status *)&vstat; + + if (host->sdio_irqen) { + if ((ista->irq_sdio || !(ista->dat_i & 0x02)) && + (host->mmc->sdio_irq_thread) && + (!atomic_read(&host->mmc->sdio_irq_thread_abort))) { + /* pr_info("signalling irq 0x%x\n", vstat); */ + mmc_signal_sdio_irq(host->mmc); + } + } +} +static int meson_mmc_request_done(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct amlsd_host *host = mmc_priv(mmc); + struct amlsd_platform *pdata = host->pdata; + unsigned long flags; + + WARN_ON(host->mrq != mrq); + + 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); + if (pdata->xfer_post) + pdata->xfer_post(mmc); + + aml_sd_emmc_check_sdio_irq(host); + mmc_request_done(host->mmc, mrq); + + return 0; +} + +static void meson_mmc_start_cmd(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct amlsd_host *host = mmc_priv(mmc); + struct amlsd_platform *pdata = host->pdata; + struct sd_emmc_desc_info *desc_cur; + struct cmd_cfg *des_cmd_cur = NULL; + u32 conf_flag = 0; + u32 vconf; + struct sd_emmc_config *pconf = (struct sd_emmc_config *)&vconf; + u32 vstart; + struct sd_emmc_start *desc_start = (struct sd_emmc_start *)&vstart; +#ifdef AML_CALIBRATION + u32 adjust; + struct sd_emmc_adjust *gadjust = (struct sd_emmc_adjust *)&adjust; +#endif + u32 desc_cnt = 0; +#ifdef SD_EMMC_REQ_DMA_SGMAP + u32 sg_len = 0; +#else + u32 cfg; + u8 blk_len; + unsigned int xfer_bytes = 0; +#endif + + /* Stop execution */ + vstart = readl(host->base + SD_EMMC_START); + desc_start->busy = 0; + writel(vstart, host->base + SD_EMMC_START); + + /* Setup descriptors */ + desc_cur = (struct sd_emmc_desc_info *)host->desc_buf; + desc_cnt++; + + memset(desc_cur, 0, sizeof(struct sd_emmc_desc_info)); + + sd_emmc_dbg(AMLSD_DBG_REQ, "%s %d cmd:%d, flags:0x%x, args:0x%x\n", + __func__, __LINE__, mrq->cmd->opcode, + mrq->cmd->flags, mrq->cmd->arg); + + vconf = readl(host->base + SD_EMMC_CFG); + /*sd/sdio switch volatile cmd11 need clock 6ms base on sd spec. */ + if (mrq->cmd->opcode == SD_SWITCH_VOLTAGE) { + conf_flag |= 1 << 0; + pconf->auto_clk = 0; + host->sd_sdio_switch_volat_done = 0; + } + if ((pconf->auto_clk) && (pdata->auto_clk_close)) { + conf_flag |= 1 << 1; + pconf->auto_clk = 0; + } + + /*check package size*/ + if (mrq->cmd->data) { + if (pconf->bl_len != log2i(mrq->data->blksz)) { + conf_flag |= 1 << 3; + pconf->bl_len = log2i(mrq->data->blksz); + } + } + if (conf_flag) + writel(vconf, host->base + SD_EMMC_CFG); + + /*Add external CMD23 for multi-block operation*/ +#ifdef SD_EMMC_MANUAL_CMD23 + if (((mrq->cmd->opcode == MMC_READ_MULTIPLE_BLOCK) + || (mrq->cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK)) + && (mrq->cmd->data) && (mrq->sbc)) { + des_cmd_cur = (struct cmd_cfg *)&(desc_cur->cmd_info); + /*Command Index*/ + des_cmd_cur->cmd_index = MMC_SET_BLOCK_COUNT; + des_cmd_cur->no_resp = 0; + des_cmd_cur->r1b = 0; + des_cmd_cur->data_io = 0; + /* 10mS for only cmd timeout */ + des_cmd_cur->timeout = 0xc; + des_cmd_cur->owner = 1; + des_cmd_cur->end_of_chain = 0; + + desc_cur->cmd_arg = mrq->cmd->data->blocks; + /* response save into resp_addr itself */ + des_cmd_cur->resp_num = 1; + desc_cur->resp_addr = 0; + desc_cnt++; + desc_cur++; + memset(desc_cur, 0, sizeof(struct sd_emmc_desc_info)); + + } +#endif + + des_cmd_cur = (struct cmd_cfg *)&(desc_cur->cmd_info); + des_cmd_cur->cmd_index = mrq->cmd->opcode; + des_cmd_cur->error = 0; + des_cmd_cur->owner = 1; + des_cmd_cur->end_of_chain = 0; + + /* Response */ + if (mrq->cmd->flags & MMC_RSP_PRESENT) { + des_cmd_cur->no_resp = 0; + if (mrq->cmd->flags & MMC_RSP_136) { + /* response save into sram*/ + des_cmd_cur->resp_128 = 1; + des_cmd_cur->resp_num = 0; + desc_cur->resp_addr = 1; + } else { + /* response save into resp_addr itself */ + des_cmd_cur->resp_num = 1; + desc_cur->resp_addr = 0; + } + + if (mrq->cmd->flags & MMC_RSP_BUSY) + des_cmd_cur->r1b = 1; + + if (!(mrq->cmd->flags & MMC_RSP_CRC)) + des_cmd_cur->resp_nocrc = 1; + } else + des_cmd_cur->no_resp = 1; + + desc_cur->cmd_arg = mrq->cmd->arg; + /* data? */ + if (mrq->cmd->data) { +#ifdef SD_EMMC_REQ_DMA_SGMAP + des_cmd_cur->timeout = 0xc; + sg_len = aml_sd_emmc_pre_dma(host, mrq, desc_cur); + WARN_ON(sg_len == 0); + desc_cnt += (sg_len - 1); + desc_cur += (sg_len - 1); /* last desc here */ +#else + desc_cur->cmd_info |= CMD_CFG_DATA_IO; + if (mrq->cmd->data->blocks > 1) { + desc_cur->cmd_info |= CMD_CFG_BLOCK_MODE; + desc_cur->cmd_info |= + (mrq->cmd->data->blocks & CMD_CFG_LENGTH_MASK) + << CMD_CFG_LENGTH_SHIFT; + + /* check if block-size matches, if not update */ + cfg = readl(host->base + SD_EMMC_CFG); + blk_len = cfg & (CFG_BLK_LEN_MASK << CFG_BLK_LEN_SHIFT); + blk_len >>= CFG_BLK_LEN_SHIFT; + if (blk_len != ilog2(mrq->cmd->data->blksz)) { + dev_warn(host->dev, "%s: update blk_len %d -> %d\n", + __func__, blk_len, + ilog2(mrq->cmd->data->blksz)); + blk_len = ilog2(mrq->cmd->data->blksz); + cfg &= ~(CFG_BLK_LEN_MASK << CFG_BLK_LEN_SHIFT); + cfg |= blk_len << CFG_BLK_LEN_SHIFT; + writel(cfg, host->base + SD_EMMC_CFG); + } + } else { + desc_cur->cmd_info &= ~CMD_CFG_BLOCK_MODE; + desc_cur->cmd_info |= + (mrq->cmd->data->blksz & CMD_CFG_LENGTH_MASK) + << CMD_CFG_LENGTH_SHIFT; + } + + mrq->cmd->data->bytes_xfered = 0; + xfer_bytes = mrq->cmd->data->blksz * mrq->cmd->data->blocks; + if (mrq->cmd->data->flags & MMC_DATA_WRITE) { + desc_cur->cmd_info |= CMD_CFG_DATA_WR; + WARN_ON(xfer_bytes > SD_EMMC_BOUNCE_REQ_SIZE); + sg_copy_to_buffer(mrq->cmd->data->sg, + mrq->cmd->data->sg_len, + host->bn_buf, xfer_bytes); + mrq->cmd->data->bytes_xfered = xfer_bytes; + dma_wmb(); + } else { + desc_cur->cmd_info &= ~CMD_CFG_DATA_WR; + } + + if (xfer_bytes > 0) { + desc_cur->cmd_info &= ~CMD_CFG_DATA_NUM; + desc_cur->data_addr = host->bn_dma_buf & CMD_DATA_MASK; + } else { + /* write data to data_addr */ + desc_cur->cmd_info |= CMD_CFG_DATA_NUM; + desc_cur->data_addr = 0; + } + + cmd_cfg_timeout = 12; +#endif + +#ifdef SD_EMMC_MANUAL_CMD23 + if (((mrq->cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK) + || (mrq->cmd->opcode == MMC_READ_MULTIPLE_BLOCK)) + && (!host->cmd_is_stop) && (!mrq->sbc) + && !(mrq->cmd->flags & (1 << 30))) { + + /* pr_info("Send stop command here\n"); */ + + /* for stop command, + * add another descriptor for stop command + */ + desc_cnt++; + desc_cur++; + memset(desc_cur, 0, sizeof(struct sd_emmc_desc_info)); + + des_cmd_cur = (struct cmd_cfg *)&(desc_cur->cmd_info); + + /*Command Index*/ + des_cmd_cur->cmd_index = MMC_STOP_TRANSMISSION; + des_cmd_cur->no_resp = 0; + des_cmd_cur->r1b = 1; + des_cmd_cur->data_io = 0; + /* 10mS for only cmd timeout */ + des_cmd_cur->timeout = 0xc; + des_cmd_cur->owner = 1; + + /* response save into resp_addr itself */ + des_cmd_cur->resp_num = 1; + desc_cur->resp_addr = 0; + } +#endif + } else { + des_cmd_cur->data_io = 0; + /* Current 10uS based. 2^10 = 10mS for only cmd timeout */ + des_cmd_cur->timeout = 0xa; + } + + if (mrq->cmd->opcode == MMC_SEND_STATUS) + des_cmd_cur->timeout = 0xb; + if (mrq->cmd->opcode == MMC_ERASE) + des_cmd_cur->timeout = 0xf; + + host->cmd = mrq->cmd; + + /* Last descriptor */ + des_cmd_cur = (struct cmd_cfg *)&(desc_cur->cmd_info); + des_cmd_cur->end_of_chain = 1; + writel(SD_EMMC_IRQ_ALL, host->base + SD_EMMC_STATUS); + + vstart = readl(host->base + SD_EMMC_START); + desc_start->init = 0; + desc_start->busy = 1; + desc_start->addr = host->desc_dma_addr >> 2; + + dma_rmb(); + wmb(); /* ensure descriptor is written before kicked */ +#ifdef AML_CALIBRATION + if ((mrq->cmd->opcode == 18) && (pdata->caling == 1)) { + adjust = readl(host->base + SD_EMMC_ADJUST); + gadjust->cali_rise = 0; + gadjust->cali_sel = pdata->c_ctrl.line_x; + gadjust->cali_enable = 1; + writel(adjust, host->base + SD_EMMC_ADJUST); + } +#endif + writel(vstart, host->base + SD_EMMC_START); +} + +static void meson_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct amlsd_host *host = mmc_priv(mmc); + struct amlsd_platform *pdata = host->pdata; + unsigned long flags; + + WARN_ON(!mmc); + WARN_ON(!mrq); + + if (aml_check_unsupport_cmd(mmc, mrq)) + return; + + sd_emmc_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 ((pdata->is_in) && (mrq->cmd->opcode == 0)) + host->init_flag = 1; + + /*clear error flag if last command retried failed */ + if (host->error_flag & (1 << 30)) + host->error_flag = 0; + + /*clear pinmux & set pinmux*/ + if (pdata->xfer_pre) + pdata->xfer_pre(mmc); + + spin_lock_irqsave(&host->mrq_lock, flags); + host->mrq = mrq; + host->opcode = mrq->cmd->opcode; + + meson_mmc_start_cmd(mmc, mrq); + host->xfer_step = XFER_START; + spin_unlock_irqrestore(&host->mrq_lock, flags); +} + +static int meson_mmc_read_resp(struct mmc_host *mmc, struct mmc_command *cmd) +{ + struct amlsd_host *host = mmc_priv(mmc); + struct sd_emmc_desc_info *desc_info = + (struct sd_emmc_desc_info *)host->desc_buf; + struct cmd_cfg *des_cmd_cur = NULL; + int i; + + for (i = 0; i < (SD_EMMC_MAX_DESC_MUN>>2); i++) { + des_cmd_cur = (struct cmd_cfg *)&(desc_info->cmd_info); + if (des_cmd_cur->cmd_index == cmd->opcode) + break; + desc_info++; + } + if (cmd->flags & MMC_RSP_136) { + cmd->resp[0] = readl(host->base + SD_EMMC_CMD_RSP3); + cmd->resp[1] = readl(host->base + SD_EMMC_CMD_RSP2); + cmd->resp[2] = readl(host->base + SD_EMMC_CMD_RSP1); + cmd->resp[3] = readl(host->base + SD_EMMC_CMD_RSP); + } else if (cmd->flags & MMC_RSP_PRESENT) { + cmd->resp[0] = desc_info->resp_addr; + } + + return 0; +} + +void aml_host_bus_fsm_show(struct amlsd_host *host, int fsm_val) +{ + switch (fsm_val) { + case BUS_FSM_IDLE: + sd_emmc_err("%s: err: idle, bus_fsm:0x%x\n", + mmc_hostname(host->mmc), fsm_val); + break; + case BUS_FSM_SND_CMD: + sd_emmc_err("%s: err: send cmd, bus_fsm:0x%x\n", + mmc_hostname(host->mmc), fsm_val); + break; + case BUS_FSM_CMD_DONE: + sd_emmc_err("%s: err: wait for cmd done, bus_fsm:0x%x\n", + mmc_hostname(host->mmc), fsm_val); + break; + case BUS_FSM_RESP_START: + sd_emmc_err("%s: err: resp start, bus_fsm:0x%x\n", + mmc_hostname(host->mmc), fsm_val); + break; + case BUS_FSM_RESP_DONE: + sd_emmc_err("%s: err: wait for resp done, bus_fsm:0x%x\n", + mmc_hostname(host->mmc), fsm_val); + break; + case BUS_FSM_DATA_START: + sd_emmc_err("%s: err: data start, bus_fsm:0x%x\n", + mmc_hostname(host->mmc), fsm_val); + break; + case BUS_FSM_DATA_DONE: + sd_emmc_err("%s: err: wait for data done, bus_fsm:0x%x\n", + mmc_hostname(host->mmc), fsm_val); + break; + case BUS_FSM_DESC_WRITE_BACK: + sd_emmc_err("%s: err: wait for desc write back, bus_fsm:0x%x\n", + mmc_hostname(host->mmc), fsm_val); + break; + case BUS_FSM_IRQ_SERVICE: + sd_emmc_err("%s: err: wait for irq service, bus_fsm:0x%x\n", + mmc_hostname(host->mmc), fsm_val); + break; + default: + sd_emmc_err("%s: err: unknown err, bus_fsm:0x%x\n", + mmc_hostname(host->mmc), fsm_val); + break; + } +} + +void mmc_cmd_LBA_show(struct mmc_host *mmc, struct mmc_request *mrq) +{ + int i; + uint64_t offset, size; + struct partitions *pp; + + if (!pt_fmt || !mrq->cmd->arg) /* no disk or no arg, nothing to do */ + return; + + for (i = 0; i < pt_fmt->part_num; i++) { + pp = &(pt_fmt->partitions[i]); + offset = pp->offset >> 9; /* unit:512 bytes */ + size = pp->size >> 9; /* unit:512 bytes */ + + if ((mrq->cmd->arg >= offset) + && (mrq->cmd->arg < (offset + size))) { + sd_emmc_err("%s: cmd %d, arg 0x%x, operation is in [%s] disk!\n", + mmc_hostname(mmc), + mrq->cmd->opcode, mrq->cmd->arg, pp->name); + break; + } + } + if (i == pt_fmt->part_num) + sd_emmc_err("%s: cmd %d, arg 0x%x, operation is in [unknown] disk!\n", + mmc_hostname(mmc), + mrq->cmd->opcode, mrq->cmd->arg); +} + +static irqreturn_t meson_mmc_irq(int irq, void *dev_id) +{ + struct amlsd_host *host = dev_id; + struct mmc_request *mrq; + struct mmc_command *cmd = host->cmd; + unsigned long flags; + struct amlsd_platform *pdata = host->pdata; + struct mmc_host *mmc; + unsigned int err_flag = 0; + u32 vstat = 0; + u32 virqc = 0; + u32 vstart = 0; + struct sd_emmc_irq_en *irqc = (struct sd_emmc_irq_en *)&virqc; + struct sd_emmc_status *ista = (struct sd_emmc_status *)&vstat; + struct sd_emmc_start *desc_start = (struct sd_emmc_start *)&vstart; + + if (WARN_ON(!host)) + return IRQ_NONE; + + spin_lock_irqsave(&host->mrq_lock, flags); + virqc = readl(host->base + SD_EMMC_IRQ_EN) & 0xffff; + vstat = readl(host->base + SD_EMMC_STATUS) & 0xffffffff; + host->ista = vstat; + + sd_emmc_dbg(AMLSD_DBG_REQ, "%s %d occurred, vstat:0x%x\n", + __func__, __LINE__, vstat); + + if (irqc->irq_sdio && ista->irq_sdio) { + if ((host->mmc->sdio_irq_thread) + && (!atomic_read(&host->mmc->sdio_irq_thread_abort))) { + mmc_signal_sdio_irq(host->mmc); + if (!(vstat & 0x3fff)) + return IRQ_HANDLED; + } + } else if (!(vstat & 0x3fff)) + return IRQ_HANDLED; + + mrq = host->mrq; + mmc = host->mmc; + + vstart = readl(host->base + SD_EMMC_START); + if ((desc_start->busy == 1) + && (aml_card_type_mmc(pdata) || + (aml_card_type_non_sdio(pdata)))) { + desc_start->busy = 0; + writel(vstart, host->base + SD_EMMC_START); + } + if (!mmc) { + pr_info("sd_emmc_regs: irq_en = 0x%x at line %d\n", + readl(host->base + SD_EMMC_IRQ_EN), __LINE__); + pr_info("sd_emmc_regs: status = 0x%x at line %d\n", + readl(host->base + SD_EMMC_STATUS), __LINE__); + pr_info("sd_emmc_regs: cfg = 0x%x at line %d\n", + readl(host->base + SD_EMMC_CFG), __LINE__); + pr_info("sd_emmc_regs: clock = 0x%x at line %d\n", + readl(host->base + SD_EMMC_CLOCK), __LINE__); + } + +#ifdef CHOICE_DEBUG + pr_info("%s %d cmd:%d arg:0x%x ", + __func__, __LINE__, mrq->cmd->opcode, mrq->cmd->arg); + if (mrq->cmd->data) + pr_info("blksz:%d, blocks:%d\n", + mrq->data->blksz, mrq->data->blocks); +#endif + + if (!mrq && !irqc->irq_sdio) { + if (!ista->irq_sdio) { + sd_emmc_err("NULL mrq in aml_sd_emmc_irq step %d", + host->xfer_step); + sd_emmc_err("status:0x%x,irq_c:0x%0x\n", + readl(host->base + SD_EMMC_STATUS), + readl(host->base + SD_EMMC_IRQ_EN)); + } + if (host->xfer_step == XFER_FINISHED || + host->xfer_step == XFER_TIMER_TIMEOUT){ + spin_unlock_irqrestore(&host->mrq_lock, flags); + return IRQ_HANDLED; + } +#ifdef CHOICE_DEBUG +/* aml_sd_emmc_print_reg(host);*/ +#endif + spin_unlock_irqrestore(&host->mrq_lock, flags); + return IRQ_HANDLED; + } +#ifdef CHOICE_DEBUG + if ((host->xfer_step != XFER_AFTER_START) + && (!host->cmd_is_stop) && !irqc->irq_sdio) { + sd_emmc_err("%s: host->xfer_step=%d\n", + mmc_hostname(mmc), host->xfer_step); + pr_info("%%sd_emmc_regs: irq_en = 0x%x at line %d\n", + readl(host->base + SD_EMMC_IRQ_EN), __LINE__); + pr_info("%%sd_emmc_regs: status = 0x%x at line %d\n", + readl(host->base + SD_EMMC_STATUS), __LINE__); + pr_info("%%sd_emmc_regs: cfg = 0x%x at line %d\n", + readl(host->base + SD_EMMC_CFG), __LINE__); + pr_info("%%sd_emmc_regs: clock = 0x%x at line %d\n", + readl(host->base + SD_EMMC_CLOCK), __LINE__); + } +#endif + if (mrq) { + if (host->cmd_is_stop) + host->xfer_step = XFER_IRQ_TASKLET_BUSY; + else + host->xfer_step = XFER_IRQ_OCCUR; + } + + /* ack all (enabled) interrupts */ + writel(0x7fff, host->base + SD_EMMC_STATUS); + spin_unlock_irqrestore(&host->mrq_lock, flags); + + cmd->error = 0; + if (vstat & 0x7fff) + err_flag = 1; + if (err_flag) { + if (ista->end_of_chain || ista->desc_irq) { + if (mrq->data) + host->status = HOST_TASKLET_DATA; + else + host->status = HOST_TASKLET_CMD; + mrq->cmd->error = 0; + } + if (ista->rxd_err || ista->txd_err) { + host->status = HOST_DAT_CRC_ERR; + cmd->error = -EILSEQ; + if (host->is_tunning == 0) { + sd_emmc_err("%s: warning... data crc, vstat:0x%x, virqc:%x", + mmc_hostname(host->mmc), + vstat, virqc); + sd_emmc_err("@ cmd %d with %p; stop %d, status %d\n", + mrq->cmd->opcode, mrq->data, + host->cmd_is_stop, + host->status); + } + } else if (ista->desc_err) { + if (host->is_tunning == 0) + sd_emmc_err("%s: warning... desc err,vstat:0x%x,virqc:%x\n", + mmc_hostname(host->mmc), + vstat, virqc); + host->status = HOST_DAT_CRC_ERR; + cmd->error = -EILSEQ; + } else if (ista->resp_err) { + if (host->is_tunning == 0) + sd_emmc_err("%s: warning... response crc,vstat:0x%x,virqc:%x\n", + mmc_hostname(host->mmc), + vstat, virqc); + pr_info("%s %d cmd:%d arg:0x%x ", + __func__, __LINE__, + mrq->cmd->opcode, mrq->cmd->arg); + host->status = HOST_RSP_CRC_ERR; + cmd->error = -EILSEQ; + } else if (ista->resp_timeout) { + if (host->is_tunning == 0) + sd_emmc_err("%s: resp_timeout,vstat:0x%x,virqc:%x\n", + mmc_hostname(host->mmc), + vstat, virqc); + host->status = HOST_RSP_TIMEOUT_ERR; + cmd->error = -ETIMEDOUT; + } else if (ista->desc_timeout) { + if (host->is_tunning == 0) + sd_emmc_err("%s: desc_timeout,vstat:0x%x,virqc:%x\n", + mmc_hostname(host->mmc), + vstat, virqc); + host->status = HOST_DAT_TIMEOUT_ERR; + cmd->error = -ETIMEDOUT; + } + if (cmd->error) { + if (host->is_tunning == 0) + aml_host_bus_fsm_show(host, ista->bus_fsm); + if (aml_card_type_mmc(pdata)) + mmc_cmd_LBA_show(mmc, mrq); + } + } else { + host->xfer_step = XFER_IRQ_UNKNOWN_IRQ; + sd_emmc_err("%s: %s Unknown Irq Ictl 0x%x, Ista 0x%x\n", + mmc_hostname(host->mmc), + pdata->pinname, virqc, vstat); + dev_warn(host->dev, "Unknown IRQ! status=0x%04x: ", host->ista); + dev_warn(host->dev, "MMC CMD%u arg=0x%08x flags=0x%08x stop=%d\n", + cmd->opcode, cmd->arg, + cmd->flags, mrq->stop ? 1 : 0); + if (cmd->data) { + struct mmc_data *data = cmd->data; + + dev_warn(host->dev, "\tblksz %u blocks %u flags 0x%08x (%s%s)", + data->blksz, data->blocks, data->flags, + data->flags & MMC_DATA_WRITE ? + "write" : "", + data->flags & MMC_DATA_READ ? + "read" : ""); + } + } + + if (host->xfer_step == XFER_IRQ_UNKNOWN_IRQ) { + meson_mmc_read_resp(host->mmc, cmd); + meson_mmc_request_done(host->mmc, cmd->mrq); + return IRQ_HANDLED; + } + return IRQ_WAKE_THREAD; +} + +struct mmc_command aml_sd_emmc_cmd = { + .opcode = MMC_STOP_TRANSMISSION, + .flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC, +}; +struct mmc_request aml_sd_emmc_stop = { + .cmd = &aml_sd_emmc_cmd, +}; + +void aml_sd_emmc_send_stop(struct amlsd_host *host) +{ + unsigned long flags; + + /*Already in mrq_lock*/ + spin_lock_irqsave(&host->mrq_lock, flags); + host->error_bak = host->mrq->cmd->error; + host->mrq->cmd->error = 0; + host->cmd_is_stop = 1; + meson_mmc_start_cmd(host->mmc, &aml_sd_emmc_stop); + spin_unlock_irqrestore(&host->mrq_lock, flags); +} + +static irqreturn_t meson_mmc_irq_thread(int irq, void *dev_id) +{ + struct amlsd_host *host = dev_id; + struct amlsd_platform *pdata = host->pdata; + unsigned long flags; + struct mmc_request *mrq; + struct mmc_command *cmd = host->cmd; + + u32 status, rx_phase, xfer_bytes = 0; + enum aml_mmc_waitfor xfer_step; + struct aml_emmc_adjust *emmc_adj = &host->emmc_adj; + struct aml_emmc_rxclk *emmc_rxclk = &host->emmc_rxclk; + u32 adjust; + struct sd_emmc_adjust *gadjust = (struct sd_emmc_adjust *)&adjust; + u32 vclk; + struct sd_emmc_clock *clkc = (struct sd_emmc_clock *)&(vclk); + + 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)) { + sd_emmc_err("Warning: %s xfer_step=%d, host->status=%d\n", + mmc_hostname(host->mmc), 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) { + sd_emmc_err("%s: !mrq xfer_step %d\n", + mmc_hostname(host->mmc), xfer_step); + if (xfer_step == XFER_FINISHED || + xfer_step == XFER_TIMER_TIMEOUT){ + spin_unlock_irqrestore(&host->mrq_lock, flags); + return IRQ_HANDLED; + } +/* aml_sd_emmc_print_err(host);*/ + } + + if (host->cmd_is_stop) { + /* --new irq enter, */ + host->cmd_is_stop = 0; + mrq->cmd->error = host->error_bak; + spin_unlock_irqrestore(&host->mrq_lock, flags); + meson_mmc_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: + case HOST_TASKLET_CMD: + /* WARN_ON(aml_sd_emmc_desc_check(host)); */ + sd_emmc_dbg(AMLSD_DBG_REQ, "%s %d cmd:%d\n", + __func__, __LINE__, mrq->cmd->opcode); + host->error_flag = 0; + if (mrq->cmd->data && mrq->cmd->opcode) { + xfer_bytes = mrq->data->blksz*mrq->data->blocks; + /* copy buffer from dma to data->sg in read cmd*/ +#ifdef SD_EMMC_REQ_DMA_SGMAP + WARN_ON(aml_sd_emmc_post_dma(host, mrq)); +#else + if (host->mrq->data->flags & MMC_DATA_READ) { + WARN_ON(xfer_bytes > SD_EMMC_BOUNCE_REQ_SIZE); + sg_copy_from_buffer(mrq->data->sg, + mrq->data->sg_len, + host->bn_buf, xfer_bytes); + } + } + +#endif + mrq->data->bytes_xfered = xfer_bytes; + host->xfer_step = XFER_TASKLET_DATA; + } else { + host->xfer_step = XFER_TASKLET_CMD; + } + spin_lock_irqsave(&host->mrq_lock, flags); + mrq->cmd->error = 0; + spin_unlock_irqrestore(&host->mrq_lock, flags); + + /* check ready?? */ + /*Wait command busy*/ + meson_mmc_read_resp(host->mmc, cmd); + meson_mmc_request_done(host->mmc, host->mrq); + + break; + + case HOST_RSP_TIMEOUT_ERR: + case HOST_DAT_TIMEOUT_ERR: + case HOST_RSP_CRC_ERR: + case HOST_DAT_CRC_ERR: + if (host->is_tunning == 0) + pr_info("%s %d %s: cmd:%d\n", __func__, __LINE__, + mmc_hostname(host->mmc), mrq->cmd->opcode); + if (mrq->cmd->data) { + dma_unmap_sg(mmc_dev(host->mmc), mrq->cmd->data->sg, + mrq->cmd->data->sg_len, + (mrq->cmd->data->flags & MMC_DATA_READ) ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + } + meson_mmc_read_resp(host->mmc, cmd); + + /* set retry @ 1st error happens! */ + if ((host->error_flag == 0) + && (aml_card_type_mmc(pdata) + || aml_card_type_non_sdio(pdata)) + && (host->is_tunning == 0)) { + + sd_emmc_err("%s() %d: set 1st retry!\n", + __func__, __LINE__); + host->error_flag |= (1<<0); + spin_lock_irqsave(&host->mrq_lock, flags); + mrq->cmd->retries = AML_ERROR_RETRY_COUNTER; + spin_unlock_irqrestore(&host->mrq_lock, flags); + } + + if (aml_card_type_non_sdio(pdata) + && (host->error_flag & (1<<0)) + && mrq->cmd->retries + /* && host->mmc->uhs_speed*/) { + sd_emmc_err("retry cmd %d the %d-th time(s)\n", + mrq->cmd->opcode, mrq->cmd->retries); + vclk = readl(host->base + SD_EMMC_CLOCK); + rx_phase = clkc->rx_phase; + rx_phase++; + rx_phase %= 4; + pr_err("sd, retry, rx_phase %d -> %d\n", + clkc->rx_phase, rx_phase); + clkc->rx_phase = rx_phase; + writel(vclk, host->base + SD_EMMC_CLOCK); + pdata->clkc = vclk; + } + + if (aml_card_type_mmc(pdata) && + (host->error_flag & (1<<0)) && mrq->cmd->retries) { + sd_emmc_err("retry cmd %d the %d-th time(s)\n", + mrq->cmd->opcode, mrq->cmd->retries); + /* change configs on current host */ + switch (host->tuning_mode) { + case AUTO_TUNING_MODE: + if ((status == HOST_RSP_TIMEOUT_ERR) + || (status == HOST_RSP_CRC_ERR)) { + adjust = readl(host->base + + SD_EMMC_ADJUST); + if (gadjust->cmd_delay <= 13) + gadjust->cmd_delay += 2; + else if (gadjust->cmd_delay % 2) + gadjust->cmd_delay = 0; + else + gadjust->cmd_delay = 1; + writel(adjust, host->base + + SD_EMMC_ADJUST); + sd_emmc_err("cmd_delay change to %d\n", + gadjust->cmd_delay); + } + break; + case ADJ_TUNING_MODE: + adjust = readl(host->base + SD_EMMC_ADJUST); + emmc_adj->adj_point++; + emmc_adj->adj_point %= emmc_adj->clk_div; + pr_err("emmc, %d retry, adj %d -> %d\n", + mrq->cmd->retries, + gadjust->adj_delay, + emmc_adj->adj_point); + + /*set new adjust!*/ + gadjust->adj_delay = emmc_adj->adj_point; + gadjust->adj_enable = 1; + writel(adjust, host->base + SD_EMMC_ADJUST); + break; + case RX_PHASE_DELAY_TUNING_MODE: + vclk = readl(host->base + SD_EMMC_CLOCK); + emmc_rxclk->rxclk_point++; + emmc_rxclk->rxclk_point %= 25; + if (emmc_rxclk->rxclk_point < 10) { + emmc_rxclk->rxclk_rx_phase = 0; + emmc_rxclk->rxclk_rx_delay + = emmc_rxclk->rxclk_point; + } else { + emmc_rxclk->rxclk_rx_phase = 2; + emmc_rxclk->rxclk_rx_delay + = emmc_rxclk->rxclk_point - 10; + } + pr_err("emmc, %d retry, rx_phase %d -> %d, rx_delay %d -> %d\n", + mrq->cmd->retries, + clkc->rx_phase, + emmc_rxclk->rxclk_rx_phase, + clkc->rx_delay, + emmc_rxclk->rxclk_rx_delay); + clkc->rx_phase = emmc_rxclk->rxclk_rx_phase; + clkc->rx_delay = emmc_rxclk->rxclk_rx_delay; + writel(vclk, host->base + SD_EMMC_CLOCK); + pdata->clkc = vclk; + break; + case NONE_TUNING: + default: + vclk = readl(host->base + SD_EMMC_CLOCK); + rx_phase = clkc->rx_phase; + rx_phase++; + rx_phase %= 4; + pr_err("emmc: retry, rx_phase %d -> %d\n", + clkc->rx_phase, rx_phase); + clkc->rx_phase = rx_phase; + writel(vclk, host->base + SD_EMMC_CLOCK); + pdata->clkc = vclk; + break; + } + } + /* last retry effort! */ + if ((aml_card_type_mmc(pdata) || aml_card_type_non_sdio(pdata)) + && host->error_flag && (mrq->cmd->retries == 0)) { + host->error_flag |= (1<<30); + sd_emmc_err("Command retried failed line:%d, cmd:%d\n", + __LINE__, mrq->cmd->opcode); + } + /* retry need send a stop 2 emmc... */ + /* do not send stop for sdio wifi case */ + if (host->mrq->stop + && (aml_card_type_mmc(pdata) + || aml_card_type_non_sdio(pdata)) + && pdata->is_in + && (host->mrq->cmd->opcode != MMC_SEND_TUNING_BLOCK) + && (host->mrq->cmd->opcode != + MMC_SEND_TUNING_BLOCK_HS200)) + aml_sd_emmc_send_stop(host); + else + meson_mmc_request_done(host->mmc, host->mrq); + break; + + default: + sd_emmc_err("BUG %s: xfer_step=%d, host->status=%d\n", + mmc_hostname(host->mmc), xfer_step, status); +/* aml_sd_emmc_print_err(host);*/ + } + + return IRQ_HANDLED; +} + +static void aml_sd_emmc_enable_sdio_irq(struct mmc_host *mmc, int enable) +{ + struct amlsd_host *host = mmc_priv(mmc); + struct amlsd_platform *pdata = host->pdata; + unsigned long flags; + /* u32 vstat = 0; */ + u32 vclkc = 0; + struct sd_emmc_clock *pclock = (struct sd_emmc_clock *)&vclkc; + u32 vconf = 0; + struct sd_emmc_config *pconf = (struct sd_emmc_config *)&vconf; + u32 virqc = 0; + struct sd_emmc_irq_en *irqc = (struct sd_emmc_irq_en *)&virqc; + + host->sdio_irqen = enable; + spin_lock_irqsave(&host->mrq_lock, flags); + vclkc = readl(host->base + SD_EMMC_CLOCK); + vconf = readl(host->base + SD_EMMC_CFG); + virqc = readl(host->base + SD_EMMC_IRQ_EN); + + pclock->irq_sdio_sleep = 1; + pclock->irq_sdio_sleep_ds = 0; + pconf->irq_ds = 0; + + /* vstat = sd_emmc_regs->gstatus&SD_EMMC_IRQ_ALL; */ + if (enable) + irqc->irq_sdio = 1; + else + irqc->irq_sdio = 0; + + writel(virqc, host->base + SD_EMMC_IRQ_EN); + writel(vclkc, host->base + SD_EMMC_CLOCK); + writel(vconf, host->base + SD_EMMC_CFG); + pdata->clkc = vclkc; + + spin_unlock_irqrestore(&host->mrq_lock, flags); + + /* check if irq already occurred */ + aml_sd_emmc_check_sdio_irq(host); +} + +/*get readonly: 0 for rw, 1 for ro*/ +static int aml_sd_emmc_get_ro(struct mmc_host *mmc) +{ + struct amlsd_host *host = mmc_priv(mmc); + struct amlsd_platform *pdata = host->pdata; + u32 ro = 0; + + if (pdata->ro) + ro = pdata->ro(pdata); + return ro; +} + +/* + * NOTE: we only need this until the GPIO/pinctrl driver can handle + * interrupts. For now, the MMC core will use this for polling. + */ +static int meson_mmc_get_cd(struct mmc_host *mmc) +{ + struct amlsd_host *host = mmc_priv(mmc); + struct amlsd_platform *pdata = host->pdata; + + return pdata->is_in; +} + +int aml_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_sd_emmc_card_busy(struct mmc_host *mmc) +{ + struct amlsd_host *host = mmc_priv(mmc); + struct amlsd_platform *pdata = host->pdata; + unsigned int status = 0; + /* only check data3_0 gpio level?? */ + u32 vstat; + struct sd_emmc_status *ista = (struct sd_emmc_status *)&vstat; + u32 vconf; + struct sd_emmc_config *pconf = (struct sd_emmc_config *)&vconf; + + vstat = readl(host->base + SD_EMMC_STATUS); + status = ista->dat_i & 0xf; + + /*must open auto_clk after sd/sdio switch volatile base on sd spec.*/ + if ((!aml_card_type_mmc(pdata)) + && (host->sd_sdio_switch_volat_done)) { + vconf = readl(host->base + SD_EMMC_CFG); + pconf->auto_clk = 1; + writel(vconf, host->base + SD_EMMC_CFG); + } + return !status; +} + +/*this function tells wifi is using sd(sdiob) or sdio(sdioa)*/ +const char *get_wifi_inf(void) +{ + if (sdio_host != NULL) + return mmc_hostname(sdio_host); + else + return "sdio"; + +} +EXPORT_SYMBOL(get_wifi_inf); + +static const struct mmc_host_ops meson_mmc_ops = { + .request = meson_mmc_request, + .set_ios = meson_mmc_set_ios, + .enable_sdio_irq = aml_sd_emmc_enable_sdio_irq, + .get_cd = meson_mmc_get_cd, + .get_ro = aml_sd_emmc_get_ro, + .start_signal_voltage_switch = aml_signal_voltage_switch, + .card_busy = aml_sd_emmc_card_busy, + .execute_tuning = aml_mmc_execute_tuning, + .hw_reset = aml_emmc_hw_reset, +}; + +static void aml_reg_print(struct amlsd_host *host) +{ + struct amlsd_platform *pdata = host->pdata; + + pr_info("%s reg val:\n", pdata->pinname); + pr_info("SD_EMMC_CLOCK = 0x%x\n", readl(host->base + SD_EMMC_CLOCK)); + pr_info("SD_EMMC_CFG = 0x%x\n", readl(host->base + SD_EMMC_CFG)); + pr_info("SD_EMMC_STATUS = 0x%x\n", readl(host->base + SD_EMMC_STATUS)); + pr_info("SD_EMMC_IRQ_EN = 0x%x\n", readl(host->base + SD_EMMC_IRQ_EN)); +}; + +static int meson_mmc_probe(struct platform_device *pdev) +{ + struct resource *res_mem; + struct amlsd_host *host; + struct amlsd_platform *pdata = NULL; + struct mmc_host *mmc; + int ret; + + aml_mmc_ver_msg_show(); + + pdata = kzalloc(sizeof(struct amlsd_platform), GFP_KERNEL); + if (!pdata) + ret = -ENOMEM; + + mmc = mmc_alloc_host(sizeof(struct amlsd_host), &pdev->dev); + if (!mmc) + return -ENOMEM; + + host = mmc_priv(mmc); + host->mmc = mmc; + host->pdev = pdev; + host->dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, host); + + host->pinmux_base = ioremap(0xc8834400, 0x200); + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + host->base = devm_ioremap_resource(&pdev->dev, res_mem); + if (IS_ERR(host->base)) { + ret = PTR_ERR(host->base); + goto free_host; + } + + host->irq = platform_get_irq(pdev, 0); + if (host->irq == 0) { + dev_err(&pdev->dev, "failed to get interrupt resource.\n"); + ret = -EINVAL; + goto free_host; + } + + ret = devm_request_threaded_irq(&pdev->dev, host->irq, + meson_mmc_irq, meson_mmc_irq_thread, + IRQF_SHARED, "meson-aml-mmc", host); + if (ret) + goto free_host; + + /* data desc buffer */ + host->desc_buf = + dma_alloc_coherent(host->dev, + SD_EMMC_MAX_DESC_MUN + * (sizeof(struct sd_emmc_desc_info)), + &host->desc_dma_addr, GFP_KERNEL); + if (host->desc_buf == NULL) { + dev_err(host->dev, "Unable to map allocate DMA desc buffer.\n"); + ret = -ENOMEM; + goto free_host; + } + + /* data bounce buffer */ + host->bn_buf = + dma_alloc_coherent(host->dev, SD_EMMC_BOUNCE_REQ_SIZE, + &host->bn_dma_buf, GFP_KERNEL); + if (host->bn_buf == NULL) { + dev_err(host->dev, "Unable to map allocate DMA bounce buffer.\n"); + ret = -ENOMEM; + goto free_host; + } + + host->pdata = pdata; + spin_lock_init(&host->mrq_lock); + mutex_init(&host->pinmux_lock); + host->xfer_step = XFER_INIT; + host->init_flag = 1; + host->is_gated = false; + + ret = meson_mmc_clk_init(host); + if (ret) + goto free_host; + + ret = mmc_of_parse(mmc); + if (ret) { + dev_warn(&pdev->dev, "error parsing DT: %d\n", ret); + goto free_host; + } + + if (amlsd_get_platform_data(pdata, mmc, 0)) + mmc_free_host(mmc); + + if (aml_card_type_mmc(pdata)) + /**set emmc tx_phase regs here base on dts**/ + aml_sd_emmc_tx_phase_set(host); + + dev_set_name(&mmc->class_dev, "%s", pdata->pinname); + + if (pdata->caps & MMC_PM_KEEP_POWER) + mmc->pm_caps |= MMC_PM_KEEP_POWER; + host->init_flag = 1; + host->version = AML_MMC_VERSION; + host->pinctrl = NULL; + host->status = HOST_INVALID; + host->is_tunning = 0; + 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->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 (mmc->caps & MMC_CAP_NONREMOVABLE) + pdata->is_in = 1; + + if (aml_card_type_sdio(pdata)) { /* if sdio_wifi */ + /* mmc->host_rescan_disable = true;*/ + /* do NOT run mmc_rescan for the first time */ + mmc->rescan_entered = 1; + } else { + /* mmc->host_rescan_disable = false;*/ + mmc->rescan_entered = 0; + } + + if (aml_card_type_mmc(pdata)) { + /* Poll down BOOT_15 in case hardward not pull down */ + u32 boot_poll_en, boot_poll_down; + + boot_poll_down = readl(host->pinmux_base + BOOT_POLL_UP_DOWN); + boot_poll_down &= (~(1 << 15)); + boot_poll_en = readl(host->pinmux_base + BOOT_POLL_UP_DOWN_EN); + boot_poll_en |= (1 << 15); + writel(boot_poll_down, host->pinmux_base + BOOT_POLL_UP_DOWN); + writel(boot_poll_en, host->pinmux_base + BOOT_POLL_UP_DOWN_EN); + } + + if (pdata->port_init) + pdata->port_init(pdata); + mmc->ops = &meson_mmc_ops; + aml_reg_print(host); + ret = mmc_add_host(mmc); + if (ret) { /* error */ + sd_emmc_err("Failed to add mmc host.\n"); + goto free_host; + } + if (aml_card_type_sdio(pdata)) /* if sdio_wifi */ + sdio_host = mmc; + + /*Register card detect irq : plug in & unplug*/ + if (aml_card_type_non_sdio(pdata)) { + pdata->irq_init(pdata); + mutex_init(&pdata->in_out_lock); + ret = devm_request_threaded_irq(&pdev->dev, pdata->irq_cd, + aml_sd_irq_cd, aml_irq_cd_thread, + IRQF_TRIGGER_RISING + | IRQF_TRIGGER_FALLING + | IRQF_ONESHOT, + "amlsd_cd", host); + if (ret) { + sd_emmc_err("Failed to request SD IN detect\n"); + goto free_host; + } + } + pr_info("%s() : success!\n", __func__); + return 0; + +free_host: + mmc_free_host(mmc); + kfree(pdata); + pr_err("%s() fail!\n", __func__); + return ret; +} + +static int meson_mmc_remove(struct platform_device *pdev) +{ + struct amlsd_host *host = dev_get_drvdata(&pdev->dev); + struct amlsd_platform *pdata = host->pdata; + + if (WARN_ON(!host)) + return 0; + + if (host->bn_buf) + dma_free_coherent(host->dev, SD_EMMC_BOUNCE_REQ_SIZE, + host->bn_buf, host->bn_dma_buf); + + devm_free_irq(&pdev->dev, host->irq, host); + iounmap(host->pinmux_base); + + if (host->cfg_div_clk) + clk_disable_unprepare(host->cfg_div_clk); + if (host->core_clk) + clk_disable_unprepare(host->core_clk); + + mmc_free_host(host->mmc); + kfree(pdata); + return 0; +} + +static const struct of_device_id meson_mmc_of_match[] = { + { + .compatible = "amlogic, meson-aml-mmc", + }, + {} +}; +MODULE_DEVICE_TABLE(of, meson_mmc_of_match); + +static struct platform_driver meson_mmc_driver = { + .probe = meson_mmc_probe, + .remove = meson_mmc_remove, + .driver = { + .name = "meson-aml-mmc", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(meson_mmc_of_match), + }, +}; + +static int __init meson_mmc_init(void) +{ + return platform_driver_register(&meson_mmc_driver); +} + +static void __exit meson_mmc_cleanup(void) +{ + platform_driver_unregister(&meson_mmc_driver); +} + +module_init(meson_mmc_init); +module_exit(meson_mmc_cleanup); + +MODULE_DESCRIPTION("Amlogic S912/GXM SD/eMMC driver"); +MODULE_AUTHOR("Kevin Hilman "); +MODULE_LICENSE("GPL"); + diff --git a/drivers/amlogic/mmc/amlsd.c b/drivers/amlogic/mmc/amlsd.c new file mode 100644 index 000000000000..efc48449843e --- /dev/null +++ b/drivers/amlogic/mmc/amlsd.c @@ -0,0 +1,542 @@ +/* + * drivers/amlogic/mmc/amlsd.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 + +const u8 tuning_blk_pattern_4bit[64] = { + 0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc, + 0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef, + 0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb, + 0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef, + 0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c, + 0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee, + 0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff, + 0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde, +}; +const u8 tuning_blk_pattern_8bit[128] = { + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc, + 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff, + 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff, + 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd, + 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, + 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff, + 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, + 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, + 0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, + 0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, + 0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, + 0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, + 0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, +}; + +void aml_mmc_ver_msg_show(void) +{ + static bool one_time_flag; + + if (!one_time_flag) { + pr_info("mmc driver version: %d.%02d, %s\n", + AML_MMC_MAJOR_VERSION, AML_MMC_MINOR_VERSION, + AML_MMC_VER_MESSAGE); + + one_time_flag = true; + } +} + + + +static int aml_is_card_insert(struct amlsd_platform *pdata) +{ + int ret = 0, in_count = 0, out_count = 0, i; + + if (pdata->gpio_cd) { + mdelay(pdata->card_in_delay); + for (i = 0; i < 200; i++) { + ret = gpio_get_value(pdata->gpio_cd); + if (ret) + out_count++; + in_count++; + if ((out_count > 100) || (in_count > 100)) + break; + } + if (out_count > 100) + ret = 1; + else if (in_count > 100) + ret = 0; + } + sdio_err("card %s\n", ret?"OUT":"IN"); + if (!pdata->gpio_cd_level) + ret = !ret; /* reverse, so ---- 0: no inserted 1: inserted */ + + return ret; +} +int aml_sd_uart_detect(struct amlsd_host *host) +{ + struct amlsd_platform *pdata = host->pdata; + + if (aml_is_card_insert(pdata)) { + if (pdata->is_in) + return 1; + 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; + } else { + if (!pdata->is_in) + return 1; + pdata->is_in = false; + pr_info("card out\n"); + + pdata->is_tuned = false; + if (host->mmc && host->mmc->card) + mmc_card_set_removed(host->mmc->card); + /* switch to 3.3V */ + aml_sd_voltage_switch(host->mmc, + MMC_SIGNAL_VOLTAGE_330); + + if (pdata->caps & MMC_CAP_4_BIT_DATA) + host->mmc->caps |= MMC_CAP_4_BIT_DATA; + } + return 0; +} + +static int card_dealed; +irqreturn_t aml_irq_cd_thread(int irq, void *data) +{ + struct amlsd_host *host = (struct amlsd_host *)data; + struct amlsd_platform *pdata = host->pdata; + int ret = 0; + + mutex_lock(&pdata->in_out_lock); + if (card_dealed == 1) { + card_dealed = 0; + mutex_unlock(&pdata->in_out_lock); + return IRQ_HANDLED; + } + ret = aml_sd_uart_detect(host); + if (ret == 1) {/* the same as the last*/ + mutex_unlock(&pdata->in_out_lock); + return IRQ_HANDLED; + } + card_dealed = 1; + if ((pdata->is_in == 0) && aml_card_type_non_sdio(pdata)) + host->init_flag = 0; + mutex_unlock(&pdata->in_out_lock); + + /* mdelay(500); */ + if (pdata->is_in) + mmc_detect_change(host->mmc, msecs_to_jiffies(100)); + else + mmc_detect_change(host->mmc, msecs_to_jiffies(0)); + + card_dealed = 0; + return IRQ_HANDLED; +} + +irqreturn_t aml_sd_irq_cd(int irq, void *dev_id) +{ + /* pr_info("cd dev_id %x\n", (unsigned)dev_id); */ + return IRQ_WAKE_THREAD; +} + +static int aml_cmd_invalid(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct amlsd_host *host = mmc_priv(mmc); + unsigned long flags; + + spin_lock_irqsave(&host->mrq_lock, flags); + mrq->cmd->error = -EINVAL; + spin_unlock_irqrestore(&host->mrq_lock, flags); + mmc_request_done(mmc, mrq); + + return -EINVAL; +} + +static int aml_rpmb_cmd_invalid(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct amlsd_host *host = mmc_priv(mmc); + 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); + mrq->data->bytes_xfered = mrq->data->blksz*mrq->data->blocks; + mmc_request_done(mmc, mrq); + return -EINVAL; +} + +int aml_check_unsupport_cmd(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct amlsd_host *host = mmc_priv(mmc); + struct amlsd_platform *pdata = host->pdata; + u32 opcode, arg; + + opcode = mrq->cmd->opcode; + arg = mrq->cmd->arg; + /* CMD3 means the first time initialized flow is running */ + if (opcode == 3) + mmc->first_init_flag = false; + + if (aml_card_type_mmc(pdata)) { + if (opcode == 6) { + if (arg == 0x3B30301) + pdata->rmpb_cmd_flag = 1; + else + pdata->rmpb_cmd_flag = 0; + } + if (pdata->rmpb_cmd_flag && (!pdata->rpmb_valid_command)) { + if ((opcode == 18) + || (opcode == 25)) + return aml_rpmb_cmd_invalid(mmc, mrq); + } + if (pdata->rmpb_cmd_flag && (opcode == 23)) + pdata->rpmb_valid_command = 1; + else + pdata->rpmb_valid_command = 0; + } + + if (mmc->caps & MMC_CAP_NONREMOVABLE) { /* nonremovable device */ + if (mmc->first_init_flag) { /* init for the first time */ + /* for 8189ETV needs ssdio reset when starts */ + if (aml_card_type_sdio(pdata)) { + /* if (opcode == SD_IO_RW_DIRECT + * || opcode == SD_IO_RW_EXTENDED + * || opcode == SD_SEND_IF_COND) + * return aml_cmd_invalid(mmc, mrq); + */ + return 0; + } else if (aml_card_type_mmc(pdata)) { + if (opcode == SD_IO_SEND_OP_COND + || opcode == SD_IO_RW_DIRECT + || opcode == SD_IO_RW_EXTENDED + || opcode == SD_SEND_IF_COND + || opcode == MMC_APP_CMD) + return aml_cmd_invalid(mmc, mrq); + } else if (aml_card_type_sd(pdata) + || aml_card_type_non_sdio(pdata)) { + if (opcode == SD_IO_SEND_OP_COND + || opcode == SD_IO_RW_DIRECT + || opcode == SD_IO_RW_EXTENDED) + return aml_cmd_invalid(mmc, mrq); + } + } + } else { /* removable device */ + /* filter cmd 5/52/53 for a non-sdio device */ + if (!aml_card_type_sdio(pdata) + && !aml_card_type_unknown(pdata)) { + if (opcode == SD_IO_SEND_OP_COND + || opcode == SD_IO_RW_DIRECT + || opcode == SD_IO_RW_EXTENDED) + return aml_cmd_invalid(mmc, mrq); + } + } + return 0; +} + +int aml_sd_voltage_switch(struct mmc_host *mmc, char signal_voltage) +{ + struct amlsd_host *host = mmc_priv(mmc); + struct amlsd_platform *pdata = host->pdata; + int ret = 0; + + /* voltage is the same, return directly */ + if (!aml_card_type_non_sdio(pdata) + || (pdata->signal_voltage == signal_voltage)) { + if (aml_card_type_sdio(pdata)) + host->sd_sdio_switch_volat_done = 1; + return 0; + } + if (pdata->vol_switch) { + if (pdata->signal_voltage == 0xff) { + gpio_free(pdata->vol_switch); + ret = gpio_request_one(pdata->vol_switch, + GPIOF_OUT_INIT_HIGH, MODULE_NAME); + if (ret) { + pr_err("%s [%d] request error\n", + __func__, __LINE__); + return -EINVAL; + } + } + if (signal_voltage == MMC_SIGNAL_VOLTAGE_180) + ret = gpio_direction_output(pdata->vol_switch, + pdata->vol_switch_18); + else + ret = gpio_direction_output(pdata->vol_switch, + (!pdata->vol_switch_18)); + CHECK_RET(ret); + if (!ret) + pdata->signal_voltage = signal_voltage; + } else + return -EINVAL; + + host->sd_sdio_switch_volat_done = 1; + return 0; +} + +/* boot9 here */ +void aml_emmc_hw_reset(struct mmc_host *mmc) +{ + struct amlsd_host *host = mmc_priv(mmc); + struct amlsd_platform *pdata = host->pdata; + u32 ret; + + if (!aml_card_type_mmc(pdata) || !pdata->hw_reset) + return; + + /* boot_9 used as eMMC hw_rst pin here. */ + gpio_free(pdata->hw_reset); + ret = gpio_request_one(pdata->hw_reset, + GPIOF_OUT_INIT_HIGH, MODULE_NAME); + CHECK_RET(ret); + if (ret) { + pr_err("%s [%d] request error\n", + __func__, __LINE__); + return; + } + ret = gpio_direction_output(pdata->hw_reset, 0); + CHECK_RET(ret); + if (ret) { + pr_err("%s [%d] output high error\n", + __func__, __LINE__); + return; + } + mdelay(2); + ret = gpio_direction_output(pdata->hw_reset, 1); + CHECK_RET(ret); + if (ret) { + pr_err("%s [%d] output high error\n", + __func__, __LINE__); + return; + } + mdelay(2); +} + +static void sdio_rescan(struct mmc_host *mmc) +{ + int ret; + + mmc->rescan_entered = 0; +/* mmc->host_rescan_disable = false;*/ + mmc_detect_change(mmc, 0); + /* start the delayed_work */ + ret = flush_work(&(mmc->detect.work)); + /* wait for the delayed_work to finish */ + if (!ret) + pr_info("Error: delayed_work mmc_rescan() already idle!\n"); +} + +void sdio_reinit(void) +{ + if (sdio_host) { + if (sdio_host->card) + sdio_reset_comm(sdio_host->card); + else + sdio_rescan(sdio_host); + } else { + pr_info("Error: sdio_host is NULL\n"); + } + + pr_info("[%s] finish\n", __func__); +} +EXPORT_SYMBOL(sdio_reinit); + +void of_amlsd_irq_init(struct amlsd_platform *pdata) +{ + if (aml_card_type_non_sdio(pdata)) + pdata->irq_cd = gpio_to_irq(pdata->gpio_cd); + pr_info("sd irq num = %d\n", pdata->irq_cd); +} + +int of_amlsd_init(struct amlsd_platform *pdata) +{ + int ret; + + WARN_ON(!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); + CHECK_RET(ret); + } + + /* if(pdata->port == MESON_SDIO_PORT_A) */ + /* wifi_setup_dt(); */ + return 0; +} + +void aml_devm_pinctrl_put(struct amlsd_host *host) +{ + if (host->pinctrl) { + devm_pinctrl_put(host->pinctrl); + host->pinctrl = NULL; + + host->pinctrl_name[0] = '\0'; + /* sdio_err("Put Pinctrl\n"); */ + } +} + +static struct pinctrl * __must_check aml_devm_pinctrl_get_select( + struct amlsd_host *host, const char *name) +{ + struct pinctrl *p = host->pinctrl; + struct pinctrl_state *s; + int ret; + + if (!p) { + p = devm_pinctrl_get(&host->pdev->dev); + + if (IS_ERR(p)) + return p; + + host->pinctrl = p; + /* sdio_err("switch %s\n", name); */ + } + + s = pinctrl_lookup_state(p, name); + if (IS_ERR(s)) { + sdio_err("lookup %s fail\n", name); + devm_pinctrl_put(p); + return ERR_CAST(s); + } + + ret = pinctrl_select_state(p, s); + if (ret < 0) { + sdio_err("select %s fail\n", name); + devm_pinctrl_put(p); + return ERR_PTR(ret); + } + return p; +} + +void of_amlsd_xfer_pre(struct mmc_host *mmc) +{ + struct amlsd_host *host = mmc_priv(mmc); + struct amlsd_platform *pdata = host->pdata; + char pinctrl[30]; + char *p = pinctrl; + int i, size = 0; + struct pinctrl *ppin; + + size = sizeof(pinctrl); + if (mmc->ios.chip_select == MMC_CS_DONTCARE) { + if ((mmc->caps & MMC_CAP_4_BIT_DATA) + || (strcmp(pdata->pinname, "sd")) + || (mmc->caps & MMC_CAP_8_BIT_DATA)) + aml_snprint(&p, &size, "%s_all_pins", pdata->pinname); + } else { /* MMC_CS_HIGH */ + if (pdata->is_sduart && (!strcmp(pdata->pinname, "sd"))) { + aml_snprint(&p, &size, + "%s_clk_cmd_uart_pins", pdata->pinname); + } else { + aml_snprint(&p, &size, + "%s_clk_cmd_pins", pdata->pinname); + } + } + + /* if pinmux setting is changed (pinctrl_name is different) */ + if (strncmp(host->pinctrl_name, pinctrl, + sizeof(host->pinctrl_name))) { + if (strlcpy(host->pinctrl_name, pinctrl, + sizeof(host->pinctrl_name)) + >= sizeof(host->pinctrl_name)) { + + sdio_err("Pinctrl name is too long!\n"); + return; + } + + for (i = 0; i < 100; i++) { + mutex_lock(&host->pinmux_lock); + ppin = aml_devm_pinctrl_get_select(host, pinctrl); + mutex_unlock(&host->pinmux_lock); + if (!IS_ERR(ppin)) { + /* pdata->host->pinctrl = ppin; */ + break; + } + /* else -> aml_irq_cdin_thread() + *should be using one of the GPIO of card, + * then we should wait here until the GPIO is free, + * otherwise something must be wrong. + */ + mdelay(1); + } + if (i == 100) + sdhc_err("CMD%d: get pinctrl %s fail.\n", + host->opcode, pinctrl); + } +} + +void of_amlsd_xfer_post(struct mmc_host *mmc) +{ +} + +int of_amlsd_ro(struct amlsd_platform *pdata) +{ + int ret = 0; /* 0--read&write, 1--read only */ + + if (pdata->gpio_ro) + ret = gpio_get_value(pdata->gpio_ro); + /* sdio_err("read-only?--%s\n", ret?"YES":"NO"); */ + return ret; +} + +void aml_snprint (char **pp, int *left_size, const char *fmt, ...) +{ + va_list args; + char *p = *pp; + int size; + + if (*left_size <= 1) { + sdhc_err("buf is full\n"); + return; + } + + va_start(args, fmt); + size = vsnprintf(p, *left_size, fmt, args); + va_end(args); + *pp += size; + *left_size -= size; +} + + diff --git a/drivers/amlogic/mmc/amlsd_of.c b/drivers/amlogic/mmc/amlsd_of.c new file mode 100644 index 000000000000..7c8f77c7e871 --- /dev/null +++ b/drivers/amlogic/mmc/amlsd_of.c @@ -0,0 +1,210 @@ +/* + * drivers/amlogic/mmc/amlsd_of.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 +unsigned int sd_emmc_debug; + +static const struct sd_caps host_caps[] = { + SD_CAPS(MMC_CAP_4_BIT_DATA, "MMC_CAP_4_BIT_DATA"), + SD_CAPS(MMC_CAP_MMC_HIGHSPEED, "MMC_CAP_MMC_HIGHSPEED"), + SD_CAPS(MMC_CAP_SD_HIGHSPEED, "MMC_CAP_SD_HIGHSPEED"), + SD_CAPS(MMC_CAP_SDIO_IRQ, "MMC_CAP_SDIO_IRQ"), + SD_CAPS(MMC_CAP_SPI, "MMC_CAP_SPI"), + SD_CAPS(MMC_CAP_NEEDS_POLL, "MMC_CAP_NEEDS_POLL"), + SD_CAPS(MMC_CAP_8_BIT_DATA, "MMC_CAP_8_BIT_DATA"), + SD_CAPS(MMC_CAP_NONREMOVABLE, "MMC_CAP_NONREMOVABLE"), + SD_CAPS(MMC_CAP_WAIT_WHILE_BUSY, "MMC_CAP_WAIT_WHILE_BUSY"), + SD_CAPS(MMC_CAP_ERASE, "MMC_CAP_ERASE"), + SD_CAPS(MMC_CAP_1_8V_DDR, "MMC_CAP_1_8V_DDR"), + SD_CAPS(MMC_CAP_1_2V_DDR, "MMC_CAP_1_2V_DDR"), + SD_CAPS(MMC_CAP_POWER_OFF_CARD, "MMC_CAP_POWER_OFF_CARD"), + SD_CAPS(MMC_CAP_BUS_WIDTH_TEST, "MMC_CAP_BUS_WIDTH_TEST"), + SD_CAPS(MMC_CAP_UHS_SDR12, "MMC_CAP_UHS_SDR12"), + SD_CAPS(MMC_CAP_UHS_SDR25, "MMC_CAP_UHS_SDR25"), + SD_CAPS(MMC_CAP_UHS_SDR50, "MMC_CAP_UHS_SDR50"), + SD_CAPS(MMC_CAP_UHS_SDR104, "MMC_CAP_UHS_SDR104"), + SD_CAPS(MMC_CAP_UHS_DDR50, "MMC_CAP_UHS_DDR50"), + SD_CAPS(MMC_CAP_DRIVER_TYPE_A, "MMC_CAP_DRIVER_TYPE_A"), + SD_CAPS(MMC_CAP_DRIVER_TYPE_C, "MMC_CAP_DRIVER_TYPE_C"), + SD_CAPS(MMC_CAP_DRIVER_TYPE_D, "MMC_CAP_DRIVER_TYPE_D"), + SD_CAPS(MMC_CAP_CMD23, "MMC_CAP_CMD23"), + SD_CAPS(MMC_CAP_HW_RESET, "MMC_CAP_HW_RESET"), + SD_CAPS(MMC_CAP_AGGRESSIVE_PM, "MMC_CAP_AGGRESSIVE_PM"), + SD_CAPS(MMC_PM_KEEP_POWER, "MMC_PM_KEEP_POWER"), +}; + +static int amlsd_get_host_caps(struct device_node *of_node, + struct amlsd_platform *pdata) +{ + const char *str_caps; + struct property *prop; + u32 i, caps = 0; + + of_property_for_each_string(of_node, "caps", prop, str_caps) { + for (i = 0; i < ARRAY_SIZE(host_caps); i++) { + if (!strcasecmp(host_caps[i].name, str_caps)) + caps |= host_caps[i].caps; + } + }; + if (caps & MMC_CAP_8_BIT_DATA) + caps |= MMC_CAP_4_BIT_DATA; + + pdata->caps = caps; + pr_info("%s:pdata->caps = %x\n", pdata->pinname, pdata->caps); + return 0; +} + +static const struct sd_caps host_caps2[] = { + SD_CAPS(MMC_CAP2_BOOTPART_NOACC, "MMC_CAP2_BOOTPART_NOACC"), + /*SD_CAPS(MMC_CAP2_CACHE_CTRL, "MMC_CAP2_CACHE_CTRL"),*/ + /* SD_CAPS(MMC_CAP2_POWEROFF_NOTIFY, "MMC_CAP2_POWEROFF_NOTIFY"), */ + SD_CAPS(MMC_CAP2_NO_MULTI_READ, "MMC_CAP2_NO_MULTI_READ"), + /*SD_CAPS(MMC_CAP2_NO_SLEEP_CMD, "MMC_CAP2_NO_SLEEP_CMD"),*/ + SD_CAPS(MMC_CAP2_HS200_1_8V_SDR, "MMC_CAP2_HS200_1_8V_SDR"), + SD_CAPS(MMC_CAP2_HS200_1_2V_SDR, "MMC_CAP2_HS200_1_2V_SDR"), + SD_CAPS(MMC_CAP2_HS200, "MMC_CAP2_HS200"), + SD_CAPS(MMC_CAP2_HS400_1_8V, "MMC_CAP2_HS400_1_8V"), + SD_CAPS(MMC_CAP2_HS400_1_2V, "MMC_CAP2_HS400_1_2V"), + SD_CAPS(MMC_CAP2_HS400, "MMC_CAP2_HS400"), + /*SD_CAPS(MMC_CAP2_BROKEN_VOLTAGE, "MMC_CAP2_BROKEN_VOLTAGE"),*/ + /* SD_CAPS(MMC_CAP2_DETECT_ON_ERR, "MMC_CAP2_DETECT_ON_ERR"), */ + SD_CAPS(MMC_CAP2_HC_ERASE_SZ, "MMC_CAP2_HC_ERASE_SZ"), + SD_CAPS(MMC_CAP2_CD_ACTIVE_HIGH, "MMC_CAP2_CD_ACTIVE_HIGH"), + SD_CAPS(MMC_CAP2_RO_ACTIVE_HIGH, "MMC_CAP2_RO_ACTIVE_HIGH"), +}; + +static int amlsd_get_host_caps2(struct device_node *of_node, + struct amlsd_platform *pdata) +{ + const char *str_caps; + struct property *prop; + u32 i, caps = 0; + + of_property_for_each_string(of_node, "caps2", prop, str_caps) { + for (i = 0; i < ARRAY_SIZE(host_caps2); i++) { + if (!strcasecmp(host_caps2[i].name, str_caps)) + caps |= host_caps2[i].caps; + } + }; + pdata->caps2 = caps; + pr_info("%s:pdata->caps2 = %x\n", pdata->pinname, pdata->caps2); + return 0; +} + +int amlsd_get_platform_data(struct amlsd_platform *pdata, + struct mmc_host *mmc, u32 index) +{ + struct device_node *of_node; + struct device_node *child; + u32 i, prop; + const char *str = "none"; + + if (!mmc->parent || !mmc->parent->of_node) + return 0; + + of_node = mmc->parent->of_node; + if (of_node) { + child = of_node->child; + WARN_ON(!child); + WARN_ON(index >= MMC_MAX_DEVICE); + for (i = 0; i < index; i++) + child = child->sibling; + if (!child) + return -EINVAL; + + /* amlsd_init_pins_input(child, pdata);*/ + + SD_PARSE_U32_PROP_HEX(child, "port", + prop, pdata->port); + SD_PARSE_U32_PROP_HEX(child, "ocr_avail", + prop, pdata->ocr_avail); + WARN_ON(!pdata->ocr_avail); + SD_PARSE_U32_PROP_DEC(child, "f_min", + 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_GPIO_NUM_PROP(child, "gpio_cd", + str, pdata->gpio_cd); + SD_PARSE_GPIO_NUM_PROP(child, "gpio_ro", + str, pdata->gpio_ro); + SD_PARSE_GPIO_NUM_PROP(child, "vol_switch", + str, pdata->vol_switch); + + SD_PARSE_U32_PROP_HEX(child, "power_level", + prop, pdata->power_level); + + SD_PARSE_U32_PROP_DEC(child, "gpio_cd_level", + prop, pdata->gpio_cd_level); + SD_PARSE_STRING_PROP(child, "pinname", + str, pdata->pinname); + SD_PARSE_U32_PROP_DEC(child, "auto_clk_close", + prop, pdata->auto_clk_close); + SD_PARSE_U32_PROP_DEC(child, "vol_switch_18", + prop, pdata->vol_switch_18); + SD_PARSE_U32_PROP_DEC(child, "vol_switch_delay", + 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); + } + SD_PARSE_GPIO_NUM_PROP(child, "hw_reset", + str, pdata->hw_reset); + SD_PARSE_GPIO_NUM_PROP(child, "gpio_dat3", + str, pdata->gpio_dat3); + + pdata->xfer_pre = of_amlsd_xfer_pre; + pdata->xfer_post = of_amlsd_xfer_post; + + amlsd_get_host_caps(child, pdata); + amlsd_get_host_caps2(child, pdata); + pdata->port_init = of_amlsd_init; + pdata->irq_init = of_amlsd_irq_init; + pdata->ro = of_amlsd_ro; + } + return 0; +} + + diff --git a/drivers/amlogic/mmc/emmc_partitions.c b/drivers/amlogic/mmc/emmc_partitions.c new file mode 100644 index 000000000000..2eeda6d1d189 --- /dev/null +++ b/drivers/amlogic/mmc/emmc_partitions.c @@ -0,0 +1,1011 @@ +/* + * drivers/amlogic/mmc/emmc_partitions.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 + +#define DTB_NAME "dtb" +#define SZ_1M 0x00100000 +#define MMC_DTB_PART_OFFSET (40*SZ_1M) +#define EMMC_BLOCK_SIZE (0x100) +#define MAX_EMMC_BLOCK_SIZE (128*1024) + +static dev_t amlmmc_dtb_no; +struct cdev amlmmc_dtb; +struct device *dtb_dev; +struct class *amlmmc_dtb_class; +struct mmc_card *card_dtb; +struct mmc_partitions_fmt *pt_fmt; + +int amlmmc_dtb_write(struct mmc_card *card, + unsigned char *buf, int len) +{ + int ret = 0, start_blk, size, blk_cnt; + int bit = card->csd.read_blkbits; + unsigned char *src = NULL; + + if (len > CONFIG_DTB_SIZE) { + pr_err("%s dtb data len too much", __func__); + return -EFAULT; + } + start_blk = MMC_DTB_PART_OFFSET; + if (start_blk < 0) { + ret = -EINVAL; + return ret; + } + start_blk >>= bit; + size = CONFIG_DTB_SIZE; + blk_cnt = size>>bit; + src = (unsigned char *)buf; + do { + ret = mmc_write_internal(card, start_blk, EMMC_BLOCK_SIZE, src); + if (ret) { + pr_err("%s: save dtb error", __func__); + ret = -EFAULT; + return ret; + } + start_blk += EMMC_BLOCK_SIZE; + blk_cnt -= EMMC_BLOCK_SIZE; + src = (unsigned char *)buf + MAX_EMMC_BLOCK_SIZE; + } while (blk_cnt != 0); + + return ret; +} + +int amlmmc_dtb_read(struct mmc_card *card, + unsigned char *buf, int len) +{ + int ret = 0, start_blk, size, blk_cnt; + int bit = card->csd.read_blkbits; + unsigned char *dst = NULL; + + if (len > CONFIG_DTB_SIZE) { + pr_err("%s dtb data len too much", __func__); + return -EFAULT; + } + memset(buf, 0x0, len); + + start_blk = MMC_DTB_PART_OFFSET; + if (start_blk < 0) { + ret = -EINVAL; + return ret; + } + + start_blk >>= bit; + size = CONFIG_DTB_SIZE; + blk_cnt = size>>bit; + dst = (unsigned char *)buf; + do { + ret = mmc_read_internal(card, start_blk, EMMC_BLOCK_SIZE, dst); + if (ret) { + pr_err("%s read dtb error", __func__); + ret = -EFAULT; + return ret; + } + start_blk += EMMC_BLOCK_SIZE; + blk_cnt -= EMMC_BLOCK_SIZE; + dst = (unsigned char *)buf + MAX_EMMC_BLOCK_SIZE; + } while (blk_cnt != 0); + return ret; +} +static CLASS_ATTR(emmcdtb, 0644, NULL, NULL); + +int mmc_dtb_open(struct inode *node, struct file *file) +{ + return 0; +} + +ssize_t mmc_dtb_read(struct file *file, + char __user *buf, + size_t count, + loff_t *ppos) +{ + unsigned char *dtb_ptr = NULL; + ssize_t read_size = 0; + int ret = 0; + + if (*ppos == CONFIG_DTB_SIZE) + return 0; + + if (*ppos >= CONFIG_DTB_SIZE) { + pr_err("%s: out of space!", __func__); + return -EFAULT; + } + + dtb_ptr = vmalloc(CONFIG_DTB_SIZE); + if (dtb_ptr == NULL) { + /* pr_err("%s: malloc buf failed", __func__);*/ + return -ENOMEM; + } + + mmc_claim_host(card_dtb->host); + ret = amlmmc_dtb_read(card_dtb, + (unsigned char *)dtb_ptr, + CONFIG_DTB_SIZE); + if (ret) { + pr_err("%s: read failed:%d", __func__, ret); + ret = -EFAULT; + goto exit; + } + if ((*ppos + count) > CONFIG_DTB_SIZE) + read_size = CONFIG_DTB_SIZE - *ppos; + else + read_size = count; + ret = copy_to_user(buf, (dtb_ptr + *ppos), read_size); + *ppos += read_size; +exit: + mmc_release_host(card_dtb->host); + vfree(dtb_ptr); + return read_size; +} + +ssize_t mmc_dtb_write(struct file *file, + const char __user *buf, + size_t count, loff_t *ppos) +{ + unsigned char *dtb_ptr = NULL; + ssize_t write_size = 0; + int ret = 0; + + if (*ppos == CONFIG_DTB_SIZE) + return 0; + + if (*ppos >= CONFIG_DTB_SIZE) { + pr_err("%s: out of space!", __func__); + return -EFAULT; + } + dtb_ptr = vmalloc(CONFIG_DTB_SIZE); + if (dtb_ptr == NULL) { + /* pr_err("%s: malloc buf failed", __func__);*/ + return -ENOMEM; + } + mmc_claim_host(card_dtb->host); + + if ((*ppos + count) > CONFIG_DTB_SIZE) + write_size = CONFIG_DTB_SIZE - *ppos; + else + write_size = count; + + ret = copy_from_user((dtb_ptr + *ppos), buf, write_size); + + ret = amlmmc_dtb_write(card_dtb, + dtb_ptr, CONFIG_DTB_SIZE); + if (ret) { + pr_err("%s: write dtb failed", __func__); + ret = -EFAULT; + goto exit; + } + + *ppos += write_size; +exit: + mmc_release_host(card_dtb->host); + /* kfree(dtb_ptr); */ + vfree(dtb_ptr); + return write_size; +} + +long mmc_dtb_ioctl(struct file *file, unsigned int cmd, unsigned long args) +{ + return 0; +} + +static const struct file_operations dtb_ops = { + .open = mmc_dtb_open, + .read = mmc_dtb_read, + .write = mmc_dtb_write, + .unlocked_ioctl = mmc_dtb_ioctl, +}; + +int amlmmc_dtb_init(struct mmc_card *card) +{ + int ret = 0; + + card_dtb = card; + pr_info("%s: register dtb chardev", __func__); + ret = alloc_chrdev_region(&amlmmc_dtb_no, 0, 1, DTB_NAME); + if (ret < 0) { + pr_err("alloc dtb dev_t no failed"); + ret = -1; + goto exit_err; + } + + cdev_init(&amlmmc_dtb, &dtb_ops); + amlmmc_dtb.owner = THIS_MODULE; + ret = cdev_add(&amlmmc_dtb, amlmmc_dtb_no, 1); + if (ret) { + pr_err("dtb dev add failed"); + ret = -1; + goto exit_err1; + } + + amlmmc_dtb_class = class_create(THIS_MODULE, DTB_NAME); + if (IS_ERR(amlmmc_dtb_class)) { + pr_err("dtb dev add failed"); + ret = -1; + goto exit_err2; + } + + ret = class_create_file(amlmmc_dtb_class, &class_attr_emmcdtb); + if (ret) { + pr_err("dtb dev add failed"); + ret = -1; + goto exit_err2; + } + + dtb_dev = device_create(amlmmc_dtb_class, + NULL, + amlmmc_dtb_no, + NULL, + DTB_NAME); + if (IS_ERR(dtb_dev)) { + pr_err("dtb dev add failed"); + ret = -1; + goto exit_err3; + } + + pr_info("%s: register dtb chardev OK", __func__); + + return ret; + +exit_err3: + class_remove_file(amlmmc_dtb_class, &class_attr_emmcdtb); + class_destroy(amlmmc_dtb_class); +exit_err2: + cdev_del(&amlmmc_dtb); +exit_err1: + unregister_chrdev_region(amlmmc_dtb_no, 1); +exit_err: + return ret; +} + +/* + * Checks that a normal transfer didn't have any errors + */ +static int mmc_check_result(struct mmc_request *mrq) +{ + int ret; + + WARN_ON(!mrq || !mrq->cmd || !mrq->data); + + ret = 0; + + if (!ret && mrq->cmd->error) + ret = mrq->cmd->error; + if (!ret && mrq->data->error) + ret = mrq->data->error; + if (!ret && mrq->stop && mrq->stop->error) + ret = mrq->stop->error; + if (!ret && mrq->data->bytes_xfered != + mrq->data->blocks * mrq->data->blksz) + ret = RESULT_FAIL; + + if (ret == -EINVAL) + ret = RESULT_UNSUP_HOST; + + return ret; +} + +static void mmc_prepare_mrq(struct mmc_card *card, + struct mmc_request *mrq, struct scatterlist *sg, + unsigned int sg_len, unsigned int dev_addr, unsigned int blocks, + unsigned int blksz, int write) +{ + WARN_ON(!mrq || !mrq->cmd || !mrq->data || !mrq->stop); + + if (blocks > 1) { + mrq->cmd->opcode = write ? + MMC_WRITE_MULTIPLE_BLOCK : MMC_READ_MULTIPLE_BLOCK; + } else { + mrq->cmd->opcode = write ? + MMC_WRITE_BLOCK : MMC_READ_SINGLE_BLOCK; + } + + mrq->cmd->arg = dev_addr; + if (!mmc_card_blockaddr(card)) + mrq->cmd->arg <<= 9; + + mrq->cmd->flags = MMC_RSP_R1 | MMC_CMD_ADTC; + + if (blocks == 1) + mrq->stop = NULL; + else { + mrq->stop->opcode = MMC_STOP_TRANSMISSION; + mrq->stop->arg = 0; + mrq->stop->flags = MMC_RSP_R1B | MMC_CMD_AC; + } + + mrq->data->blksz = blksz; + mrq->data->blocks = blocks; + mrq->data->flags = write ? MMC_DATA_WRITE : MMC_DATA_READ; + mrq->data->sg = sg; + mrq->data->sg_len = sg_len; + + mmc_set_data_timeout(mrq->data, card); +} + +unsigned int mmc_capacity(struct mmc_card *card) +{ + if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) + return card->ext_csd.sectors; + else + return card->csd.capacity << (card->csd.read_blkbits - 9); +} + +static int mmc_transfer(struct mmc_card *card, unsigned int dev_addr, + unsigned int blocks, void *buf, int write) +{ + unsigned int size; + struct scatterlist sg; + struct mmc_request mrq = {0}; + struct mmc_command cmd = {0}; + struct mmc_command stop = {0}; + struct mmc_data data = {0}; + int ret; + + if ((dev_addr + blocks) >= mmc_capacity(card)) { + pr_info("[%s] %s range exceeds device capacity!\n", + __func__, write?"write":"read"); + ret = -1; + return ret; + } + + size = blocks << card->csd.read_blkbits; + sg_init_one(&sg, buf, size); + + mrq.cmd = &cmd; + mrq.data = &data; + mrq.stop = &stop; + + mmc_prepare_mrq(card, &mrq, &sg, 1, dev_addr, + blocks, 1<csd.read_blkbits, write); + + mmc_wait_for_req(card->host, &mrq); + + ret = mmc_check_result(&mrq); + return ret; +} + +int mmc_read_internal(struct mmc_card *card, unsigned int dev_addr, + unsigned int blocks, void *buf) +{ + return mmc_transfer(card, dev_addr, blocks, buf, 0); +} + +int mmc_write_internal(struct mmc_card *card, unsigned int dev_addr, + unsigned int blocks, void *buf) +{ + return mmc_transfer(card, dev_addr, blocks, buf, 1); +} + + +static int mmc_partition_tbl_checksum_calc( + struct partitions *part, int part_num) +{ + int i, j; + u32 checksum = 0, *p; + + for (i = 0; i < part_num; i++) { + p = (u32 *)part; + + for (j = sizeof(struct partitions)/sizeof(checksum); + j > 0; j--) { + checksum += *p; + p++; + } + } + + return checksum; +} + +int get_reserve_partition_off(struct mmc_card *card) /* byte unit */ +{ + int off = -1, storage_flag; + struct mmc_host *mmc_host = card->host; + struct amlsd_host *host = mmc_priv(mmc_host); + + storage_flag = host->storage_flag; + if (!strcmp(mmc_hostname(mmc_host), "emmc")) + storage_flag = EMMC_BOOT_FLAG; + if ((storage_flag == EMMC_BOOT_FLAG) + || (storage_flag == SPI_EMMC_FLAG)) { + off = MMC_BOOT_PARTITION_SIZE + MMC_BOOT_PARTITION_RESERVED; + } else if ((storage_flag == 0) || (storage_flag == -1)) { + if (POR_EMMC_BOOT()) { + off = MMC_BOOT_PARTITION_SIZE + + MMC_BOOT_PARTITION_RESERVED; + } else if (POR_SPI_BOOT() || POR_CARD_BOOT()) { + off = 0; + } else { /* POR_NAND_BOOT */ + off = -1; + } + } else { /* error, the storage device does NOT relate to eMMC */ + off = -1; + } + + if (off == -1) + pr_info( + "[%s] Error, NOT relate to eMMC,\"\" storage_flag=%d\n", + __func__, storage_flag); + + return off; +} + +int get_reserve_partition_off_from_tbl(void) +{ + int i; + + for (i = 0; i < pt_fmt->part_num; i++) { + if (!strcmp(pt_fmt->partitions[i].name, MMC_RESERVED_NAME)) + return pt_fmt->partitions[i].offset; + } + return -1; +} + +/* static void show_mmc_patition (struct partitions *part, int part_num) + * { + * int i, cnt_stuff; + + * pr_info(" name offset size\n"); + * pr_info("===========================\n"); + * for (i=0; i < part_num ; i++) { + * pr_info("%4d: %s", i, part[i].name); + * cnt_stuff = sizeof(part[i].name) - strlen(part[i].name); + * // something is wrong + * if (cnt_stuff < 0) + * cnt_stuff = 0; + * cnt_stuff += 2; + * while (cnt_stuff--) { + * pr_info(" "); + * } + * pr_info("%18llx%18llx\n", part[i].offset, part[i].size); + * } + * } + */ + +static int mmc_read_partition_tbl(struct mmc_card *card, + struct mmc_partitions_fmt *pt_fmt) +{ + int ret = 0, start_blk, size, blk_cnt; + int bit = card->csd.read_blkbits; + int blk_size = 1 << bit; /* size of a block */ + char *buf, *dst; + + buf = kmalloc(blk_size, GFP_KERNEL); + if (buf == NULL) { + /* pr_info("malloc failed for buffer!\n");*/ + ret = -ENOMEM; + goto exit_err; + } + memset(pt_fmt, 0, sizeof(struct mmc_partitions_fmt)); + memset(buf, 0, blk_size); + + start_blk = get_reserve_partition_off(card); + if (start_blk < 0) { + ret = -EINVAL; + goto exit_err; + } + start_blk >>= bit; + size = sizeof(struct mmc_partitions_fmt); + dst = (char *)pt_fmt; + if (size >= blk_size) { + blk_cnt = size >> bit; + ret = mmc_read_internal(card, start_blk, blk_cnt, dst); + if (ret) { /* error */ + goto exit_err; + } + start_blk += blk_cnt; + dst += blk_cnt << bit; + size -= blk_cnt << bit; + } + if (size > 0) { /* the last block */ + ret = mmc_read_internal(card, start_blk, 1, buf); + if (ret) + goto exit_err; + memcpy(dst, buf, size); + } + /* pr_info("Partition table stored in eMMC/TSD:\n"); */ + /* pr_info("magic: %s, version: %s, checksum=%#x\n", */ + /* pt_fmt->magic, pt_fmt->version, pt_fmt->checksum); */ + /* show_mmc_patition(pt_fmt->partitions, pt_fmt->part_num); */ + + if ((strncmp(pt_fmt->magic, + MMC_PARTITIONS_MAGIC, + sizeof(pt_fmt->magic)) == 0) /* the same */ + && (pt_fmt->part_num > 0) + && (pt_fmt->part_num <= MAX_MMC_PART_NUM) + && (pt_fmt->checksum == + mmc_partition_tbl_checksum_calc( + pt_fmt->partitions, + pt_fmt->part_num))) { + + ret = 0; /* everything is OK now */ + + } else { + if (strncmp(pt_fmt->magic, MMC_PARTITIONS_MAGIC, + sizeof(pt_fmt->magic)) != 0) { + + print_tmp("magic error: %s\n", + (pt_fmt->magic)?pt_fmt->magic:"NULL"); + + } else if ((pt_fmt->part_num < 0) + || (pt_fmt->part_num > MAX_MMC_PART_NUM)) { + + print_tmp("partition number error: %d\n", + pt_fmt->part_num); + + } else { + print_tmp( + "checksum error: pt_fmt->checksum=%d,calc_result=%d\n", + pt_fmt->checksum, + mmc_partition_tbl_checksum_calc( + pt_fmt->partitions, + pt_fmt->part_num)); + } + + pr_info("[%s]: partition verified error\n", __func__); + ret = -1; /* the partition information is invalid */ + } + +exit_err: + kfree(buf); + + pr_info("[%s] mmc read partition %s!\n", + __func__, (ret == 0) ? "OK" : "ERROR"); + + return ret; +} + +/* This function is copy and modified from kernel function add_partition() */ +static struct hd_struct *add_emmc_each_part(struct gendisk *disk, int partno, + sector_t start, sector_t len, int flags, + char *pname) +{ + struct hd_struct *p; + dev_t devt = MKDEV(0, 0); + struct device *ddev = disk_to_dev(disk); + struct device *pdev; + struct disk_part_tbl *ptbl; + const char *dname; + int err; + + err = disk_expand_part_tbl(disk, partno); + if (err) + return ERR_PTR(err); + ptbl = disk->part_tbl; + + if (ptbl->part[partno]) + return ERR_PTR(-EBUSY); + + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) + return ERR_PTR(-EBUSY); + + if (!init_part_stats(p)) { + err = -ENOMEM; + goto out_free; + } + seqcount_init(&p->nr_sects_seq); + pdev = part_to_dev(p); + + p->start_sect = start; + p->alignment_offset = + queue_limit_alignment_offset(&disk->queue->limits, start); + p->discard_alignment = + queue_limit_discard_alignment(&disk->queue->limits, start); + p->nr_sects = len; + p->partno = partno; + p->policy = get_disk_ro(disk); + + dname = dev_name(ddev); + dev_set_name(pdev, "%s", pname); + + device_initialize(pdev); + pdev->class = &block_class; + pdev->type = &part_type; + pdev->parent = ddev; + + err = blk_alloc_devt(p, &devt); + if (err) + goto out_free_info; + pdev->devt = devt; + + /* delay uevent until 'holders' subdir is created */ + dev_set_uevent_suppress(pdev, 1); + err = device_add(pdev); + if (err) + goto out_put; + + err = -ENOMEM; + p->holder_dir = kobject_create_and_add("holders", &pdev->kobj); + if (!p->holder_dir) + goto out_del; + + dev_set_uevent_suppress(pdev, 0); + + /* everything is up and running, commence */ + rcu_assign_pointer(ptbl->part[partno], p); + + /* suppress uevent if the disk suppresses it */ + if (!dev_get_uevent_suppress(ddev)) + kobject_uevent(&pdev->kobj, KOBJ_ADD); + + hd_ref_init(p); + return p; + +out_free_info: + free_part_info(p); +out_free: + kfree(p); + return ERR_PTR(err); +out_del: + kobject_put(p->holder_dir); + device_del(pdev); +out_put: + put_device(pdev); + blk_free_devt(devt); + return ERR_PTR(err); +} + +static inline int card_proc_info(struct seq_file *m, char *dev_name, int i) +{ + struct partitions *this = &(pt_fmt->partitions[i]); + + if (i >= pt_fmt->part_num) + return 0; + + seq_printf(m, "%s%02d: %9llx %9x \"%s\"\n", dev_name, + i+1, (unsigned long long)this->size, + 512*1024, this->name); + return 0; +} + +static int card_proc_show(struct seq_file *m, void *v) +{ + int i; + + seq_puts(m, "dev: size erasesize name\n"); + for (i = 0; i < 16; i++) + card_proc_info(m, "inand", i); + + return 0; +} + +static int card_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, card_proc_show, NULL); +} + +static const struct file_operations card_proc_fops = { + .open = card_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int add_emmc_partition(struct gendisk *disk, + struct mmc_partitions_fmt *pt_fmt) +{ + unsigned int i; + struct hd_struct *ret = NULL; + uint64_t offset, size, cap; + struct partitions *pp; + struct proc_dir_entry *proc_card; + + pr_info("add_emmc_partition\n"); + + cap = get_capacity(disk); /* unit:512 bytes */ + for (i = 0; i < pt_fmt->part_num; i++) { + pp = &(pt_fmt->partitions[i]); + offset = pp->offset >> 9; /* unit:512 bytes */ + size = pp->size >> 9; /* unit:512 bytes */ + if ((offset + size) <= cap) { + ret = add_emmc_each_part(disk, 1+i, offset, + size, 0, pp->name); + + pr_info("[%sp%02d] %20s offset 0x%012llx, size 0x%012llx %s\n", + disk->disk_name, 1+i, + pp->name, offset<<9, + size<<9, IS_ERR(ret) ? "add fail":""); + } else { + pr_info("[%s] %s: partition exceeds device capacity:\n", + __func__, disk->disk_name); + + pr_info("\%20s offset 0x%012llx, size 0x%012llx\n", + pp->name, offset<<9, size<<9); + + break; + } + } + /* create /proc/inand */ + + proc_card = proc_create("inand", 0444, NULL, &card_proc_fops); + if (!proc_card) + pr_info("[%s] create /proc/inand fail.\n", __func__); + + /* create /proc/ntd */ + if (!proc_create("ntd", 0444, NULL, &card_proc_fops)) + pr_info("[%s] create /proc/ntd fail.\n", __func__); + + return 0; +} + +static int is_card_emmc(struct mmc_card *card) +{ + struct mmc_host *mmc = card->host; + + /* emmc port, so it must be an eMMC or TSD */ + if (!strcmp(mmc_hostname(mmc), "emmc")) + return 1; + else + return 0; + /*return mmc->is_emmc_port;*/ +} + +static ssize_t emmc_version_get(struct class *class, + struct class_attribute *attr, char *buf) +{ + int num = 0; + + return sprintf(buf, "%d", num); +} + +static void show_partition_table(struct partitions *table) +{ + int i = 0; + struct partitions *par_table = NULL; + + pr_info("show partition table:\n"); + for (i = 0; i < MAX_MMC_PART_NUM; i++) { + par_table = &table[i]; + if (par_table->size == -1) + pr_info("part: %d, name : %10s, size : %-4s mask_flag %d\n", + i, par_table->name, "end", + par_table->mask_flags); + else + pr_info("part: %d, name : %10s, size : %-4llx mask_flag %d\n", + i, par_table->name, par_table->size, + par_table->mask_flags); + } +} + +static ssize_t emmc_part_table_get(struct class *class, + struct class_attribute *attr, char *buf) +{ + struct partitions *part_table = NULL; + struct partitions *tmp_table = NULL; + int i = 0, part_num = 0; + + tmp_table = pt_fmt->partitions; + part_table = kmalloc(MAX_MMC_PART_NUM + *sizeof(struct partitions), GFP_KERNEL); + + if (!part_table) { + pr_info("[%s] malloc failed for part_table!\n", __func__); + return -ENOMEM; + } + + for (i = 0; i < MAX_MMC_PART_NUM; i++) { + if (tmp_table[i].mask_flags == STORE_CODE) { + strncpy(part_table[part_num].name, + tmp_table[i].name, + MAX_MMC_PART_NAME_LEN); + + part_table[part_num].size = tmp_table[i].size; + part_table[part_num].offset = tmp_table[i].offset; + + part_table[part_num].mask_flags = + tmp_table[i].mask_flags; + part_num++; + } + } + for (i = 0; i < MAX_MMC_PART_NUM; i++) { + if (tmp_table[i].mask_flags == STORE_CACHE) { + strncpy(part_table[part_num].name, + tmp_table[i].name, + MAX_MMC_PART_NAME_LEN); + + part_table[part_num].size = tmp_table[i].size; + part_table[part_num].offset = tmp_table[i].offset; + + part_table[part_num].mask_flags = + tmp_table[i].mask_flags; + + part_num++; + } + } + for (i = 0; i < MAX_MMC_PART_NUM; i++) { + if (tmp_table[i].mask_flags == STORE_DATA) { + strncpy(part_table[part_num].name, + tmp_table[i].name, + MAX_MMC_PART_NAME_LEN); + + part_table[part_num].size = tmp_table[i].size; + part_table[part_num].offset = tmp_table[i].offset; + part_table[part_num].mask_flags = + tmp_table[i].mask_flags; + + if (!strncmp(part_table[part_num].name, "data", + MAX_MMC_PART_NAME_LEN)) + /* last part size is FULL */ + part_table[part_num].size = -1; + + part_num++; + } + } + + show_partition_table(part_table); + memcpy(buf, part_table, MAX_MMC_PART_NUM*sizeof(struct partitions)); + + kfree(part_table); + part_table = NULL; + + return MAX_MMC_PART_NUM*sizeof(struct partitions); +} + +static int store_device = -1; +static ssize_t store_device_flag_get(struct class *class, + struct class_attribute *attr, char *buf) +{ + if (store_device == -1) { + pr_info("[%s] get store device flag something wrong !\n", + __func__); + } + + return sprintf(buf, "%d", store_device); +} + +static ssize_t get_bootloader_offset(struct class *class, + struct class_attribute *attr, char *buf) +{ + int offset = 0; + + offset = 512; + return sprintf(buf, "%d", offset); +} + +/* + * extern u32 cd_irq_cnt[2]; + * + *static ssize_t get_cdirq_cnt(struct class *class, + * struct class_attribute *attr, char *buf) + *{ + * return sprintf(buf, "in:%d, out:%d\n", cd_irq_cnt[1], cd_irq_cnt[0]); + *} + */ + +static struct class_attribute aml_version = +__ATTR(version, 0444, emmc_version_get, NULL); +static struct class_attribute aml_part_table = +__ATTR(part_table, 0444, emmc_part_table_get, NULL); +static struct class_attribute aml_store_device = +__ATTR(store_device, 0444, store_device_flag_get, NULL); +static struct class_attribute bootloader_offset = +__ATTR(bl_off_bytes, 0444, get_bootloader_offset, NULL); + +/* for irq cd dbg */ +/* static struct class_attribute cd_irq_cnt_ = + * __ATTR(cdirq_cnt, S_IRUGO, get_cdirq_cnt, NULL); + */ + +int aml_emmc_partition_ops(struct mmc_card *card, struct gendisk *disk) +{ + int ret = 0; + struct mmc_host *mmc_host = card->host; + struct amlsd_host *host = mmc_priv(mmc_host); + struct disk_part_iter piter; + struct hd_struct *part; + struct class *aml_store_class = NULL; + + pr_info("Enter %s\n", __func__); + + if (!is_card_emmc(card)) /* not emmc, nothing to do */ + return 0; + + store_device = host->storage_flag; + pt_fmt = kmalloc(sizeof(struct mmc_partitions_fmt), GFP_KERNEL); + if (pt_fmt == NULL) { + /* pr_info( + * "[%s] malloc failed for struct mmc_partitions_fmt!\n", + * __func__); + */ + return -ENOMEM; + } + + mmc_claim_host(card->host); + disk_part_iter_init(&piter, disk, DISK_PITER_INCL_EMPTY); + + while ((part = disk_part_iter_next(&piter))) { + pr_info("Delete invalid mbr partition part %p, part->partno %d\n", + part, part->partno); + delete_partition(disk, part->partno); + } + disk_part_iter_exit(&piter); + + ret = mmc_read_partition_tbl(card, pt_fmt); + if (ret == 0) { /* ok */ + ret = add_emmc_partition(disk, pt_fmt); + } + mmc_release_host(card->host); + + amlmmc_dtb_init(card); + + aml_store_class = class_create(THIS_MODULE, "aml_store"); + if (IS_ERR(aml_store_class)) { + pr_info("[%s] create aml_store_class class fail.\n", __func__); + ret = -1; + goto out; + } + + ret = class_create_file(aml_store_class, &aml_version); + if (ret) { + pr_info("[%s] can't create aml_store_class file .\n", __func__); + goto out_class1; + } + ret = class_create_file(aml_store_class, &aml_part_table); + if (ret) { + pr_info("[%s] can't create aml_store_class file .\n", __func__); + goto out_class2; + } + ret = class_create_file(aml_store_class, &aml_store_device); + if (ret) { + pr_info("[%s] can't create aml_store_class file .\n", __func__); + goto out_class3; + } + + ret = class_create_file(aml_store_class, &bootloader_offset); + if (ret) { + pr_info("[%s] can't create aml_store_class file .\n", __func__); + goto out_class3; + } + + /* ret = class_create_file(aml_store_class, &cd_irq_cnt_); + *if (ret) { + * pr_info("[%s] can't create aml_store_class file .\n", __func__); + * goto out_class3; + *} + */ + pr_info("Exit %s %s.\n", __func__, (ret == 0)?"OK":"ERROR"); + return ret; + +out_class3: + class_remove_file(aml_store_class, &aml_part_table); +out_class2: + class_remove_file(aml_store_class, &aml_version); +out_class1: + class_destroy(aml_store_class); +out: + return ret; +} + + + diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 709a872ed484..80c7e9d018e9 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -47,6 +47,9 @@ #include "queue.h" #include "block.h" +#ifdef CONFIG_AMLOGIC_MMC +#include +#endif MODULE_ALIAS("mmc:block"); #ifdef MODULE_PARAM_PREFIX @@ -2607,6 +2610,11 @@ static int mmc_blk_probe(struct mmc_card *card) if (mmc_add_disk(md)) goto out; +#ifdef CONFIG_AMLOGIC_MMC + /* amlogic add emmc partitions ops */ + aml_emmc_partition_ops(card, md->disk); +#endif + list_for_each_entry(part_md, &md->part, part) { if (mmc_add_disk(part_md)) goto out; diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index c64266f5a399..17b70921a2fc 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -284,6 +284,10 @@ int mmc_add_card(struct mmc_card *card) int ret; const char *type; const char *uhs_bus_speed_mode = ""; +#ifdef CONFIG_AMLOGIC_MMC + int width; + struct mmc_host *mmc = card->host; +#endif static const char *const uhs_speeds[] = { [UHS_SDR12_BUS_SPEED] = "SDR12 ", [UHS_SDR25_BUS_SPEED] = "SDR25 ", @@ -332,6 +336,23 @@ int mmc_add_card(struct mmc_card *card) mmc_card_ddr52(card) ? "DDR " : "", type); } else { +#ifdef CONFIG_AMLOGIC_MMC + switch (mmc->ios.bus_width) { + case MMC_BUS_WIDTH_1: + width = 1; + break; + case MMC_BUS_WIDTH_4: + width = 4; + break; + case MMC_BUS_WIDTH_8: + width = 8; + break; + default: + width = -1; + break; + } +#endif + pr_info("%s: new %s%s%s%s%s%s card at address %04x\n", mmc_hostname(card->host), mmc_card_uhs(card) ? "ultra high speed " : @@ -341,6 +362,12 @@ int mmc_add_card(struct mmc_card *card) mmc_card_hs400es(card) ? "Enhanced strobe " : "", mmc_card_ddr52(card) ? "DDR " : "", uhs_bus_speed_mode, type, card->rca); + +#ifdef CONFIG_AMLOGIC_MMC + pr_info("%s: clock %d, %u-bit-bus-width\n ", + mmc_hostname(card->host), + mmc->actual_clock, width); +#endif } #ifdef CONFIG_DEBUG_FS diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 2553d903a82b..7a863a4c59ee 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2635,6 +2635,9 @@ EXPORT_SYMBOL(mmc_hw_reset); static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq) { host->f_init = freq; +#ifdef CONFIG_AMLOGIC_MMC + host->first_init_flag = 1; +#endif #ifdef CONFIG_MMC_DEBUG pr_info("%s: %s: trying to init card at %u Hz\n", diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index df19777068a6..491b5e1c1dc6 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1159,8 +1159,29 @@ static int mmc_select_hs400(struct mmc_card *card) } /* Switch card to HS400 */ +#ifdef CONFIG_AMLOGIC_MMC + if (card->ext_csd.raw_driver_strength & (1 << 1)) { + val = + (0x1 << EXT_CSD_DRV_STR_SHIFT) + | EXT_CSD_TIMING_HS400; + pr_info("%s: support driver strength type 1\n", + mmc_hostname(host)); + } else if (card->ext_csd.raw_driver_strength & (1 << 4)) { + val = + (0x4 << EXT_CSD_DRV_STR_SHIFT) + | EXT_CSD_TIMING_HS400; + pr_info("%s: support driver strength type 4\n", + mmc_hostname(host)); + } else { + val = EXT_CSD_TIMING_HS400; + pr_info("%s: no support driver strength type 1 and 4\n", + mmc_hostname(host)); + } +#else val = EXT_CSD_TIMING_HS400 | card->drive_strength << EXT_CSD_DRV_STR_SHIFT; +#endif + err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, val, card->ext_csd.generic_cmd6_time, @@ -1560,6 +1581,9 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); } +#ifdef CONFIG_AMLOGIC_MMC + host->first_init_flag = 0; +#endif if (!oldcard) { /* * Fetch CSD from card. diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index bd44ba8116d1..d740e2b83e9a 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -21,6 +21,7 @@ #include "core.h" #include "bus.h" +#include "host.h" #include "sd.h" #include "sdio_bus.h" #include "mmc_ops.h" @@ -1172,4 +1173,43 @@ err: return err; } +#ifdef CONFIG_AMLOGIC_MMC +int sdio_reset_comm(struct mmc_card *card) +{ + struct mmc_host *host = card->host; + u32 ocr; + u32 rocr; + int err; + pr_info("%s():\n", __func__); + mmc_claim_host(host); + + mmc_retune_disable(host); + mmc_go_idle(host); + + mmc_set_clock(host, host->f_min); + + err = mmc_send_io_op_cond(host, 0, &ocr); + if (err) + goto err; + + rocr = mmc_select_voltage(host, ocr); + if (!rocr) { + err = -EINVAL; + goto err; + } + + err = mmc_sdio_init_card(host, rocr, card, 0); + if (err) + goto err; + + mmc_release_host(host); + return 0; +err: + pr_err("%s: Error resetting SDIO communications (%d)\n", + mmc_hostname(host), err); + mmc_release_host(host); + return err; +} +EXPORT_SYMBOL(sdio_reset_comm); +#endif diff --git a/include/linux/amlogic/amlsd.h b/include/linux/amlogic/amlsd.h new file mode 100644 index 000000000000..60d7d7899390 --- /dev/null +++ b/include/linux/amlogic/amlsd.h @@ -0,0 +1,229 @@ +/* + * include/linux/amlogic/amlsd.h + * + * Copyright (C) 2016 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. + * + */ + +#ifndef AMLSD_H +#define AMLSD_H +#include + +#define AML_MMC_MAJOR_VERSION 1 +#define AML_MMC_MINOR_VERSION 07 +#define AML_MMC_VERSION \ + ((AML_MMC_MAJOR_VERSION << 8) | AML_MMC_MINOR_VERSION) +#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 int sd_emmc_debug; +extern const u8 tuning_blk_pattern_4bit[64]; +extern const u8 tuning_blk_pattern_8bit[128]; +#define DEBUG_SD_OF 1 +/* #define DEBUG_SD_OF 0 */ + +#define MODULE_NAME "amlsd" + +#if 0 +#define A0_GP_CFG0 (0xc8100240) +#define A0_GP_CFG2 (0xc8100248) +#define STORAGE_DEV_NOSET (0) +#define STORAGE_DEV_EMMC (1) +#define STORAGE_DEV_NAND (2) +#define STORAGE_DEV_SPI (3) +#define STORAGE_DEV_SDCARD (4) +#define STORAGE_DEV_USB (5) +#define LDO4DAC_REG_ADDR 0x4f +#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) +#define AMLSD_DBG_REG (1<<3) +#define AMLSD_DBG_RD_TIME (1<<4) +#define AMLSD_DBG_WR_TIME (1<<5) +#define AMLSD_DBG_BUSY_TIME (1<<6) +#define AMLSD_DBG_RD_DATA (1<<7) +#define AMLSD_DBG_WR_DATA (1<<8) +#define AMLSD_DBG_IOS (1<<9) +#define AMLSD_DBG_IRQ (1<<10) +#define AMLSD_DBG_CLKC (1<<11) +#define AMLSD_DBG_TUNING (1<<12) + +#define DETECT_CARD_IN 1 +#define DETECT_CARD_OUT 2 +#define DETECT_CARD_JTAG_IN 3 +#define DETECT_CARD_JTAG_OUT 4 + +#define EMMC_DAT3_PINMUX_CLR 0 +#define EMMC_DAT3_PINMUX_SET 1 + +#define CHECK_RET(ret) { \ + if (ret) \ + pr_info("[%s] gpio op failed(%d) at line %d\n",\ + __func__, ret, __LINE__); \ +} + +#define sdhc_dbg(dbg_level, fmt, args...) do {\ + if (dbg_level & sdhc_debug) \ + pr_info("[%s]" fmt, __func__, ##args); \ +} while (0) + +#define sdhc_err(fmt, args...) \ + pr_info("[%s] " fmt, __func__, ##args) + + +#define sdio_dbg(dbg_level, fmt, args...) do {\ + if (dbg_level & sdio_debug) \ + pr_info("[%s]" fmt, __func__, ##args); \ +} while (0) + +#define sdio_err(fmt, args...) \ + pr_info("[%s] " fmt, __func__, ##args) + +#define sd_emmc_dbg(dbg_level, fmt, args...) do {\ + if (dbg_level & sd_emmc_debug) \ + pr_info("[%s]" fmt, __func__, ##args); \ +} while (0) +#define sd_emmc_err(fmt, args...) \ + pr_warn("[%s] " fmt, __func__, ##args) + +#define SD_PARSE_U32_PROP_HEX(node, prop_name, prop, value) do { \ + if (!of_property_read_u32(node, prop_name, &prop)) {\ + value = prop;\ + prop = 0;\ + if (DEBUG_SD_OF) { \ + pr_info("get property:%25s, value:0x%08x\n", \ + prop_name, (unsigned int)value); \ + } \ + } \ +} while (0) + +#define SD_PARSE_U32_PROP_DEC(node, prop_name, prop, value) do { \ + if (!of_property_read_u32(node, prop_name, &prop)) {\ + value = prop;\ + prop = 0;\ + if (DEBUG_SD_OF) { \ + pr_info("get property:%25s, value:%d\n", \ + prop_name, (unsigned int)value); \ + } \ + } \ +} while (0) + +#define SD_PARSE_GPIO_NUM_PROP(node, prop_name, str, gpio_pin) {\ + if (!of_property_read_string(node, prop_name, &str)) {\ + gpio_pin = \ + of_get_named_gpio(node, \ + prop_name, 0);\ + if (DEBUG_SD_OF) { \ + pr_info("get property:%25s, str:%s\n",\ + prop_name, str);\ + } \ + } \ +} + +#define SD_PARSE_STRING_PROP(node, prop_name, str, prop) {\ + if (!of_property_read_string(node, prop_name, &str)) {\ + strcpy(prop, str);\ + if (DEBUG_SD_OF) {\ + pr_info("get property:%25s, str:%s\n",\ + prop_name, prop); \ + } \ + } \ +} + +#define SD_CAPS(a, b) { .caps = a, .name = b } + +struct sd_caps { + unsigned int caps; + const char *name; +}; + +void aml_mmc_ver_msg_show(void); +extern int sdio_reset_comm(struct mmc_card *card); +#if 0 +extern int storage_flag; + +extern void aml_debug_print_buf(char *buf, int size); +extern int aml_buf_verify(int *buf, int blocks, int lba); +extern void aml_sdhc_init_debugfs(struct mmc_host *mmc); +void aml_sdhc_print_reg_(u32 *buf); +extern void aml_sdhc_print_reg(struct amlsd_host *host); +extern void aml_sdio_init_debugfs(struct mmc_host *mmc); +extern void aml_sd_emmc_init_debugfs(struct mmc_host *mmc); +extern void aml_sdio_print_reg(struct amlsd_host *host); +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); +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, + struct mmc_host *mmc, u32 index); + +void of_amlsd_irq_init(struct amlsd_platform *pdata); +int of_amlsd_init(struct amlsd_platform *pdata); +#if 0 +int amlsd_get_reg_base(struct platform_device *pdev, + struct amlsd_host *host); + +/* int of_amlsd_detect(struct amlsd_platform* pdata); */ +void of_amlsd_pwr_prepare(struct amlsd_platform *pdata); +void of_amlsd_pwr_on(struct amlsd_platform *pdata); +void of_amlsd_pwr_off(struct amlsd_platform *pdata); + +int aml_sd_uart_detect(struct amlsd_platform *pdata); +void aml_sd_uart_detect_clr(struct amlsd_platform *pdata); +#endif +void of_amlsd_xfer_pre(struct mmc_host *mmc); +void of_amlsd_xfer_post(struct mmc_host *mmc); + +irqreturn_t aml_sd_irq_cd(int irq, void *dev_id); +irqreturn_t aml_irq_cd_thread(int irq, void *data); +#if 0 +void aml_sduart_pre(struct amlsd_platform *pdata); + +/* chip select high */ +void aml_cs_high(struct amlsd_platform *pdata); + +/* chip select don't care */ +void aml_cs_dont_care(struct amlsd_platform *pdata); + +/* is eMMC/tSD exist */ +bool is_emmc_exist(struct amlsd_host *host); +void aml_devm_pinctrl_put(struct amlsd_host *host); +/* void of_init_pins (struct amlsd_platform* pdata); */ + +void aml_dbg_print_pinmux(void); +#ifdef CONFIG_MMC_AML_DEBUG +void aml_dbg_verify_pull_up(struct amlsd_platform *pdata); +int aml_dbg_verify_pinmux(struct amlsd_platform *pdata); +#endif +#endif +void aml_snprint (char **pp, int *left_size, const char *fmt, ...); + +int of_amlsd_ro(struct amlsd_platform *pdata); +int aml_sd_voltage_switch(struct mmc_host *mmc, char signal_voltage); +int aml_signal_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios); + +int aml_check_unsupport_cmd(struct mmc_host *mmc, struct mmc_request *mrq); +extern void aml_emmc_hw_reset(struct mmc_host *mmc); +#endif + + diff --git a/include/linux/amlogic/sd.h b/include/linux/amlogic/sd.h new file mode 100644 index 000000000000..750288df90ad --- /dev/null +++ b/include/linux/amlogic/sd.h @@ -0,0 +1,1507 @@ +/* + * include/linux/amlogic/sd.h + * + * Copyright (C) 2016 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. + * + */ + +#ifndef __AML_SD_H__ +#define __AML_SD_H__ + +#include +#include +#include +#include +#include +/* #include */ + +#define AML_ERROR_RETRY_COUNTER 10 +#define AML_TIMEOUT_RETRY_COUNTER 2 +#define AML_CALIBRATION +#define AML_SDHC_MAGIC "amlsdhc" +#define AML_SDIO_MAGIC "amlsdio" +#define AML_SD_EMMC_MAGIC "amlsd_emmc" +#define SD_EMMC_MANUAL_CMD23 +#define MAX_TUNING_RETRY 4 +#define TUNING_NUM_PER_POINT 10 +#define CALI_PATTERN_OFFSET ((SZ_1M * (36 + 3)) / 512) +/* #define AML_RESP_WR_EXT */ +#ifdef AML_CALIBRATION +#define MAX_CALI_RETRY 3 +#define MAX_DELAY_CNT 16 +#define CALI_BLK_CNT 10 +#endif + +#define SD_EMMC_CLOCK 0x0 +#define SD_EMMC_DELAY 0x4 +#define SD_EMMC_ADJUST 0x8 +#define SD_EMMC_CALOUT 0x10 +#define SD_EMMC_START 0x40 +#define SD_EMMC_CFG 0x44 +#define SD_EMMC_STATUS 0x48 +#define SD_EMMC_IRQ_EN 0x4c +#define SD_EMMC_CMD_RSP 0x5c +#define SD_EMMC_CMD_RSP1 0x60 +#define SD_EMMC_CMD_RSP2 0x64 +#define SD_EMMC_CMD_RSP3 0x68 + +#define CLK_DIV_SHIFT 0 +#define CLK_DIV_WIDTH 6 +#define CLK_DIV_MASK 0x3f +#define CLK_DIV_MAX 63 +#define CLK_SRC_SHIFT 6 +#define CLK_SRC_WIDTH 2 +#define CLK_SRC_MASK 0x3 +#define CLK_SRC_XTAL_RATE 24000000 +#define CLK_SRC_PLL_RATE 1000000000 + +#define CFG_BLK_LEN_SHIFT 4 +#define CFG_BLK_LEN_MASK 0xf + +#define CMD_CFG_LENGTH_SHIFT 0 +#define CMD_CFG_LENGTH_MASK 0x1ff +#define CMD_CFG_BLOCK_MODE BIT(9) +#define CMD_CFG_DATA_IO BIT(18) +#define CMD_CFG_DATA_WR BIT(19) +#define CMD_CFG_DATA_NUM BIT(23) + +#define CMD_DATA_MASK (~0x3) + +struct aml_tuning_data { + const u8 *blk_pattern; + unsigned int blksz; +}; + +enum aml_mmc_waitfor { + XFER_INIT, /* 0 */ + XFER_START, /* 1 */ + XFER_AFTER_START, /* 2 */ + XFER_IRQ_OCCUR, /* 3 */ + XFER_IRQ_TASKLET_CMD, /* 4 */ + XFER_IRQ_TASKLET_DATA, /* 5 */ + XFER_IRQ_TASKLET_BUSY, /* 6 */ + XFER_IRQ_UNKNOWN_IRQ, /* 7 */ + XFER_TIMER_TIMEOUT, /* 8 */ + XFER_TASKLET_CMD, /* 9 */ + XFER_TASKLET_DATA, /* 10 */ + XFER_TASKLET_BUSY, /* 11 */ + XFER_TIMEDOUT, /* 12 */ + XFER_FINISHED, /* 13 */ +}; + +enum aml_host_status { /* Host controller status */ + HOST_INVALID = 0, /* 0, invalid value */ + HOST_RX_FIFO_FULL = 1, /* 1, start with 1 */ + HOST_TX_FIFO_EMPTY, /* 2 */ + HOST_RSP_CRC_ERR, /* 3 */ + HOST_DAT_CRC_ERR, /* 4 */ + HOST_RSP_TIMEOUT_ERR, /* 5 */ + HOST_DAT_TIMEOUT_ERR, /* 6 */ + HOST_ERR_END, /* 7, end of errors */ + HOST_TASKLET_CMD, /* 8 */ + HOST_TASKLET_DATA, /* 9 */ +}; + +enum aml_host_bus_fsm { /* Host bus fsm status */ + BUS_FSM_IDLE, /* 0, idle */ + BUS_FSM_SND_CMD, /* 1, send cmd */ + BUS_FSM_CMD_DONE, /* 2, wait for cmd done */ + BUS_FSM_RESP_START, /* 3, resp start */ + BUS_FSM_RESP_DONE, /* 4, wait for resp done */ + BUS_FSM_DATA_START, /* 5, data start */ + BUS_FSM_DATA_DONE, /* 6, wait for data done */ + BUS_FSM_DESC_WRITE_BACK,/* 7, wait for desc write back */ + BUS_FSM_IRQ_SERVICE, /* 8, wait for irq service */ +}; + +enum aml_host_tuning_mode { + NONE_TUNING, + ADJ_TUNING_MODE, + AUTO_TUNING_MODE, + RX_PHASE_DELAY_TUNING_MODE, +}; + +struct cali_data { + u8 ln_delay[8]; + u32 base_index[10]; + u32 base_index_max; + u32 base_index_min; +}; + +struct cali_ctrl { + u8 line_x; + u8 cal_time; + u8 dly_tmp; + u8 max_index; +}; + +struct amlsd_host; +struct amlsd_platform { + struct amlsd_host *host; + struct mmc_host *mmc; + struct list_head sibling; + u32 ocr_avail; + u32 port; +#define PORT_SDIO_A 0 +#define PORT_SDIO_B 1 +#define PORT_SDIO_C 2 +#define PORT_SDHC_A 3 +#define PORT_SDHC_B 4 +#define PORT_SDHC_C 5 + + unsigned int caps; + unsigned int caps2; + unsigned int card_capacity; + unsigned int tx_phase; + unsigned int tx_delay; + unsigned int f_min; + unsigned int f_max; + unsigned int clkc; + unsigned int clk2; + unsigned int clkc_w; + unsigned int ctrl; + unsigned int clock; + /* signalling voltage (1.8V or 3.3V) */ + unsigned char signal_voltage; + + unsigned int low_burst; + struct mutex in_out_lock; + unsigned int irq_cd; + unsigned int gpio_cd; + unsigned int gpio_cd_level; + unsigned int gpio_power; + unsigned int power_level; + unsigned int auto_clk_close; + unsigned int vol_switch; + unsigned int vol_switch_18; + unsigned int vol_switch_delay; + char pinname[32]; + unsigned int gpio_ro; + unsigned int gpio_dat3; + unsigned int hw_reset; + unsigned int jtag_pin; + int is_sduart; + unsigned int card_in_delay; + bool is_in; + bool is_tuned; /* if card has been tuning */ + bool need_retuning; + bool rmpb_cmd_flag; + bool rpmb_valid_command; + /* we used this flag to filter + * some unnecessary cmd before initialized flow + */ + /* has been initialized for the first time */ + bool is_fir_init; + struct delayed_work retuning; +#ifdef AML_CALIBRATION + unsigned char caling; + unsigned char calout[20][20]; +#endif + /* 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 + */ + unsigned int card_type; + struct cali_ctrl c_ctrl; + /* unknown */ +#define CARD_TYPE_UNKNOWN 0 + /* MMC card */ +#define CARD_TYPE_MMC 1 + /* SD card */ +#define CARD_TYPE_SD 2 + /* SDIO card */ +#define CARD_TYPE_SDIO 3 + /* SD combo (IO+mem) card */ +#define CARD_TYPE_SD_COMBO 4 + /* NON sdio device (means SD/MMC card) */ +#define CARD_TYPE_NON_SDIO 5 + +#define aml_card_type_unknown(c) ((c)->card_type == CARD_TYPE_UNKNOWN) +#define aml_card_type_mmc(c) ((c)->card_type == CARD_TYPE_MMC) +#define aml_card_type_sd(c) ((c)->card_type == CARD_TYPE_SD) +#define aml_card_type_sdio(c) ((c)->card_type == CARD_TYPE_SDIO) +#define aml_card_type_non_sdio(c) ((c)->card_type == CARD_TYPE_NON_SDIO) + + /* struct pinctrl *uart_ao_pinctrl; */ + void (*irq_init)(struct amlsd_platform *pdata); + + unsigned int max_blk_count; + unsigned int max_blk_size; + unsigned int max_req_size; + unsigned int max_seg_size; + + /*for inand partition: struct mtd_partition, easy porting from nand*/ + struct mtd_partition *parts; + unsigned int nr_parts; + + struct resource *resource; + void (*xfer_pre)(struct mmc_host *mmc); + void (*xfer_post)(struct mmc_host *mmc); + + int (*port_init)(struct amlsd_platform *pdata); + int (*cd)(struct amlsd_platform *pdata); + int (*ro)(struct amlsd_platform *pdata); + void (*pwr_pre)(struct amlsd_platform *pdata); + void (*pwr_on)(struct amlsd_platform *pdata); + void (*pwr_off)(struct amlsd_platform *pdata); + +}; + +struct aml_emmc_adjust { + int adj_win_start; + int adj_win_len; + int adj_point; + int clk_div; +}; + +struct aml_emmc_rxclk { + int rxclk_win_start; + int rxclk_win_len; + int rxclk_rx_phase; + int rxclk_rx_delay; + int rxclk_point; +}; + +#define MUX_CLK_NUM_PARENTS 2 +struct amlsd_host { + /* back-link to device */ + struct device *dev; + struct list_head sibling; + struct platform_device *pdev; + struct amlsd_platform *pdata; + struct mmc_host *mmc; + struct mmc_request *request; + + struct mmc_command *cmd; + u32 ocr_mask; + struct clk *core_clk; + struct clk_mux mux; + struct clk *mux_clk; + struct clk *mux_parent[MUX_CLK_NUM_PARENTS]; + unsigned long mux_parent_rate[MUX_CLK_NUM_PARENTS]; + struct clk_divider cfg_div; + struct clk *cfg_div_clk; + + struct resource *mem; + struct sd_emmc_regs *sd_emmc_regs; + void __iomem *base; + void __iomem *pinmux_base; + int dma; + char *bn_buf; + dma_addr_t bn_dma_buf; +#ifdef AML_RESP_WR_EXT + u32 *resp_buf; + dma_addr_t resp_dma_buf; +#endif + dma_addr_t dma_gdesc; /* 0x200 */ + dma_addr_t dma_gping; /* 0x400 */ + dma_addr_t dma_gpong; /* 0x800 */ + char is_tunning; + char tuning_mode; + unsigned int irq; + unsigned int irq_in; + unsigned int irq_out; + unsigned int f_max; + unsigned int f_max_w; + unsigned int f_min; + int sdio_irqen; + unsigned int error_bak; + struct delayed_work timeout; + struct class debug; + + unsigned int send; + unsigned int ctrl; + unsigned int clkc; + unsigned int misc; + unsigned int ictl; + unsigned int ista; + unsigned int dma_addr; + + unsigned long clk_rate; + + char *desc_buf; + dma_addr_t desc_dma_addr; + unsigned int dma_sts; + unsigned int sg_cnt; + char *desc_cur; + unsigned int desc_cur_cnt; + char *desc_pre; + unsigned int desc_pre_cnt; + struct mmc_request *mrq; + struct mmc_request *mrq2; + spinlock_t mrq_lock; + struct mutex pinmux_lock; + int cmd_is_stop; + enum aml_mmc_waitfor xfer_step; + enum aml_mmc_waitfor xfer_step_prev; + + int bus_width; + int port; + int locked; + bool is_gated; + unsigned char sd_sdio_switch_volat_done; + + int status; /* host status: xx_error/ok */ + int init_flag; + + char *msg_buf; +#define MESSAGE_BUF_SIZE 512 + +#ifdef CONFIG_DEBUG_FS + struct dentry *debug_root; + struct dentry *debug_state; + struct dentry *debug_regs; +#endif + +#ifdef CONFIG_CPU_FREQ + struct notifier_block freq_transition; +#endif + + u32 opcode; + u32 arg; + u32 cmd25_cnt; + +#ifdef CONFIG_MMC_AML_DEBUG + u32 req_cnt; + u32 trans_size; + + u32 reg_buf[16]; +#endif + u32 time_req_sta; /* request start time */ + + struct pinctrl *pinctrl; + char pinctrl_name[30]; + /* used for judging if there is a tsd/emmc */ + int storage_flag; + /* bit[7-0]--minor version, bit[31-8]--major version */ + int version; + unsigned long clksrc_rate; + struct aml_emmc_adjust emmc_adj; + struct aml_emmc_rxclk emmc_rxclk; + u32 error_flag; +}; + +/*-sdio-*/ + +#define SDIO_ARGU (0x0) +#define SDIO_SEND (0x4) +#define SDIO_CONF (0x8) +#define SDIO_IRQS (0xc) +#define SDIO_IRQC (0x10) +#define SDIO_MULT (0x14) +#define SDIO_ADDR (0x18) +#define SDIO_EXT (0x1c) +#define SDIO_CCTL (0x40) +#define SDIO_CDAT (0x44) + +#define CLK_DIV (0x1f4) + +struct cmd_send { + u32 cmd_command:8; /*[7:0] Command Index*/ + u32 cmd_response_bits:8; + /*[15:8] + * 00 means no response + * others: Response bit number + * (cmd bits+response bits+crc bits-1) + */ + u32 response_do_not_have_crc7:1; + /*[16] + * 0:Response need check CRC7, + * 1: dont need check + */ + u32 response_have_data:1; + /*[17] + * 0:Receiving Response without data, + * 1:Receiving response with data + */ + u32 response_crc7_from_8:1; + /*[18] + * 0:Normal CRC7, Calculating CRC7 will + * be from bit0 of all response bits, + * 1:Calculating CRC7 will be from + * bit8 of all response bits + */ + u32 check_busy_on_dat0:1; + /*[19] + * used for R1b response + * 0: dont check busy on dat0, + * 1:need check + */ + u32 cmd_send_data:1; + /*[20] + * 0:This command is not for transmitting data, + * 1:This command is for transmitting data + */ + u32 use_int_window:1; + /*[21] + * 0:SDIO DAT1 interrupt window disabled, 1:Enabled + */ + u32 reserved:2;/*[23:22]*/ + u32 repeat_package_times:8; + /*[31:24] Total packages to be sent*/ +}; + +struct sdio_config { + u32 cmd_clk_divide:10; + /*[9:0] Clock rate setting, + * Frequency of SD equals to Fsystem/((cmd_clk_divide+1)*2) + */ + u32 cmd_disable_crc:1; + /*[10] + * 0:CRC employed, 1:dont send CRC during command being sent + */ + u32 cmd_out_at_posedge:1; + /*[11] + * Command out at negedge normally, 1:at posedge + */ + u32 cmd_argument_bits:6; + /*[17:12] before CRC added, normally 39*/ + u32 do_not_delay_data:1; + /*[18] + *0:Delay one clock normally, 1:dont delay + */ + u32 data_latch_at_negedge:1; + /*[19] + * 0:Data caught at posedge normally, 1:negedge + */ + u32 bus_width:1; + /*[20] 0:1bit, 1:4bit*/ + u32 m_endian:2; + /*[22:21] + * Change ENDIAN(bytes order) from DMA data (e.g. dma_din[31:0]). + * (00: ENDIAN no change, data output equals to original dma_din[31:0]; + * 01: data output equals to {dma_din[23:16],dma_din[31:24], + * dma_din[7:0],dma_din[15:8]};10: data output equals to + * {dma_din[15:0],dma_din[31:16]};11: data output equals to + * {dma_din[7:0],dma_din[15:8],dma_din[23:16],dma_din[31:24]}) + */ + u32 sdio_write_nwr:6; + /*[28:23] + * Number of clock cycles waiting before writing data + */ + u32 sdio_write_crc_ok_status:3; + /*[31:29] if CRC status + * equals this register, sdio write can be consider as correct + */ +}; + +struct sdio_status_irq { + u32 sdio_status:4; + /*[3:0] Read Only + * SDIO State Machine Current State, just for debug + */ + u32 sdio_cmd_busy:1; + /*[4] Read Only + * SDIO Command Busy, 1:Busy State + */ + u32 sdio_response_crc7_ok:1; + /*[5] Read Only + * SDIO Response CRC7 status, 1:OK + */ + u32 sdio_data_read_crc16_ok:1; + /*[6] Read Only + * SDIO Data Read CRC16 status, 1:OK + */ + u32 sdio_data_write_crc16_ok:1; + /*[7] Read Only + * SDIO Data Write CRC16 status, 1:OK + */ + u32 sdio_if_int:1; + /*[8] write 1 clear this int bit + * SDIO DAT1 Interrupt Status + */ + u32 sdio_cmd_int:1; + /*[9] write 1 clear this int bit + * Command Done Interrupt Status + */ + u32 sdio_soft_int:1; + /*[10] write 1 clear this int bit + * Soft Interrupt Status + */ + u32 sdio_set_soft_int:1; + /*[11] write 1 to this bit + * will set Soft Interrupt, read out is m_req_sdio, just for debug + */ + u32 sdio_status_info:4; + /*[15:12] + * used for change information between ARC and Amrisc + */ + u32 sdio_timing_out_int:1; + /*[16] write 1 clear this int bit + * Timeout Counter Interrupt Status + */ + u32 amrisc_timing_out_int_en:1; + /*[17] + * Timeout Counter Interrupt Enable for AMRISC + */ + u32 arc_timing_out_int_en:1; + /*[18] + * Timeout Counter Interrupt Enable for ARC/ARM + */ + u32 sdio_timing_out_count:13; + /*[31:19] + * Timeout Counter Preload Setting and Present Status + */ +}; + +struct sdio_irq_config { + u32 amrisc_if_int_en:1; + /*[0] + * 1:SDIO DAT1 Interrupt Enable for AMRISC + */ + u32 amrisc_cmd_int_en:1; + /*[1] + * 1:Command Done Interrupt Enable for AMRISC + */ + u32 amrisc_soft_int_en:1; + /*[2] + * 1:Soft Interrupt Enable for AMRISC + */ + u32 arc_if_int_en:1; + /*[3] + * 1:SDIO DAT1 Interrupt Enable for ARM/ARC + */ + u32 arc_cmd_int_en:1; + /*[4] + * 1:Command Done Interrupt Enable for ARM/ARC + */ + u32 arc_soft_int_en:1; + /*[5] + * 1:Soft Interrupt Enable for ARM/ARC + */ + u32 sdio_if_int_config:2; + /*[7:6] + * 00:sdio_if_interrupt window will reset after data Tx/Rx or command + * done, others: only after command done + */ + u32 sdio_force_data:6; + /*[13:8] + * Write operation: Data forced by software + * Read operation: {CLK,CMD,DAT[3:0]} + */ + u32 sdio_force_enable:1; + /*[14] Software Force Enable + * This is the software force mode, Software can directly + * write to sdio 6 ports (cmd, clk, dat0..3) if force_output_en + * is enabled. and hardware outputs will be bypassed. + */ + u32 soft_reset:1; + /*[15] + * Write 1 Soft Reset, Don't need to clear it + */ + u32 sdio_force_output_en:6; + /*[21:16] + * Force Data Output Enable,{CLK,CMD,DAT[3:0]} + */ + u32 disable_mem_halt:2; + /*[23:22] write and read + * 23:Disable write memory halt, 22:Disable read memory halt + */ + u32 sdio_force_data_read:6; + /*[29:24] Read Only + * Data read out which have been forced by software + */ + u32 force_halt:1; + /*[30] 1:Force halt SDIO by software + * Halt in this sdio host controller means stop to transmit or + * receive data from sd card. and then sd card clock will be shutdown. + * Software can force to halt anytime, and hardware will automatically + * halt the sdio when reading fifo is full or writing fifo is empty + */ + u32 halt_hole:1; + /*[31] + * 0: SDIO halt for 8bit mode, 1:SDIO halt for 16bit mode + */ +}; + +struct sdio_mult_config { + u32 sdio_port_sel:2; /*[1:0] 0:sdio_a, 1:sdio_b, 2:sdio_c*/ + u32 ms_enable:1; /*[2] 1:Memory Stick Enable*/ + u32 ms_sclk_always:1; /*[3] 1: Always send ms_sclk*/ + u32 stream_enable:1; /*[4] 1:Stream Enable*/ + u32 stream_8_bits_mode:1; /*[5] Stream 8bits mode*/ + u32 data_catch_level:2; /*[7:6] Level of data catch*/ + u32 write_read_out_index:1; + /*[8] Write response index Enable + * [31:16], [11:10], [7:0] is set only when + * bit8 of this register is not set. + * And other bits are set only when bit8 + * of this register is also set. + */ + u32 data_catch_readout_en:1; /*[9] Data catch readout Enable*/ + u32 sdio_0_data_on_1:1; /*[10] 1:dat0 is on dat1*/ + u32 sdio_1_data_swap01:1; /*[11] 1:dat1 and dat0 swapped*/ + u32 response_read_index:4; /*[15:12] Index of internal read response*/ + u32 data_catch_finish_point:12; + /*[27:16] If internal data + * catch counter equals this register, + * it indicates data catching is finished + */ + u32 reserved:4; /*[31:28]*/ +}; + +struct sdio_extension { + u32 cmd_argument_ext:16; + /*[15:0] for future use*/ + u32 data_rw_number:14; + /*[29:16] + * Data Read/Write Number in one packet, include CRC16 if has CRC16 + */ + u32 data_rw_do_not_have_crc16:1; + /*[30] + * 0:data Read/Write has crc16, 1:without crc16 + */ + u32 crc_status_4line:1; + /*[31] 1:4Lines check CRC Status*/ +}; + +struct sdio_reg { + u32 argument; /*2308*/ + struct cmd_send send; /*2309*/ + struct sdio_config config; /*230a*/ + struct sdio_status_irq status; /*230b*/ + struct sdio_irq_config irqc; /*230c*/ + struct sdio_mult_config mult; /*230d*/ + u32 m_addr; /*230e*/ + struct sdio_extension ext;/*230f*/ +}; + +/*-sdhc-*/ + +#define SDHC_ARGU (0x00) +#define SDHC_SEND (0x04) +#define SDHC_CTRL (0x08) +#define SDHC_STAT (0x0C) +#define SDHC_CLKC (0x10) +#define SDHC_ADDR (0x14) +#define SDHC_PDMA (0x18) +#define SDHC_MISC (0x1C) +#define SDHC_DATA (0x20) +#define SDHC_ICTL (0x24) +#define SDHC_ISTA (0x28) +#define SDHC_SRST (0x2C) +#define SDHC_ESTA (0x30) +#define SDHC_ENHC (0x34) +#define SDHC_CLK2 (0x38) + +/* sdio cbus register */ +#define CBUS_SDIO_ARGU (0x2308) +#define CBUS_SDIO_SEND (0x2309) +#define CBUS_SDIO_CONF (0x230a) +#define CBUS_SDIO_IRQS (0x230b) +#define CBUS_SDIO_IRQC (0x230c) +#define CBUS_SDIO_MULT (0x230d) +#define CBUS_SDIO_ADDR (0x230e) +#define CBUS_SDIO_EXT (0x230f) + + +/* CBUS reg definition */ +#define ISA_TIMERE 0x2655 +#define HHI_GCLK_MPEG0 0x1050 +#define ASSIST_POR_CONFIG 0x1f55 + +#define PREG_PAD_GPIO0_EN_N 0x200c +#define PREG_PAD_GPIO0_O 0x200d +#define PREG_PAD_GPIO0_I 0x200e +#define PREG_PAD_GPIO1_EN_N 0x200f +#define PREG_PAD_GPIO1_O 0x2010 +#define PREG_PAD_GPIO1_I 0x2011 +#define PREG_PAD_GPIO2_EN_N 0x2012 +#define PREG_PAD_GPIO2_O 0x2013 +#define PREG_PAD_GPIO2_I 0x2014 +#define PREG_PAD_GPIO3_EN_N 0x2015 +#define PREG_PAD_GPIO3_O 0x2016 +#define PREG_PAD_GPIO3_I 0x2017 +#define PREG_PAD_GPIO4_EN_N 0x2018 +#define PREG_PAD_GPIO4_O 0x2019 +#define PREG_PAD_GPIO4_I 0x201a +#define PREG_PAD_GPIO5_EN_N 0x201b +#define PREG_PAD_GPIO5_O 0x201c +#define PREG_PAD_GPIO5_I 0x201d + +#define PERIPHS_PIN_MUX_0 0x202c +#define PERIPHS_PIN_MUX_1 0x202d +#define PERIPHS_PIN_MUX_2 0x202e +#define PERIPHS_PIN_MUX_3 0x202f +#define PERIPHS_PIN_MUX_4 0x2030 +#define PERIPHS_PIN_MUX_5 0x2031 +#define PERIPHS_PIN_MUX_6 0x2032 +#define PERIPHS_PIN_MUX_7 0x2033 +#define PERIPHS_PIN_MUX_8 0x2034 +#define PERIPHS_PIN_MUX_9 0x2035 + +/* interrupt definition */ +#define INT_SDIO (60-32) +#define INT_SDHC (110-32) + +#define INT_GPIO_0 (96-32) +#define INT_GPIO_1 (97-32) +#define INT_GPIO_2 (98-32) +#define INT_GPIO_3 (99-32) +#define INT_GPIO_4 (100-32) +#define INT_GPIO_5 (101-32) +#define INT_GPIO_6 (102-32) +#define INT_GPIO_7 (103-32) + + +struct sdhc_send { + /*[5:0] command index*/ + u32 cmd_index:6; + /*[6] 0:no resp 1:has resp*/ + u32 cmd_has_resp:1; + /*[7] 0:no data 1:has data*/ + u32 cmd_has_data:1; + /*[8] 0:48bit 1:136bit*/ + u32 resp_len:1; + /*[9] 0:check crc7 1:don't check crc7*/ + u32 resp_no_crc:1; + /*[10] 0:data rx, 1:data tx*/ + u32 data_dir:1; + /*[11] 0:rx or tx, 1:data stop,ATTN:will give rx a softreset*/ + u32 data_stop:1; + /*[12] 0: resp with no busy, 1:R1B*/ + u32 r1b:1; + /*[15:13] reserved*/ + u32 reserved:3; + /*[31:16] total package number for writing or reading*/ + u32 total_pack:16; +}; + +struct sdhc_ctrl { + /*[1:0] 0:1bit, 1:4bits, 2:8bits, 3:reserved*/ + u32 dat_type:2; + /*[2] 0:SDR mode, 1:Don't set it*/ + u32 ddr_mode:1; + /*[3] 0:check sd write crc result, 1:disable tx crc check*/ + u32 tx_crc_nocheck:1; + /*[12:4] 0:512Bytes, 1:1, 2:2, ..., 511:511Bytes*/ + u32 pack_len:9; + /*[19:13] cmd or wcrc Receiving Timeout, default 64*/ + u32 rx_timeout:7; + /*[23:20]Period between response/cmd and next cmd, default 8*/ + u32 rx_period:4; + /*[26:24] Rx Endian Control*/ + u32 rx_endian:3; + /*[27]0:Normal mode, 1: support data block gap + *(need turn off clock gating) + */ + u32 sdio_irq_mode:1; + /*[28] Dat0 Interrupt selection,0:busy check after response, + *1:any rising edge of dat0 + */ + u32 dat0_irq_sel:1; + /*[31:29] Tx Endian Control*/ + u32 tx_endian:3; +}; + +struct sdhc_stat { + /*[0] 0:Ready for command, 1:busy*/ + u32 cmd_busy:1; + /*[4:1] DAT[3:0]*/ + u32 dat3_0:4; + /*[5] CMD*/ + u32 cmd:1; + /*[12:6] RxFIFO count*/ + u32 rxfifo_cnt:7; + /*[19:13] TxFIFO count*/ + u32 txfifo_cnt:7; + /*[23:20] DAT[7:4]*/ + u32 dat7_4:4; + /*[31:24] Reserved*/ + u32 reserved:8; +}; + +/* + * to avoid glitch issue, + * 1. clk_switch_on better be set after cfg_en be set to 1'b1 + * 2. clk_switch_off shall be set before cfg_en be set to 1'b0 + * 3. rx_clk/sd_clk phase diff please see SD_REGE_CLK2. + */ +struct sdhc_clkc { + /*[11:0] clk_div for TX_CLK 0: don't set it,1:div2, 2:div3, 3:div4 ...*/ + u32 clk_div:12; + /*[12] TX_CLK 0:switch off, 1:switch on*/ + u32 tx_clk_on:1; + /*[13] RX_CLK 0:switch off, 1:switch on*/ + u32 rx_clk_on:1; + /*[14] SD_CLK 0:switch off, 1:switch on*/ + u32 sd_clk_on:1; + /*[15] Clock Module Enable, Should set before bit[14:12] switch on, + * and after bit[14:12] switch off + */ + u32 mod_clk_on:1; + /*[17:16] 0:osc, 1:fclk_div4, 2:fclk_div3, 3:fclk_div5*/ + u32 clk_src_sel:2; + /*[23:18] Reserved*/ + u32 reserved:6; + /*[24] Clock JIC for clock gating control + *1: will turn off clock gating + */ + u32 clk_jic:1; + /*[26:25] 00:Memory Power Up, 11:Memory Power Off*/ + u32 mem_pwr_off:2; + /*[31:27] Reserved*/ + u32 reserved2:5; +}; + +/* + * Note1: dma_urgent is just set when bandwidth is very tight + * Note2: pio_rdresp need to be combined with REG0_ARGU; + * For R0, when 0, reading REG0 will get the normal 32bit response; + * For R2, when 1, reading REG0 will get CID[31:0], when 2, get CID[63:32], + * and so on; 6 or 7, will get original command argument. + */ +struct sdhc_pdma { + /*[0] 0:PIO mode, 1:DMA mode*/ + u32 dma_mode:1; + /*[3:1] 0:[39:8] 1:1st 32bits, 2:2nd ...,6 or 7:command argument*/ + u32 pio_rdresp:3; + /*[4] 0:not urgent, 1:urgent*/ + u32 dma_urgent:1; + /*[9:5] Number in one Write request burst(0:1,1:2...)*/ + u32 wr_burst:5; + /*[14:10] Number in one Read request burst(0:1, 1:2...)*/ + u32 rd_burst:5; + /*[21:15] RxFIFO threshold, >=rxth, will request write*/ + u32 rxfifo_th:7; + /*[28:22] TxFIFO threshold, <=txth, will request read*/ + u32 txfifo_th:7; + /*[30:29] [30]self-clear-flush,[29] mode: 0:hw, 1:sw*/ + u32 rxfifo_manual_flush:2; + /*[31] self-clear-fill, recommand to write before sd send*/ + u32 txfifo_fill:1; +}; + +struct sdhc_misc { + /*[3:0] reserved*/ + u32 reserved:4; + /*[6:4] WCRC Error Pattern*/ + u32 wcrc_err_patt:3; + /*[9:7] WCRC OK Pattern*/ + u32 wcrc_ok_patt:3; + /*[15:10] reserved*/ + u32 reserved1:6; + /*[21:16] Burst Number*/ + u32 burst_num:6; + /*[27:22] Thread ID*/ + u32 thread_id:6; + /*[28] 0:auto stop mode, 1:manual stop mode*/ + u32 manual_stop:1; + /*[31:29] txstart_thres(if (txfifo_cnt/4)> + * (threshold*2), Tx will start) + */ + u32 txstart_thres:3; +}; + +struct sdhc_ictl { + /*[0] Response is received OK*/ + u32 resp_ok:1; + /*[1] Response Timeout Error*/ + u32 resp_timeout:1; + /*[2] Response CRC Error*/ + u32 resp_err_crc:1; + /*[3] Response is received OK(always no self reset)*/ + u32 resp_ok_noclear:1; + /*[4] One Package Data Completed ok*/ + u32 data_1pack_ok:1; + /*[5] One Package Data Failed (Timeout Error)*/ + u32 data_timeout:1; + /*[6] One Package Data Failed (CRC Error)*/ + u32 data_err_crc:1; + /*[7] Data Transfer Completed ok*/ + u32 data_xfer_ok:1; + /*[8] RxFIFO count > threshold*/ + u32 rx_higher:1; + /*[9] TxFIFO count < threshold*/ + u32 tx_lower:1; + /*[10] SDIO DAT1 Interrupt*/ + u32 dat1_irq:1; + /*[11] DMA Done*/ + u32 dma_done:1; + /*[12] RxFIFO Full*/ + u32 rxfifo_full:1; + /*[13] TxFIFO Empty*/ + u32 txfifo_empty:1; + /*[14] Additional SDIO DAT1 Interrupt*/ + u32 addi_dat1_irq:1; + /*[15] reserved*/ + u32 reserved:1; + /*[17:16] sdio dat1 interrupt mask windows + * clear delay control,0:2cycle 1:1cycles + */ + u32 dat1_irq_delay:2; + /*[31:18] reserved*/ + u32 reserved1:14; +}; + +/*Note1: W1C is write one clear.*/ +struct sdhc_ista { + /*[0] Response is received OK (W1C)*/ + u32 resp_ok:1; + /*[1] Response is received Failed (Timeout Error) (W1C)*/ + u32 resp_timeout:1; + /*[2] Response is received Failed (CRC Error) (W1C)*/ + u32 resp_err_crc:1; + /*[3] Response is Received OK (always no self reset)*/ + u32 resp_ok_noclear:1; + /*[4] One Package Data Completed ok (W1C)*/ + u32 data_1pack_ok:1; + /*[5] One Package Data Failed (Timeout Error) (W1C)*/ + u32 data_timeout:1; + /*[6] One Package Data Failed (CRC Error) (W1C)*/ + u32 data_err_crc:1; + /*[7] Data Transfer Completed ok (W1C)*/ + u32 data_xfer_ok:1; + /*[8] RxFIFO count > threshold (W1C)*/ + u32 rx_higher:1; + /*[9] TxFIFO count < threshold (W1C)*/ + u32 tx_lower:1; + /*[10] SDIO DAT1 Interrupt (W1C)*/ + u32 dat1_irq:1; + /*[11] DMA Done (W1C)*/ + u32 dma_done:1; + /*[12] RxFIFO Full(W1C)*/ + u32 rxfifo_full:1; + /*[13] TxFIFO Empty(W1C)*/ + u32 txfifo_empty:1; + /*[14] Additional SDIO DAT1 Interrupt*/ + u32 addi_dat1_irq:1; + /*[31:13] reserved*/ + u32 reserved:17; +}; + +/* + * Note1: Soft reset for DPHY TX/RX needs programmer to set it + * and then clear it manually. + */ +struct sdhc_srst { + /*[0] Soft reset for MAIN CTRL(self clear)*/ + u32 main_ctrl:1; + /*[1] Soft reset for RX FIFO(self clear)*/ + u32 rxfifo:1; + /*[2] Soft reset for TX FIFO(self clear)*/ + u32 txfifo:1; + /*[3] Soft reset for DPHY RX*/ + u32 dphy_rx:1; + /*[4] Soft reset for DPHY TX*/ + u32 dphy_tx:1; + /*[5] Soft reset for DMA IF(self clear)*/ + u32 dma_if:1; + /*[31:6] reserved*/ + u32 reserved:26; +}; + +struct sdhc_enhc { + union { + struct { + /*[0] 0:Wrrsp Check in DMA Rx FSM 1:No Check in FSM*/ + u32 wrrsp_mode:1; + /*[1] Rx Done without checking if Wrrsp count is 0*/ + u32 chk_wrrsp:1; + /*[2] Rx Done without checking if DMA is IDLE*/ + u32 chk_dma:1; + /*[5:3] debug only*/ + u32 debug:3; + u32 reserved:2; + /*[15:8] SDIO IRQ Period Setting*/ + u32 sdio_irq_period:8; + u32 reserved1:2; + /*[24:18] RXFIFO Full Threshold,default 60*/ + u32 rxfifo_th:7; + /*[31:25] TXFIFO Empty Threshold,default 0*/ + u32 txfifo_th:7; + } meson8m2; + struct { + /*[7:0] Data Rx Timeout Setting*/ + u32 rx_timeout:8; + /*[15:8] SDIO IRQ Period Setting + *(IRQ checking window length) + */ + u32 sdio_irq_period:8; + /*[16] No Read DMA Response Check*/ + u32 dma_rd_resp:1; + /*[16] No Write DMA Response Check*/ + u32 dma_wr_resp:1; + /*[24:18] RXFIFO Full Threshold,default 60*/ + u32 rxfifo_th:7; + /*[31:25] TXFIFO Empty Threshold,default 0*/ + u32 txfifo_th:7; + } meson; + } reg; +}; + +struct sdhc_clk2 { + /*[11:0] rx_clk phase diff(default 0:no diff, + *1:one input clock cycle ...) + */ + u32 rx_clk_phase:12; + /*[23:12] sd_clk phase diff(default 0:half(180 degree), + *1:half+one input clock cycle, 2:half+2 input clock cycles, ...) + */ + u32 sd_clk_phase:12; + /*[31:24] reserved*/ + u32 reserved:8; +}; + +#define SDHC_CLOCK_SRC_OSC 0 /* 24MHz */ +#define SDHC_CLOCK_SRC_FCLK_DIV4 1 +#define SDHC_CLOCK_SRC_FCLK_DIV3 2 +#define SDHC_CLOCK_SRC_FCLK_DIV5 3 +#define SDHC_ISTA_W1C_ALL 0x7fff +#define SDHC_SRST_ALL 0x3f +#define SDHC_ICTL_ALL 0x7fff + +struct sd_emmc_regs { + u32 gclock; /* 0x00 */ + u32 gdelay; /* 0x04 */ + u32 gadjust; /* 0x08 */ + u32 reserved_0c; /* 0x0c */ + u32 gcalout[4]; /* 0x10~0x1c */ + u32 reserved_20[8]; /* 0x20~0x3c */ + u32 gstart; /* 0x40 */ + u32 gcfg; /* 0x44 */ + u32 gstatus; /* 0x48 */ + u32 girq_en; /* 0x4c */ + u32 gcmd_cfg; /* 0x50 */ + u32 gcmd_arg; /* 0x54 */ + u32 gcmd_dat; /* 0x58 */ + u32 gcmd_rsp0; /* 0x5c */ + u32 gcmd_rsp1; /* 0x60 */ + u32 gcmd_rsp2; /* 0x64 */ + u32 gcmd_rsp3; /* 0x68 */ + u32 reserved_6c; /* 0x6c */ + u32 gcurr_cfg; /* 0x70 */ + u32 gcurr_arg; /* 0x74 */ + u32 gcurr_dat; /* 0x78 */ + u32 gcurr_rsp; /* 0x7c */ + u32 gnext_cfg; /* 0x80 */ + u32 gnext_arg; /* 0x84 */ + u32 gnext_dat; /* 0x88 */ + u32 gnext_rsp; /* 0x8c */ + u32 grxd; /* 0x90 */ + u32 gtxd; /* 0x94 */ + u32 reserved_98[90]; /* 0x98~0x1fc */ + u32 gdesc[128]; /* 0x200 */ + u32 gping[128]; /* 0x400 */ + u32 gpong[128]; /* 0x800 */ +}; +struct sd_emmc_clock { + /*[5:0] Clock divider. + *Frequency = clock source/cfg_div, Maximum divider 63. + */ + u32 div:6; + /*[7:6] Clock source, 0: Crystal 24MHz, 1: Fix PLL, 850MHz*/ + u32 src:2; + /*[9:8] Core clock phase. 0: 0 phase, + *1: 90 phase, 2: 180 phase, 3: 270 phase. + */ + u32 core_phase:2; + /*[11:10] TX clock phase. 0: 0 phase, + *1: 90 phase, 2: 180 phase, 3: 270 phase. + */ + u32 tx_phase:2; + /*[13:12] RX clock phase. 0: 0 phase, + *1: 90 phase, 2: 180 phase, 3: 270 phase. + */ + u32 rx_phase:2; + u32 reserved14:2; + /*[19:16] TX clock delay line. 0: no delay, + *n: delay n*200ps. Maximum delay 3ns. + */ + u32 tx_delay:4; + /*[23:20] RX clock delay line. 0: no delay, + *n: delay n*200ps. Maximum delay 3ns. + */ + u32 rx_delay:4; + /*[24] 1: Keep clock always on. + *0: Clock on/off controlled by activities. + */ + u32 always_on:1; + /*[25] 1: enable IRQ sdio when in sleep mode. */ + u32 irq_sdio_sleep:1; + /*[26] 1: select DS as IRQ source during sleep.. */ + u32 irq_sdio_sleep_ds:1; + u32 reserved27:5; +}; +struct sd_emmc_delay { + u32 dat0:4; /*[3:0] Data 0 delay line. */ + u32 dat1:4; /*[7:4] Data 1 delay line. */ + u32 dat2:4; /*[11:8] Data 2 delay line. */ + u32 dat3:4; /*[15:12] Data 3 delay line. */ + u32 dat4:4; /*[19:16] Data 4 delay line. */ + u32 dat5:4; /*[23:20] Data 5 delay line. */ + u32 dat6:4; /*[27:24] Data 6 delay line. */ + u32 dat7:4; /*[31:28] Data 7 delay line. */ +}; +struct sd_emmc_adjust { + /*[3:0] Command delay line. */ + u32 cmd_delay:4; + /*[7:4] DS delay line. */ + u32 ds_delay:4; + /*[11:8] Select one signal to be tested.*/ + u32 cali_sel:4; + /*[12] Enable calibration. */ + u32 cali_enable:1; + /*[13] Adjust interface timing + *by resampling the input signals. + */ + u32 adj_enable:1; + /*[14] 1: test the rising edge. + *0: test the falling edge. + */ + u32 cali_rise:1; + /*[15] 1: Sampling the DAT based on DS in HS400 mode. + *0: Sampling the DAT based on RXCLK. + */ + u32 ds_enable:1; + /*[21:16] Resample the input signals + *when clock index==adj_delay. + */ + u32 adj_delay:6; + /*[22] 1: Use cali_dut first falling edge to adjust + * the timing, set cali_enable to 1 to use this function. + *0: no use adj auto. + */ + u32 adj_auto:1; + u32 reserved22:9; +}; +struct sd_emmc_calout { + /*[5:0] Calibration reading. + *The event happens at this index. + */ + u32 cali_idx:6; + u32 reserved6:1; + /*[7] The reading is valid. */ + u32 cali_vld:1; + /*[15:8] Copied from BASE+0x8 + *[15:8] include cali_sel, cali_enable, adj_enable, cali_rise. + */ + u32 cali_setup:8; + u32 reserved16:16; +}; +struct sd_emmc_start { + /*[0] 1: Read descriptor from internal SRAM, + *limited to 32 descriptors. + */ + u32 init:1; + /*[1] 1: Start command chain execution process. 0: Stop */ + u32 busy:1; + /*[31:2] Descriptor address, the last 2 bits are 0, + *4 bytes aligned. + */ + u32 addr:30; +}; +struct sd_emmc_config { + /*[1:0] 0: 1 bit, 1: 4 bits, + *2: 8 bits, 3: 2 bits (not supported) + */ + u32 bus_width:2; + /*[2] 1: DDR mode, 0: SDR mode */ + u32 ddr:1; + /*[3] 1: DDR access urgent, 0: DDR access normal. */ + u32 dc_ugt:1; + /*[7:4] Block length 2^cfg_bl_len, + *because internal buffer size is limited to 512 bytes, + *the cfg_bl_len <=9. + */ + u32 bl_len:4; + /*[11:8] Wait response till 2^cfg_resp_timeout core clock cycles. + *Maximum 32768 core cycles. + */ + u32 resp_timeout:4; + /*[15:12] Wait response-command, + *command-command gap before next command, + *2^cfg_rc_cc core clock cycles. + */ + u32 rc_cc:4; + /*[16] DDR mode only. The command and TXD start from rising edge. + *Set 1 to start from falling edge. + */ + u32 out_fall:1; + /*[17] 1: Enable SDIO data block gap interrupt period. + *0: Disabled. + */ + u32 blk_gap_ip:1; + /*[18] Spare, ??? need check*/ + u32 spare:1; + /*[19] Use this descriptor + *even if its owner bit is ???0???бь?ии. + */ + u32 ignore_owner:1; + /*[20] Check data strobe in HS400.*/ + u32 chk_ds:1; + /*[21] Hold CMD as output Low, eMMC boot mode.*/ + u32 cmd_low:1; + /*[22] 1: stop clock. 0: normal clock.*/ + u32 stop_clk:1; + /*[23] 1: when BUS is idle and no descriptor is available, + *turn off clock, to save power. + */ + u32 auto_clk:1; + /*[24] TXD add error test*/ + u32 txd_add_err:1; + /*[25] When TXD CRC error, host sends the block again.*/ + u32 txd_retry:1; + /*[26] 1: Use DS pin as SDIO IRQ input, + *0: Use DAT1 pin as SDIO IRQ input.. + */ + u32 irq_ds:1; + u32 err_abort:1; + u32 revd:4; /*[31:27] reved*/ +}; +struct sd_emmc_status { + /*[7:0] RX data CRC error per wire, for multiple block read, + *the CRC errors are ORed together. + */ + u32 rxd_err:8; + /*[8] TX data CRC error, for multiple block write, + *any one of blocks CRC error. + */ + u32 txd_err:1; + /*[9] SD/eMMC controller doesn???и║?ииt own descriptor. + *The owner bit is set cfg_ignore_owner to ignore this error. + */ + u32 desc_err:1; + /*[10] Response CRC error.*/ + u32 resp_err:1; + /*[11] No response received before time limit. + *The timeout limit is set by cfg_resp_timeout. + */ + u32 resp_timeout:1; + /*[12] Descriptor execution time over time limit. + *The timeout limit is set by descriptor itself. + */ + u32 desc_timeout:1; + /*[13] End of Chain IRQ, Normal IRQ. */ + u32 end_of_chain:1; + /*[14] This descriptor requests an IRQ, Normal IRQ, + *the descriptor chain execution keeps going on. + */ + u32 desc_irq:1; + /*[15] SDIO device uses DAT[1] to request IRQ. */ + u32 irq_sdio:1; + /*[23:16] Input data signals. */ + u32 dat_i:8; + /*[24] nput response signal. */ + u32 cmd_i:1; + /*[25] Input data strobe. */ + u32 ds:1; + /*[29:26] BUS fsm */ + u32 bus_fsm:4; + /*[30] Descriptor write back process is done + *and it is ready for CPU to read. + */ + u32 desc_wr_rdy:1; + /*[31] Core is busy,desc_busy or sd_emmc_irq + * or bus_fsm is not idle. + */ + u32 core_wr_rdy:1; +}; +struct sd_emmc_irq_en { + /*[7:0] RX data CRC error per wire.*/ + u32 rxd_err:8; + /*[8] TX data CRC error. */ + u32 txd_err:1; + /*[9] SD/eMMC controller doesn???и║?ииt own descriptor. */ + u32 desc_err:1; + /*[10] Response CRC error.*/ + u32 resp_err:1; + /*[11] No response received before time limit. */ + u32 resp_timeout:1; + /*[12] Descriptor execution time over time limit. */ + u32 desc_timeout:1; + /*[13] End of Chain IRQ. */ + u32 end_of_chain:1; + /*[14] This descriptor requests an IRQ. */ + u32 desc_irq:1; + /*[15] Enable sdio interrupt. */ + u32 irq_sdio:1; + /*[31:16] reved*/ + u32 revd:16; +}; +struct sd_emmc_data_info { + /*[9:0] Rxd words received from BUS. Txd words received from DDR.*/ + u32 cnt:10; + /*[24:16] Rxd Blocks received from BUS. + *Txd blocks received from DDR. + */ + u32 blk:9; + /*[31:17] Reved. */ + u32 revd:30; +}; +struct sd_emmc_card_info { + /*[9:0] Txd BUS cycle counter. */ + u32 txd_cnt:10; + /*[24:16] Txd BUS block counter.*/ + u32 txd_blk:9; + /*[31:17] Reved. */ + u32 revd:30; +}; +struct cmd_cfg { + u32 length:9; + u32 block_mode:1; + u32 r1b:1; + u32 end_of_chain:1; + u32 timeout:4; + u32 no_resp:1; + u32 no_cmd:1; + u32 data_io:1; + u32 data_wr:1; + u32 resp_nocrc:1; + u32 resp_128:1; + u32 resp_num:1; + u32 data_num:1; + u32 cmd_index:6; + u32 error:1; + u32 owner:1; +}; +struct sd_emmc_desc_info { + u32 cmd_info; + u32 cmd_arg; + u32 data_addr; + u32 resp_addr; +}; +#define SD_EMMC_MAX_DESC_MUN 512 +#define SD_EMMC_REQ_DESC_MUN 4 +#define SD_EMMC_CLOCK_SRC_OSC 0 /* 24MHz */ +#define SD_EMMC_CLOCK_SRC_FCLK_DIV2 1 /* 1GHz */ +#define SD_EMMC_CLOCK_SRC_MPLL 2 /* MPLL */ +#define SD_EMMC_CLOCK_SRC_DIFF_PLL 3 +#define SD_EMMC_IRQ_ALL 0x3fff +#define SD_EMMC_RESP_SRAM_OFF 0 +/*#define SD_EMMC_DESC_SET_REG*/ + +#define SD_EMMC_DESC_REG_CONF 0x4 +#define SD_EMMC_DESC_REG_IRQC 0xC +#define SD_EMMC_DESC_RESP_STAT 0xfff80000 +#define SD_EMMC_IRQ_EN_ALL_INIT +#define SD_EMMC_REQ_DMA_SGMAP +/* #define SD_EMMC_CLK_CTRL*/ +/* #define SD_EMMC_DATA_TASKLET */ +#define STAT_POLL_TIMEOUT 0xfffff +#define STAT_POLL_TIMEOUT 0xfffff + +#define MMC_RSP_136_NUM 4 +#define MMC_MAX_DEVICE 3 +#define MMC_TIMEOUT 5000 + +/* #define pr_info(a...) */ +#define DBG_LINE_INFO() \ +{ \ + pr_info("[%s] : %s\n", __func__, __FILE__); \ +} +/* #define DBG_LINE_INFO() */ +/* #define dev_err(a,s) pr_info(KERN_INFO s); */ +#define BOOT_POLL_UP_DOWN (0x3C << 2) +#define BOOT_POLL_UP_DOWN_EN (0x4A << 2) + +#define AML_MMC_DISABLED_TIMEOUT 100 +#define AML_MMC_SLEEP_TIMEOUT 1000 +#define AML_MMC_OFF_TIMEOUT 8000 + +#define SD_EMMC_BOUNCE_REQ_SIZE (512*1024) +#define SDHC_BOUNCE_REQ_SIZE (512*1024) +#define SDIO_BOUNCE_REQ_SIZE (128*1024) +#define MMC_TIMEOUT_MS 20 + +#define MESON_SDIO_PORT_A 0 +#define MESON_SDIO_PORT_B 1 +#define MESON_SDIO_PORT_C 2 +#define MESON_SDIO_PORT_XC_A 3 +#define MESON_SDIO_PORT_XC_B 4 +#define MESON_SDIO_PORT_XC_C 5 + +void aml_sdhc_request(struct mmc_host *mmc, struct mmc_request *mrq); +int aml_sdhc_get_cd(struct mmc_host *mmc); +extern void amlsd_init_debugfs(struct mmc_host *host); + +extern struct mmc_host *sdio_host; + +#define SPI_BOOT_FLAG 0 +#define NAND_BOOT_FLAG 1 +#define EMMC_BOOT_FLAG 2 +#define CARD_BOOT_FLAG 3 +#define SPI_NAND_FLAG 4 +#define SPI_EMMC_FLAG 5 + +#define R_BOOT_DEVICE_FLAG (aml_read_cbus(ASSIST_POR_CONFIG)) + + +#define POR_BOOT_VALUE ((((R_BOOT_DEVICE_FLAG>>9)&1)<<2)|\ + ((R_BOOT_DEVICE_FLAG>>6)&3)) /* {poc[9],poc[7:6]} */ + +#define POR_NAND_BOOT() ((POR_BOOT_VALUE == 7) \ + || (POR_BOOT_VALUE == 6)) +#define POR_SPI_BOOT() ((POR_BOOT_VALUE == 5) || (POR_BOOT_VALUE == 4)) +/* #define POR_EMMC_BOOT() (POR_BOOT_VALUE == 3) */ +#define POR_EMMC_BOOT() (POR_BOOT_VALUE == 3) + +#define POR_CARD_BOOT() (POR_BOOT_VALUE == 0) + +#define print_tmp(fmt, args...) \ +{ \ + pr_info("[%s] " fmt, __func__, ##args); \ +} + +#define print_dbg(fmt, args...) \ +{ \ + pr_info("[%s] " fmt, __func__, ##args); \ +} + +/* for external codec status, if using external codec, + * jtag should not be set. + */ +extern int ext_codec; + +#ifndef CONFIG_MESON_TRUSTZONE +/* P_AO_SECURE_REG1 is "Secure Register 1" in */ +#define aml_jtag_gpioao() +/*do{\ + * aml_clr_reg32_mask(P_AO_SECURE_REG1, ((1<<5) | (1<<9))); \ + * if(!ext_codec)\ + * aml_set_reg32_mask(P_AO_SECURE_REG1, ((1<<8) | (1<<1))); \ + *}while(0) + */ + +#define aml_jtag_sd() +/*do{\ + * aml_clr_reg32_mask(P_AO_SECURE_REG1, ((1<<8) | (1<<1))); \ + * aml_set_reg32_mask(P_AO_SECURE_REG1, ((1<<5) | (1<<9))); \ + *}while(0) + */ +#else +/* Secure REG can only be accessed in Secure World if TrustZone enabled.*/ +#include +#define aml_jtag_gpioao() \ +{ \ + meson_secure_reg_write(P_AO_SECURE_REG1, \ + meson_secure_reg_read(P_AO_SECURE_REG1) \ + & (~((1<<5) | (1<<9)))); \ +} + +#define aml_jtag_sd() do {\ + meson_secure_reg_write(P_AO_SECURE_REG1,\ + meson_secure_reg_read(P_AO_SECURE_REG1)\ + & (~(1<<8) | (1<<1))); \ + meson_secure_reg_write(P_AO_SECURE_REG1,\ + meson_secure_reg_read(P_AO_SECURE_REG1)\ + | ((1<<5) | (1<<9))); \ +} while (0) +#endif /* CONFIG_MESON_TRUSTZONE */ + +#define aml_uart_pinctrl() do {\ + \ +} while (0) + +#endif + diff --git a/include/linux/mmc/emmc_partitions.h b/include/linux/mmc/emmc_partitions.h new file mode 100644 index 000000000000..712c9f66eeb5 --- /dev/null +++ b/include/linux/mmc/emmc_partitions.h @@ -0,0 +1,78 @@ +#ifndef _EMMC_PARTITIONS_H +#define _EMMC_PARTITIONS_H + +#include + +#include +#include +#include +#include + +/* #include */ +/* #include */ +#define CONFIG_DTB_SIZE (256*1024U) +#define STORE_CODE 1 +#define STORE_CACHE (1<<1) +#define STORE_DATA (1<<2) + +#define MAX_PART_NAME_LEN 16 +#define MAX_MMC_PART_NUM 32 + +/* MMC Partition Table */ +#define MMC_PARTITIONS_MAGIC "MPT" +#define MMC_RESERVED_NAME "reserved" + +#define SZ_1M 0x00100000 + +/* the size of bootloader partition */ +#define MMC_BOOT_PARTITION_SIZE (4*SZ_1M) + +/* the size of reserve space behind bootloader partition */ +#define MMC_BOOT_PARTITION_RESERVED (32*SZ_1M) + +#define RESULT_OK 0 +#define RESULT_FAIL 1 +#define RESULT_UNSUP_HOST 2 +#define RESULT_UNSUP_CARD 3 + +struct partitions { + /* identifier string */ + char name[MAX_PART_NAME_LEN]; + /* partition size, byte unit */ + uint64_t size; + /* offset within the master space, byte unit */ + uint64_t offset; + /* master flags to mask out for this partition */ + unsigned mask_flags; +}; + +struct mmc_partitions_fmt { + char magic[4]; + unsigned char version[12]; + int part_num; + int checksum; + struct partitions partitions[MAX_MMC_PART_NUM]; +}; +/*#ifdef CONFIG_MMC_AML*/ +int aml_emmc_partition_ops(struct mmc_card *card, struct gendisk *disk); +/* + *#else + *static inline int aml_emmc_partition_ops(struct mmc_card *card, + * struct gendisk *disk) + *{ + * return -1; + *} + *#endif + */ +unsigned int mmc_capacity(struct mmc_card *card); +int mmc_read_internal(struct mmc_card *card, + unsigned dev_addr, unsigned blocks, void *buf); +int mmc_write_internal(struct mmc_card *card, + unsigned dev_addr, unsigned blocks, void *buf); +int get_reserve_partition_off_from_tbl(void); +int get_reserve_partition_off(struct mmc_card *card);/* byte unit */ + +#endif + +extern struct mmc_partitions_fmt *pt_fmt; + diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 0b2439441cc8..aa7276b7d90e 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -225,6 +225,9 @@ struct mmc_host { unsigned int f_min; unsigned int f_max; unsigned int f_init; +#ifdef CONFIG_AMLOGIC_MMC + u8 first_init_flag; +#endif u32 ocr_avail; u32 ocr_avail_sdio; /* SDIO-specific OCR */ u32 ocr_avail_sd; /* SD-specific OCR */ @@ -287,8 +290,11 @@ struct mmc_host { u32 caps2; /* More host capabilities */ -#define MMC_CAP2_BOOTPART_NOACC (1 << 0) /* Boot partition no access */ -#define MMC_CAP2_FULL_PWR_CYCLE (1 << 2) /* Can do full power cycle */ +#define MMC_CAP2_BOOTPART_NOACC (1 << 0) /* Boot partition no access */ +#define MMC_CAP2_FULL_PWR_CYCLE (1 << 2) /* Can do full power cycle */ +#ifdef CONFIG_AMLOGIC_MMC +#define MMC_CAP2_NO_MULTI_READ (1 << 3) /* Multiblock reads don't work */ +#endif #define MMC_CAP2_HS200_1_8V_SDR (1 << 5) /* can support */ #define MMC_CAP2_HS200_1_2V_SDR (1 << 6) /* can support */ #define MMC_CAP2_HS200 (MMC_CAP2_HS200_1_8V_SDR | \