// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2019 Marvell International Ltd. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(CONFIG_ARCH_OCTEON) #include #include #include #else #include #include #include #endif #include "octeontx_hsmmc.h" /* Use dummy implementation for MIPS Octeon to always return false */ #if defined(CONFIG_ARCH_OCTEON) #define otx_is_soc(ver) 0 #endif #define MMC_TIMEOUT_SHORT 20 /* in ms */ #define MMC_TIMEOUT_LONG 1000 #define MMC_TIMEOUT_ERASE 10000 #define MMC_DEFAULT_DATA_IN_TAP 10 #define MMC_DEFAULT_CMD_IN_TAP 10 #define MMC_DEFAULT_CMD_OUT_TAP 39 #define MMC_DEFAULT_DATA_OUT_TAP 39 #define MMC_DEFAULT_HS200_CMD_IN_TAP 24 #define MMC_DEFAULT_HS200_DATA_IN_TAP 24 #define MMC_DEFAULT_HS200_CMD_OUT_TAP (otx_is_soc(CN95XX) ? 10 : 5) #define MMC_DEFAULT_HS200_DATA_OUT_TAP (otx_is_soc(CN95XX) ? 10 : 5) #define MMC_DEFAULT_HS400_CMD_OUT_TAP (otx_is_soc(CN95XX) ? 10 : 5) #define MMC_DEFAULT_HS400_DATA_OUT_TAP (otx_is_soc(CN95XX) ? 5 : 3) #define MMC_DEFAULT_HS200_CMD_OUT_DLY 800 /* Delay in ps */ #define MMC_DEFAULT_HS200_DATA_OUT_DLY 800 /* Delay in ps */ #define MMC_DEFAULT_HS400_CMD_OUT_DLY 800 /* Delay in ps */ #define MMC_DEFAULT_HS400_DATA_OUT_DLY 400 /* Delay in ps */ #define MMC_DEFAULT_SD_UHS_SDR104_CMD_OUT_TAP MMC_DEFAULT_HS200_CMD_OUT_TAP #define MMC_DEFAULT_SD_UHS_SDR104_DATA_OUT_TAP MMC_DEFAULT_HS200_DATA_OUT_TAP #define MMC_LEGACY_DEFAULT_CMD_OUT_TAP 39 #define MMC_LEGACY_DEFAULT_DATA_OUT_TAP 39 #define MMC_SD_LEGACY_DEFAULT_CMD_OUT_TAP 63 #define MMC_SD_LEGACY_DEFAULT_DATA_OUT_TAP 63 #define MMC_HS_CMD_OUT_TAP 32 #define MMC_HS_DATA_OUT_TAP 32 #define MMC_SD_HS_CMD_OUT_TAP 26 #define MMC_SD_HS_DATA_OUT_TAP 26 #define MMC_SD_UHS_SDR25_CMD_OUT_TAP 26 #define MMC_SD_UHS_SDR25_DATA_OUT_TAP 26 #define MMC_SD_UHS_SDR50_CMD_OUT_TAP 26 #define MMC_SD_UHS_SDR50_DATA_OUT_TAP 26 #define MMC_DEFAULT_TAP_DELAY 4 #define TOTAL_NO_OF_TAPS 512 static void octeontx_mmc_switch_to(struct mmc *mmc); static void set_wdog(struct mmc *mmc, u64 us); static void do_switch(struct mmc *mmc, union mio_emm_switch emm_switch); static int octeontx_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data); static int octeontx_mmc_configure_delay(struct mmc *mmc); static int octeontx_mmc_calibrate_delay(struct mmc *mmc); #if !defined(CONFIG_ARCH_OCTEON) static int octeontx2_mmc_calc_delay(struct mmc *mmc, int delay); static void octeontx_mmc_set_timing(struct mmc *mmc); static int octeontx_mmc_set_input_bus_timing(struct mmc *mmc); static int octeontx_mmc_set_output_bus_timing(struct mmc *mmc); #endif static bool host_probed; /** * Get the slot data structure from a MMC data structure */ static inline struct octeontx_mmc_slot *mmc_to_slot(struct mmc *mmc) { return container_of(mmc, struct octeontx_mmc_slot, mmc); } static inline struct octeontx_mmc_host *mmc_to_host(struct mmc *mmc) { return mmc_to_slot(mmc)->host; } static inline struct octeontx_mmc_slot *dev_to_mmc_slot(struct udevice *dev) { return dev_get_priv(dev); } static inline struct mmc *dev_to_mmc(struct udevice *dev) { return &((struct octeontx_mmc_slot *)dev_get_priv(dev))->mmc; } #ifdef DEBUG const char *mmc_reg_str(u64 reg) { if (reg == MIO_EMM_DMA_CFG()) return "MIO_EMM_DMA_CFG"; if (reg == MIO_EMM_DMA_ADR()) return "MIO_EMM_DMA_ADR"; if (reg == MIO_EMM_DMA_INT()) return "MIO_EMM_DMA_INT"; if (reg == MIO_EMM_CFG()) return "MIO_EMM_CFG"; if (reg == MIO_EMM_MODEX(0)) return "MIO_EMM_MODE0"; if (reg == MIO_EMM_MODEX(1)) return "MIO_EMM_MODE1"; if (reg == MIO_EMM_MODEX(2)) return "MIO_EMM_MODE2"; if (reg == MIO_EMM_MODEX(3)) return "MIO_EMM_MODE3"; if (reg == MIO_EMM_IO_CTL()) return "MIO_EMM_IO_CTL"; if (reg == MIO_EMM_SWITCH()) return "MIO_EMM_SWITCH"; if (reg == MIO_EMM_DMA()) return "MIO_EMM_DMA"; if (reg == MIO_EMM_CMD()) return "MIO_EMM_CMD"; if (reg == MIO_EMM_RSP_STS()) return "MIO_EMM_RSP_STS"; if (reg == MIO_EMM_RSP_LO()) return "MIO_EMM_RSP_LO"; if (reg == MIO_EMM_RSP_HI()) return "MIO_EMM_RSP_HI"; if (reg == MIO_EMM_INT()) return "MIO_EMM_INT"; if (reg == MIO_EMM_WDOG()) return "MIO_EMM_WDOG"; if (reg == MIO_EMM_DMA_ARG()) return "MIO_EMM_DMA_ARG"; if (IS_ENABLED(CONFIG_ARCH_OCTEONTX)) { if (reg == MIO_EMM_SAMPLE()) return "MIO_EMM_SAMPLE"; } if (reg == MIO_EMM_STS_MASK()) return "MIO_EMM_STS_MASK"; if (reg == MIO_EMM_RCA()) return "MIO_EMM_RCA"; if (reg == MIO_EMM_BUF_IDX()) return "MIO_EMM_BUF_IDX"; if (reg == MIO_EMM_BUF_DAT()) return "MIO_EMM_BUF_DAT"; if (!IS_ENABLED(CONFIG_ARCH_OCTEONTX)) { if (reg == MIO_EMM_CALB()) return "MIO_EMM_CALB"; if (reg == MIO_EMM_TAP()) return "MIO_EMM_TAP"; if (reg == MIO_EMM_TIMING()) return "MIO_EMM_TIMING"; if (reg == MIO_EMM_DEBUG()) return "MIO_EMM_DEBUG"; } return "UNKNOWN"; } #endif static void octeontx_print_rsp_sts(struct mmc *mmc) { #ifdef DEBUG union mio_emm_rsp_sts emm_rsp_sts; const struct octeontx_mmc_host *host = mmc_to_host(mmc); static const char * const ctype_xor_str[] = { "No data", "Read data into Dbuf", "Write data from Dbuf", "Reserved" }; static const char * const rtype_xor_str[] = { "No response", "R1, 48 bits", "R2, 136 bits", "R3, 48 bits", "R4, 48 bits", "R5, 48 bits", "Reserved 6", "Reserved 7" }; emm_rsp_sts.u = readq(host->base_addr + MIO_EMM_RSP_STS()); printf("\nMIO_EMM_RSP_STS: 0x%016llx\n", emm_rsp_sts.u); printf(" 60-61: bus_id: %u\n", emm_rsp_sts.s.bus_id); printf(" 59: cmd_val: %s\n", emm_rsp_sts.s.cmd_val ? "yes" : "no"); printf(" 58: switch_val: %s\n", emm_rsp_sts.s.switch_val ? "yes" : "no"); printf(" 57: dma_val: %s\n", emm_rsp_sts.s.dma_val ? "yes" : "no"); printf(" 56: dma_pend: %s\n", emm_rsp_sts.s.dma_pend ? "yes" : "no"); printf(" 28: dbuf_err: %s\n", emm_rsp_sts.s.dbuf_err ? "yes" : "no"); printf(" 23: dbuf: %u\n", emm_rsp_sts.s.dbuf); printf(" 22: blk_timeout: %s\n", emm_rsp_sts.s.blk_timeout ? "yes" : "no"); printf(" 21: blk_crc_err: %s\n", emm_rsp_sts.s.blk_crc_err ? "yes" : "no"); printf(" 20: rsp_busybit: %s\n", emm_rsp_sts.s.rsp_busybit ? "yes" : "no"); printf(" 19: stp_timeout: %s\n", emm_rsp_sts.s.stp_timeout ? "yes" : "no"); printf(" 18: stp_crc_err: %s\n", emm_rsp_sts.s.stp_crc_err ? "yes" : "no"); printf(" 17: stp_bad_sts: %s\n", emm_rsp_sts.s.stp_bad_sts ? "yes" : "no"); printf(" 16: stp_val: %s\n", emm_rsp_sts.s.stp_val ? "yes" : "no"); printf(" 15: rsp_timeout: %s\n", emm_rsp_sts.s.rsp_timeout ? "yes" : "no"); printf(" 14: rsp_crc_err: %s\n", emm_rsp_sts.s.rsp_crc_err ? "yes" : "no"); printf(" 13: rsp_bad_sts: %s\n", emm_rsp_sts.s.rsp_bad_sts ? "yes" : "no"); printf(" 12: rsp_val: %s\n", emm_rsp_sts.s.rsp_val ? "yes" : "no"); printf(" 9-11: rsp_type: %s\n", rtype_xor_str[emm_rsp_sts.s.rsp_type]); printf(" 7-8: cmd_type: %s\n", ctype_xor_str[emm_rsp_sts.s.cmd_type]); printf(" 1-6: cmd_idx: %u\n", emm_rsp_sts.s.cmd_idx); printf(" 0: cmd_done: %s\n", emm_rsp_sts.s.cmd_done ? "yes" : "no"); #endif } static inline u64 read_csr(struct mmc *mmc, u64 reg) { const struct octeontx_mmc_host *host = mmc_to_host(mmc); u64 value = readq(host->base_addr + reg); #ifdef DEBUG_CSR printf(" %s: %s(0x%p) => 0x%llx\n", __func__, mmc_reg_str(reg), host->base_addr + reg, value); #endif return value; } /** * Writes to a CSR register * * @param[in] mmc pointer to mmc data structure * @param reg register offset * @param value value to write to register */ static inline void write_csr(struct mmc *mmc, u64 reg, u64 value) { const struct octeontx_mmc_host *host = mmc_to_host(mmc); void *addr = host->base_addr + reg; #ifdef DEBUG_CSR printf(" %s: %s(0x%p) <= 0x%llx\n", __func__, mmc_reg_str(reg), addr, value); #endif writeq(value, addr); } #ifdef DEBUG static void mmc_print_status(u32 status) { #ifdef DEBUG_STATUS static const char * const state[] = { "Idle", /* 0 */ "Ready", /* 1 */ "Ident", /* 2 */ "Standby", /* 3 */ "Tran", /* 4 */ "Data", /* 5 */ "Receive", /* 6 */ "Program", /* 7 */ "Dis", /* 8 */ "Btst", /* 9 */ "Sleep", /* 10 */ "reserved", /* 11 */ "reserved", /* 12 */ "reserved", /* 13 */ "reserved", /* 14 */ "reserved" /* 15 */ }; if (status & R1_APP_CMD) puts("MMC ACMD\n"); if (status & R1_SWITCH_ERROR) puts("MMC switch error\n"); if (status & R1_READY_FOR_DATA) puts("MMC ready for data\n"); printf("MMC %s state\n", state[R1_CURRENT_STATE(status)]); if (status & R1_ERASE_RESET) puts("MMC erase reset\n"); if (status & R1_WP_ERASE_SKIP) puts("MMC partial erase due to write protected blocks\n"); if (status & R1_CID_CSD_OVERWRITE) puts("MMC CID/CSD overwrite error\n"); if (status & R1_ERROR) puts("MMC undefined device error\n"); if (status & R1_CC_ERROR) puts("MMC device error\n"); if (status & R1_CARD_ECC_FAILED) puts("MMC internal ECC failed to correct data\n"); if (status & R1_ILLEGAL_COMMAND) puts("MMC illegal command\n"); if (status & R1_COM_CRC_ERROR) puts("MMC CRC of previous command failed\n"); if (status & R1_LOCK_UNLOCK_FAILED) puts("MMC sequence or password error in lock/unlock device command\n"); if (status & R1_CARD_IS_LOCKED) puts("MMC device locked by host\n"); if (status & R1_WP_VIOLATION) puts("MMC attempt to program write protected block\n"); if (status & R1_ERASE_PARAM) puts("MMC invalid selection of erase groups for erase\n"); if (status & R1_ERASE_SEQ_ERROR) puts("MMC error in sequence of erase commands\n"); if (status & R1_BLOCK_LEN_ERROR) puts("MMC block length error\n"); if (status & R1_ADDRESS_ERROR) puts("MMC address misalign error\n"); if (status & R1_OUT_OF_RANGE) puts("MMC address out of range\n"); #endif } #endif #if !defined(CONFIG_ARCH_OCTEON) /** * Print out all of the register values where mmc is optional * * @param mmc MMC device (can be NULL) * @param host Pointer to host data structure (can be NULL if mmc is !NULL) */ static void octeontx_mmc_print_registers2(struct mmc *mmc, struct octeontx_mmc_host *host) { struct octeontx_mmc_slot *slot = mmc ? mmc->priv : NULL; union mio_emm_dma_cfg emm_dma_cfg; union mio_emm_dma_adr emm_dma_adr; union mio_emm_dma_int emm_dma_int; union mio_emm_cfg emm_cfg; union mio_emm_modex emm_mode; union mio_emm_switch emm_switch; union mio_emm_dma emm_dma; union mio_emm_cmd emm_cmd; union mio_emm_rsp_sts emm_rsp_sts; union mio_emm_rsp_lo emm_rsp_lo; union mio_emm_rsp_hi emm_rsp_hi; union mio_emm_int emm_int; union mio_emm_wdog emm_wdog; union mio_emm_sample emm_sample; union mio_emm_calb emm_calb; union mio_emm_tap emm_tap; union mio_emm_timing emm_timing; union mio_emm_io_ctl io_ctl; union mio_emm_debug emm_debug; union mio_emm_sts_mask emm_sts_mask; union mio_emm_rca emm_rca; int bus; static const char * const bus_width_str[] = { "1-bit data bus (power on)", "4-bit data bus", "8-bit data bus", "reserved (3)", "reserved (4)", "4-bit data bus (dual data rate)", "8-bit data bus (dual data rate)", "reserved (7)", "reserved (8)", "invalid (9)", "invalid (10)", "invalid (11)", "invalid (12)", "invalid (13)", "invalid (14)", "invalid (15)", }; static const char * const ctype_xor_str[] = { "No data", "Read data into Dbuf", "Write data from Dbuf", "Reserved" }; static const char * const rtype_xor_str[] = { "No response", "R1, 48 bits", "R2, 136 bits", "R3, 48 bits", "R4, 48 bits", "R5, 48 bits", "Reserved 6", "Reserved 7" }; if (!host && mmc) host = mmc_to_host(mmc); if (mmc) printf("%s: bus id: %u\n", __func__, slot->bus_id); emm_dma_cfg.u = readq(host->base_addr + MIO_EMM_DMA_CFG()); printf("MIO_EMM_DMA_CFG: 0x%016llx\n", emm_dma_cfg.u); printf(" 63: en: %s\n", emm_dma_cfg.s.en ? "enabled" : "disabled"); printf(" 62: rw: %s\n", emm_dma_cfg.s.rw ? "write" : "read"); printf(" 61: clr: %s\n", emm_dma_cfg.s.clr ? "clear" : "not clear"); printf(" 59: swap32: %s\n", emm_dma_cfg.s.swap32 ? "yes" : "no"); printf(" 58: swap16: %s\n", emm_dma_cfg.s.swap16 ? "yes" : "no"); printf(" 57: swap8: %s\n", emm_dma_cfg.s.swap8 ? "yes" : "no"); printf(" 56: endian: %s\n", emm_dma_cfg.s.endian ? "little" : "big"); printf(" 36-55: size: %u\n", emm_dma_cfg.s.size); emm_dma_adr.u = readq(host->base_addr + MIO_EMM_DMA_ADR()); printf("MIO_EMM_DMA_ADR: 0x%016llx\n", emm_dma_adr.u); printf(" 0-49: adr: 0x%llx\n", (u64)emm_dma_adr.s.adr); emm_dma_int.u = readq(host->base_addr + MIO_EMM_DMA_INT()); printf("\nMIO_EMM_DMA_INT: 0x%016llx\n", emm_dma_int.u); printf(" 1: FIFO: %s\n", emm_dma_int.s.fifo ? "yes" : "no"); printf(" 0: Done: %s\n", emm_dma_int.s.done ? "yes" : "no"); emm_cfg.u = readq(host->base_addr + MIO_EMM_CFG()); printf("\nMIO_EMM_CFG: 0x%016llx\n", emm_cfg.u); printf(" 3: bus_ena3: %s\n", emm_cfg.s.bus_ena & 0x08 ? "yes" : "no"); printf(" 2: bus_ena2: %s\n", emm_cfg.s.bus_ena & 0x04 ? "yes" : "no"); printf(" 1: bus_ena1: %s\n", emm_cfg.s.bus_ena & 0x02 ? "yes" : "no"); printf(" 0: bus_ena0: %s\n", emm_cfg.s.bus_ena & 0x01 ? "yes" : "no"); for (bus = 0; bus < 4; bus++) { emm_mode.u = readq(host->base_addr + MIO_EMM_MODEX(bus)); printf("\nMIO_EMM_MODE%u: 0x%016llx\n", bus, emm_mode.u); if (!IS_ENABLED(CONFIG_ARCH_OCTEONTX)) { printf(" 50: hs400_timing: %s\n", emm_mode.s.hs400_timing ? "yes" : "no"); printf(" 49: hs200_timing: %s\n", emm_mode.s.hs200_timing ? "yes" : "no"); } printf(" 48: hs_timing: %s\n", emm_mode.s.hs_timing ? "yes" : "no"); printf(" 40-42: bus_width: %s\n", bus_width_str[emm_mode.s.bus_width]); printf(" 32-35: power_class %u\n", emm_mode.s.power_class); printf(" 16-31: clk_hi: %u\n", emm_mode.s.clk_hi); printf(" 0-15: clk_lo: %u\n", emm_mode.s.clk_lo); } emm_switch.u = readq(host->base_addr + MIO_EMM_SWITCH()); printf("\nMIO_EMM_SWITCH: 0x%016llx\n", emm_switch.u); printf(" 60-61: bus_id: %u\n", emm_switch.s.bus_id); printf(" 59: switch_exe: %s\n", emm_switch.s.switch_exe ? "yes" : "no"); printf(" 58: switch_err0: %s\n", emm_switch.s.switch_err0 ? "yes" : "no"); printf(" 57: switch_err1: %s\n", emm_switch.s.switch_err1 ? "yes" : "no"); printf(" 56: switch_err2: %s\n", emm_switch.s.switch_err2 ? "yes" : "no"); printf(" 48: hs_timing: %s\n", emm_switch.s.hs_timing ? "yes" : "no"); printf(" 42-40: bus_width: %s\n", bus_width_str[emm_switch.s.bus_width]); printf(" 32-35: power_class: %u\n", emm_switch.s.power_class); printf(" 16-31: clk_hi: %u\n", emm_switch.s.clk_hi); printf(" 0-15: clk_lo: %u\n", emm_switch.s.clk_lo); emm_dma.u = readq(host->base_addr + MIO_EMM_DMA()); printf("\nMIO_EMM_DMA: 0x%016llx\n", emm_dma.u); printf(" 60-61: bus_id: %u\n", emm_dma.s.bus_id); printf(" 59: dma_val: %s\n", emm_dma.s.dma_val ? "yes" : "no"); printf(" 58: sector: %s mode\n", emm_dma.s.sector ? "sector" : "byte"); printf(" 57: dat_null: %s\n", emm_dma.s.dat_null ? "yes" : "no"); printf(" 51-56: thres: %u\n", emm_dma.s.thres); printf(" 50: rel_wr: %s\n", emm_dma.s.rel_wr ? "yes" : "no"); printf(" 49: rw: %s\n", emm_dma.s.rw ? "write" : "read"); printf(" 48: multi: %s\n", emm_dma.s.multi ? "yes" : "no"); printf(" 32-47: block_cnt: %u\n", emm_dma.s.block_cnt); printf(" 0-31: card_addr: 0x%x\n", emm_dma.s.card_addr); emm_cmd.u = readq(host->base_addr + MIO_EMM_CMD()); printf("\nMIO_EMM_CMD: 0x%016llx\n", emm_cmd.u); printf("\n 62: skip_busy: %s\n", emm_cmd.s.skip_busy ? "yes" : "no"); printf(" 60-61: bus_id: %u\n", emm_cmd.s.bus_id); printf(" 59: cmd_val: %s\n", emm_cmd.s.cmd_val ? "yes" : "no"); printf(" 55: dbuf: %u\n", emm_cmd.s.dbuf); printf(" 49-54: offset: %u\n", emm_cmd.s.offset); printf(" 41-42: ctype_xor: %s\n", ctype_xor_str[emm_cmd.s.ctype_xor]); printf(" 38-40: rtype_xor: %s\n", rtype_xor_str[emm_cmd.s.rtype_xor]); printf(" 32-37: cmd_idx: %u\n", emm_cmd.s.cmd_idx); printf(" 0-31: arg: 0x%x\n", emm_cmd.s.arg); emm_rsp_sts.u = readq(host->base_addr + MIO_EMM_RSP_STS()); printf("\nMIO_EMM_RSP_STS: 0x%016llx\n", emm_rsp_sts.u); printf(" 60-61: bus_id: %u\n", emm_rsp_sts.s.bus_id); printf(" 59: cmd_val: %s\n", emm_rsp_sts.s.cmd_val ? "yes" : "no"); printf(" 58: switch_val: %s\n", emm_rsp_sts.s.switch_val ? "yes" : "no"); printf(" 57: dma_val: %s\n", emm_rsp_sts.s.dma_val ? "yes" : "no"); printf(" 56: dma_pend: %s\n", emm_rsp_sts.s.dma_pend ? "yes" : "no"); printf(" 28: dbuf_err: %s\n", emm_rsp_sts.s.dbuf_err ? "yes" : "no"); printf(" 23: dbuf: %u\n", emm_rsp_sts.s.dbuf); printf(" 22: blk_timeout: %s\n", emm_rsp_sts.s.blk_timeout ? "yes" : "no"); printf(" 21: blk_crc_err: %s\n", emm_rsp_sts.s.blk_crc_err ? "yes" : "no"); printf(" 20: rsp_busybit: %s\n", emm_rsp_sts.s.rsp_busybit ? "yes" : "no"); printf(" 19: stp_timeout: %s\n", emm_rsp_sts.s.stp_timeout ? "yes" : "no"); printf(" 18: stp_crc_err: %s\n", emm_rsp_sts.s.stp_crc_err ? "yes" : "no"); printf(" 17: stp_bad_sts: %s\n", emm_rsp_sts.s.stp_bad_sts ? "yes" : "no"); printf(" 16: stp_val: %s\n", emm_rsp_sts.s.stp_val ? "yes" : "no"); printf(" 15: rsp_timeout: %s\n", emm_rsp_sts.s.rsp_timeout ? "yes" : "no"); printf(" 14: rsp_crc_err: %s\n", emm_rsp_sts.s.rsp_crc_err ? "yes" : "no"); printf(" 13: rsp_bad_sts: %s\n", emm_rsp_sts.s.rsp_bad_sts ? "yes" : "no"); printf(" 12: rsp_val: %s\n", emm_rsp_sts.s.rsp_val ? "yes" : "no"); printf(" 9-11: rsp_type: %s\n", rtype_xor_str[emm_rsp_sts.s.rsp_type]); printf(" 7-8: cmd_type: %s\n", ctype_xor_str[emm_rsp_sts.s.cmd_type]); printf(" 1-6: cmd_idx: %u\n", emm_rsp_sts.s.cmd_idx); printf(" 0: cmd_done: %s\n", emm_rsp_sts.s.cmd_done ? "yes" : "no"); emm_rsp_lo.u = readq(host->base_addr + MIO_EMM_RSP_LO()); printf("\nMIO_EMM_RSP_STS_LO: 0x%016llx\n", emm_rsp_lo.u); emm_rsp_hi.u = readq(host->base_addr + MIO_EMM_RSP_HI()); printf("\nMIO_EMM_RSP_STS_HI: 0x%016llx\n", emm_rsp_hi.u); emm_int.u = readq(host->base_addr + MIO_EMM_INT()); printf("\nMIO_EMM_INT: 0x%016llx\n", emm_int.u); printf(" 6: switch_err: %s\n", emm_int.s.switch_err ? "yes" : "no"); printf(" 5: switch_done: %s\n", emm_int.s.switch_done ? "yes" : "no"); printf(" 4: dma_err: %s\n", emm_int.s.dma_err ? "yes" : "no"); printf(" 3: cmd_err: %s\n", emm_int.s.cmd_err ? "yes" : "no"); printf(" 2: dma_done: %s\n", emm_int.s.dma_done ? "yes" : "no"); printf(" 1: cmd_done: %s\n", emm_int.s.cmd_done ? "yes" : "no"); printf(" 0: buf_done: %s\n", emm_int.s.buf_done ? "yes" : "no"); emm_wdog.u = readq(host->base_addr + MIO_EMM_WDOG()); printf("\nMIO_EMM_WDOG: 0x%016llx (%u)\n", emm_wdog.u, emm_wdog.s.clk_cnt); if (IS_ENABLED(CONFIG_ARCH_OCTEONTX)) { emm_sample.u = readq(host->base_addr + MIO_EMM_SAMPLE()); printf("\nMIO_EMM_SAMPLE: 0x%016llx\n", emm_sample.u); printf(" 16-25: cmd_cnt: %u\n", emm_sample.s.cmd_cnt); printf(" 0-9: dat_cnt: %u\n", emm_sample.s.dat_cnt); } emm_sts_mask.u = readq(host->base_addr + MIO_EMM_STS_MASK()); printf("\nMIO_EMM_STS_MASK: 0x%016llx\n", emm_sts_mask.u); emm_rca.u = readq(host->base_addr + MIO_EMM_RCA()); printf("\nMIO_EMM_RCA: 0x%016llx\n", emm_rca.u); printf(" 0-15: card_rca: 0x%04x\n", emm_rca.s.card_rca); if (!IS_ENABLED(CONFIG_ARCH_OCTEONTX)) { emm_calb.u = readq(host->base_addr + MIO_EMM_CALB()); printf("\nMIO_EMM_CALB: 0x%016llx\n", emm_calb.u); printf(" 0: start: %u\n", emm_calb.s.start); emm_tap.u = readq(host->base_addr + MIO_EMM_TAP()); printf("\nMIO_EMM_TAP: 0x%016llx\n", emm_tap.u); printf(" 7-0: delay: %u\n", emm_tap.s.delay); emm_timing.u = readq(host->base_addr + MIO_EMM_TIMING()); printf("\nMIO_EMM_TIMING: 0x%016llx\n", emm_timing.u); printf(" 53-48: cmd_in_tap: %u\n", emm_timing.s.cmd_in_tap); printf(" 37-32: cmd_out_tap: %u\n", emm_timing.s.cmd_out_tap); printf(" 21-16: data_in_tap: %u\n", emm_timing.s.data_in_tap); printf(" 5-0: data_out_tap: %u\n", emm_timing.s.data_out_tap); io_ctl.u = readq(host->base_addr + MIO_EMM_IO_CTL()); printf("\nMIO_IO_CTL: 0x%016llx\n", io_ctl.u); printf(" 3-2: drive: %u (%u mA)\n", io_ctl.s.drive, 2 << io_ctl.s.drive); printf(" 0: slew: %u %s\n", io_ctl.s.slew, io_ctl.s.slew ? "high" : "low"); emm_debug.u = readq(host->base_addr + MIO_EMM_DEBUG()); printf("\nMIO_EMM_DEBUG: 0x%016llx\n", emm_debug.u); printf(" 21: rdsync_rst 0x%x\n", emm_debug.s.rdsync_rst); printf(" 20: emmc_clk_disable 0x%x\n", emm_debug.s.emmc_clk_disable); printf(" 19-16: dma_sm: 0x%x\n", emm_debug.s.dma_sm); printf(" 15-12: data_sm: 0x%x\n", emm_debug.s.data_sm); printf(" 11-8: cmd_sm: 0x%x\n", emm_debug.s.cmd_sm); printf(" 0: clk_on: 0x%x\n", emm_debug.s.clk_on); } puts("\n"); } /** * Print out all of the register values * * @param mmc MMC device */ static void octeontx_mmc_print_registers(struct mmc *mmc) { #ifdef DEBUG_REGISTERS const int print = 1; #else const int print = 0; #endif if (print) octeontx_mmc_print_registers2(mmc, mmc_to_host(mmc)); } #else static void octeontx_mmc_print_registers(struct mmc *mmc) { return; } #endif static const struct octeontx_sd_mods octeontx_cr_types[] = { { {0, 0}, {0, 0}, {0, 0} }, /* CMD0 */ { {0, 3}, {0, 3}, {0, 0} }, /* CMD1 */ { {0, 2}, {0, 2}, {0, 0} }, /* CMD2 */ { {0, 1}, {0, 3}, {0, 0} }, /* CMD3 SD_CMD_SEND_RELATIVE_ADDR 0, 2 */ { {0, 0}, {0, 0}, {0, 0} }, /* CMD4 */ { {0, 1}, {0, 1}, {0, 0} }, /* CMD5 */ { {0, 1}, {1, 1}, {0, 1} }, /* * CMD6 SD_CMD_SWITCH_FUNC 1,0 * (ACMD) SD_APP_SET_BUS_WIDTH */ { {0, 1}, {0, 1}, {0, 0} }, /* CMD7 */ { {1, 1}, {0, 3}, {0, 0} }, /* CMD8 SD_CMD_SEND_IF_COND 1,2 */ { {0, 2}, {0, 2}, {0, 0} }, /* CMD9 */ { {0, 2}, {0, 2}, {0, 0} }, /* CMD10 */ { {1, 1}, {0, 1}, {1, 1} }, /* CMD11 SD_CMD_SWITCH_UHS18V 1,0 */ { {0, 1}, {0, 1}, {0, 0} }, /* CMD12 */ { {0, 1}, {0, 1}, {1, 3} }, /* CMD13 (ACMD)) SD_CMD_APP_SD_STATUS 1,2 */ { {1, 1}, {1, 1}, {0, 0} }, /* CMD14 */ { {0, 0}, {0, 0}, {0, 0} }, /* CMD15 */ { {0, 1}, {0, 1}, {0, 0} }, /* CMD16 */ { {1, 1}, {1, 1}, {0, 0} }, /* CMD17 */ { {1, 1}, {1, 1}, {0, 0} }, /* CMD18 */ { {3, 1}, {3, 1}, {0, 0} }, /* CMD19 */ { {2, 1}, {0, 0}, {0, 0} }, /* CMD20 */ /* SD 2,0 */ { {0, 0}, {0, 0}, {0, 0} }, /* CMD21 */ { {0, 0}, {0, 0}, {1, 1} }, /* CMD22 (ACMD) SD_APP_SEND_NUM_WR_BLKS 1,0 */ { {0, 1}, {0, 1}, {0, 1} }, /* CMD23 */ /* SD ACMD 1,0 */ { {2, 1}, {2, 1}, {2, 1} }, /* CMD24 */ { {2, 1}, {2, 1}, {2, 1} }, /* CMD25 */ { {2, 1}, {2, 1}, {2, 1} }, /* CMD26 */ { {2, 1}, {2, 1}, {2, 1} }, /* CMD27 */ { {0, 1}, {0, 1}, {0, 1} }, /* CMD28 */ { {0, 1}, {0, 1}, {0, 1} }, /* CMD29 */ { {1, 1}, {1, 1}, {1, 1} }, /* CMD30 */ { {1, 1}, {1, 1}, {1, 1} }, /* CMD31 */ { {0, 0}, {0, 1}, {0, 0} }, /* CMD32 SD_CMD_ERASE_WR_BLK_START 0,1 */ { {0, 0}, {0, 1}, {0, 0} }, /* CMD33 SD_CMD_ERASE_WR_BLK_END 0,1 */ { {0, 0}, {0, 0}, {0, 0} }, /* CMD34 */ { {0, 1}, {0, 1}, {0, 1} }, /* CMD35 */ { {0, 1}, {0, 1}, {0, 1} }, /* CMD36 */ { {0, 0}, {0, 0}, {0, 0} }, /* CMD37 */ { {0, 1}, {0, 1}, {0, 1} }, /* CMD38 */ { {0, 4}, {0, 4}, {0, 4} }, /* CMD39 */ { {0, 5}, {0, 5}, {0, 5} }, /* CMD40 */ { {0, 0}, {0, 0}, {0, 3} }, /* CMD41 (ACMD) SD_CMD_APP_SEND_OP_COND 0,3 */ { {2, 1}, {2, 1}, {2, 1} }, /* CMD42 */ { {0, 0}, {0, 0}, {0, 0} }, /* CMD43 */ { {0, 0}, {0, 0}, {0, 0} }, /* CMD44 */ { {0, 0}, {0, 0}, {0, 0} }, /* CMD45 */ { {0, 0}, {0, 0}, {0, 0} }, /* CMD46 */ { {0, 0}, {0, 0}, {0, 0} }, /* CMD47 */ { {0, 0}, {1, 0}, {0, 0} }, /* CMD48 SD_CMD_READ_EXTR_SINGLE */ { {0, 0}, {2, 0}, {0, 0} }, /* CMD49 SD_CMD_WRITE_EXTR_SINGLE */ { {0, 0}, {0, 0}, {0, 0} }, /* CMD50 */ { {0, 0}, {0, 0}, {1, 1} }, /* CMD51 (ACMD) SD_CMD_APP_SEND_SCR 1,1 */ { {0, 0}, {0, 0}, {0, 0} }, /* CMD52 */ { {0, 0}, {0, 0}, {0, 0} }, /* CMD53 */ { {0, 0}, {0, 0}, {0, 0} }, /* CMD54 */ { {0, 1}, {0, 1}, {0, 1} }, /* CMD55 */ { {0xff, 0xff}, {0xff, 0xff}, {0xff, 0xff} }, /* CMD56 */ { {0, 0}, {0, 0}, {0, 0} }, /* CMD57 */ { {0, 0}, {0, 3}, {0, 3} }, /* CMD58 SD_CMD_SPI_READ_OCR 0,3 */ { {0, 0}, {0, 1}, {0, 0} }, /* CMD59 SD_CMD_SPI_CRC_ON_OFF 0,1 */ { {0, 0}, {0, 0}, {0, 0} }, /* CMD60 */ { {0, 0}, {0, 0}, {0, 0} }, /* CMD61 */ { {0, 0}, {0, 0}, {0, 0} }, /* CMD62 */ { {0, 0}, {0, 0}, {0, 0} } /* CMD63 */ }; /** * Returns XOR values needed for SD commands and other quirks * * @param mmc mmc device * @param cmd command information * * Return: octeontx_mmc_cr_mods data structure with various quirks and flags */ static struct octeontx_mmc_cr_mods octeontx_mmc_get_cr_mods(struct mmc *mmc, const struct mmc_cmd *cmd, const struct mmc_data *data) { struct octeontx_mmc_slot *slot = mmc_to_slot(mmc); struct octeontx_mmc_cr_mods cr = {0, 0}; const struct octeontx_sd_mods *sdm = &octeontx_cr_types[cmd->cmdidx & 0x3f]; u8 c = sdm->mmc.c, r = sdm->mmc.r; u8 desired_ctype = 0; if (IS_MMC(mmc)) { #ifdef MMC_SUPPORTS_TUNING if (cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200) { if (cmd->resp_type == MMC_RSP_R1) cr.rtype_xor = 1; if (data && data->flags & MMC_DATA_READ) cr.ctype_xor = 1; } #endif return cr; } if (cmd->cmdidx == 56) c = (cmd->cmdarg & 1) ? 1 : 2; if (data) { if (data->flags & MMC_DATA_READ) desired_ctype = 1; else if (data->flags & MMC_DATA_WRITE) desired_ctype = 2; } cr.ctype_xor = c ^ desired_ctype; if (slot->is_acmd) cr.rtype_xor = r ^ sdm->sdacmd.r; else cr.rtype_xor = r ^ sdm->sd.r; debug("%s(%s): mmc c: %d, mmc r: %d, desired c: %d, xor c: %d, xor r: %d\n", __func__, mmc->dev->name, c, r, desired_ctype, cr.ctype_xor, cr.rtype_xor); return cr; } /** * Keep track of switch commands internally */ static void octeontx_mmc_track_switch(struct mmc *mmc, u32 cmd_arg) { struct octeontx_mmc_slot *slot = mmc_to_slot(mmc); u8 how = (cmd_arg >> 24) & 3; u8 where = (u8)(cmd_arg >> 16); u8 val = (u8)(cmd_arg >> 8); slot->want_switch = slot->cached_switch; if (slot->is_acmd) return; if (how != 3) return; switch (where) { case EXT_CSD_BUS_WIDTH: slot->want_switch.s.bus_width = val; break; case EXT_CSD_POWER_CLASS: slot->want_switch.s.power_class = val; break; case EXT_CSD_HS_TIMING: slot->want_switch.s.hs_timing = 0; #if !defined(CONFIG_ARCH_OCTEON) slot->want_switch.s.hs200_timing = 0; slot->want_switch.s.hs400_timing = 0; #endif switch (val & 0xf) { case 0: break; case 1: slot->want_switch.s.hs_timing = 1; break; #if !defined(CONFIG_ARCH_OCTEON) case 2: if (!slot->is_asim && !slot->is_emul) slot->want_switch.s.hs200_timing = 1; break; case 3: if (!slot->is_asim && !slot->is_emul) slot->want_switch.s.hs400_timing = 1; break; #endif default: pr_err("%s(%s): Unsupported timing mode 0x%x\n", __func__, mmc->dev->name, val & 0xf); break; } break; default: break; } } static int octeontx_mmc_print_rsp_errors(struct mmc *mmc, union mio_emm_rsp_sts rsp_sts) { bool err = false; const char *name = mmc->dev->name; if (rsp_sts.s.acc_timeout) { pr_warn("%s(%s): acc_timeout\n", __func__, name); err = true; } if (rsp_sts.s.dbuf_err) { pr_warn("%s(%s): dbuf_err\n", __func__, name); err = true; } if (rsp_sts.s.blk_timeout) { pr_warn("%s(%s): blk_timeout\n", __func__, name); err = true; } if (rsp_sts.s.blk_crc_err) { pr_warn("%s(%s): blk_crc_err\n", __func__, name); err = true; } if (rsp_sts.s.stp_timeout) { pr_warn("%s(%s): stp_timeout\n", __func__, name); err = true; } if (rsp_sts.s.stp_crc_err) { pr_warn("%s(%s): stp_crc_err\n", __func__, name); err = true; } if (rsp_sts.s.stp_bad_sts) { pr_warn("%s(%s): stp_bad_sts\n", __func__, name); err = true; } if (err) pr_warn(" rsp_sts: 0x%llx\n", rsp_sts.u); return err ? -1 : 0; } /** * Starts a DMA operation for block read/write * * @param mmc mmc device * @param write true if write operation * @param clear true to clear DMA operation * @param adr source or destination DMA address * @param size size in blocks * @param timeout timeout in ms */ static void octeontx_mmc_start_dma(struct mmc *mmc, bool write, bool clear, u32 block, dma_addr_t adr, u32 size, int timeout) { const struct octeontx_mmc_slot *slot = mmc_to_slot(mmc); union mio_emm_dma_cfg emm_dma_cfg; union mio_emm_dma_adr emm_dma_adr; union mio_emm_dma emm_dma; /* Clear any interrupts */ write_csr(mmc, MIO_EMM_DMA_INT(), read_csr(mmc, MIO_EMM_DMA_INT())); emm_dma_cfg.u = 0; emm_dma_cfg.s.en = 1; emm_dma_cfg.s.rw = !!write; emm_dma_cfg.s.clr = !!clear; emm_dma_cfg.s.size = ((u64)(size * mmc->read_bl_len) / 8) - 1; #if __BYTE_ORDER != __BIG_ENDIAN emm_dma_cfg.s.endian = 1; #endif emm_dma_adr.u = 0; emm_dma_adr.s.adr = adr; write_csr(mmc, MIO_EMM_DMA_ADR(), emm_dma_adr.u); write_csr(mmc, MIO_EMM_DMA_CFG(), emm_dma_cfg.u); emm_dma.u = 0; emm_dma.s.bus_id = slot->bus_id; emm_dma.s.dma_val = 1; emm_dma.s.rw = !!write; emm_dma.s.sector = mmc->high_capacity ? 1 : 0; if (size > 1 && ((IS_SD(mmc) && (mmc->scr[0] & 2)) || !IS_SD(mmc))) emm_dma.s.multi = 1; else emm_dma.s.multi = 0; emm_dma.s.block_cnt = size; if (!mmc->high_capacity) block *= mmc->read_bl_len; emm_dma.s.card_addr = block; debug("%s(%s): card address: 0x%x, size: %d, multi: %d\n", __func__, mmc->dev->name, block, size, emm_dma.s.multi); if (timeout > 0) timeout = (timeout * 1000) - 1000; set_wdog(mmc, timeout); debug(" Writing 0x%llx to mio_emm_dma\n", emm_dma.u); write_csr(mmc, MIO_EMM_DMA(), emm_dma.u); } /** * Waits for a DMA operation to complete * * @param mmc mmc device * @param timeout timeout in ms * * Return: 0 for success (could be DMA errors), -ETIMEDOUT on timeout */ /** * Cleanup DMA engine after a failure * * @param mmc mmc device * @param rsp_sts rsp status */ static void octeontx_mmc_cleanup_dma(struct mmc *mmc, union mio_emm_rsp_sts rsp_sts) { struct octeontx_mmc_slot *slot = mmc_to_slot(mmc); union mio_emm_dma emm_dma; ulong start; int retries = 3; do { debug("%s(%s): rsp_sts: 0x%llx, rsp_lo: 0x%llx, dma_int: 0x%llx\n", __func__, mmc->dev->name, rsp_sts.u, read_csr(mmc, MIO_EMM_RSP_LO()), read_csr(mmc, MIO_EMM_DMA_INT())); emm_dma.u = read_csr(mmc, MIO_EMM_DMA()); emm_dma.s.dma_val = 1; emm_dma.s.dat_null = 1; emm_dma.s.bus_id = slot->bus_id; write_csr(mmc, MIO_EMM_DMA(), emm_dma.u); start = get_timer(0); do { rsp_sts.u = read_csr(mmc, MIO_EMM_RSP_STS()); schedule(); } while (get_timer(start) < 100 && (rsp_sts.s.dma_val || rsp_sts.s.dma_pend)); } while (retries-- >= 0 && rsp_sts.s.dma_pend); if (rsp_sts.s.dma_val) pr_err("%s(%s): Error: could not clean up DMA. RSP_STS: 0x%llx, RSP_LO: 0x%llx\n", __func__, mmc->dev->name, rsp_sts.u, read_csr(mmc, MIO_EMM_RSP_LO())); debug(" rsp_sts after clearing up DMA: 0x%llx\n", read_csr(mmc, MIO_EMM_RSP_STS())); } /** * Waits for a DMA operation to complete * * @param mmc mmc device * @param timeout timeout in ms * @param verbose true to print out error information * * Return: 0 for success (could be DMA errors), -ETIMEDOUT on timeout * or -EIO if IO error. */ static int octeontx_mmc_wait_dma(struct mmc *mmc, bool write, ulong timeout, bool verbose) { struct octeontx_mmc_host *host = mmc_to_host(mmc); ulong start_time = get_timer(0); union mio_emm_dma_int emm_dma_int; union mio_emm_rsp_sts rsp_sts; union mio_emm_dma emm_dma; bool timed_out = false; bool err = false; debug("%s(%s, %lu, %d), delay: %uus\n", __func__, mmc->dev->name, timeout, verbose, host->dma_wait_delay); udelay(host->dma_wait_delay); do { emm_dma_int.u = read_csr(mmc, MIO_EMM_DMA_INT()); rsp_sts.u = read_csr(mmc, MIO_EMM_RSP_STS()); if (write) { if ((rsp_sts.s.dma_pend && !rsp_sts.s.dma_val) || rsp_sts.s.blk_timeout || rsp_sts.s.stp_timeout || rsp_sts.s.rsp_timeout) { err = true; #ifdef DEBUG debug("%s: f1\n", __func__); octeontx_mmc_print_rsp_errors(mmc, rsp_sts); #endif break; } } else { if (rsp_sts.s.blk_crc_err || (rsp_sts.s.dma_pend && !rsp_sts.s.dma_val)) { err = true; #if defined(DEBUG) octeontx_mmc_print_rsp_errors(mmc, rsp_sts); #endif break; } } if (rsp_sts.s.dma_pend) { /* * If this is set then an error has occurred. * Try and restart the DMA operation. */ emm_dma.u = read_csr(mmc, MIO_EMM_DMA()); if (verbose) { pr_err("%s(%s): DMA pending error: rsp_sts: 0x%llx, dma_int: 0x%llx, emm_dma: 0x%llx\n", __func__, mmc->dev->name, rsp_sts.u, emm_dma_int.u, emm_dma.u); octeontx_print_rsp_sts(mmc); debug(" MIO_EMM_DEBUG: 0x%llx\n", read_csr(mmc, MIO_EMM_DEBUG())); pr_err("%s: Trying DMA resume...\n", __func__); } emm_dma.s.dma_val = 1; emm_dma.s.dat_null = 1; write_csr(mmc, MIO_EMM_DMA(), emm_dma.u); udelay(10); } else if (!rsp_sts.s.dma_val && emm_dma_int.s.done) { break; } schedule(); timed_out = (get_timer(start_time) > timeout); } while (!timed_out); if (timed_out || err) { if (verbose) { pr_err("%s(%s): MMC DMA %s after %lu ms, rsp_sts: 0x%llx, dma_int: 0x%llx, rsp_sts_lo: 0x%llx, emm_dma: 0x%llx\n", __func__, mmc->dev->name, timed_out ? "timed out" : "error", get_timer(start_time), rsp_sts.u, emm_dma_int.u, read_csr(mmc, MIO_EMM_RSP_LO()), read_csr(mmc, MIO_EMM_DMA())); octeontx_print_rsp_sts(mmc); } if (rsp_sts.s.dma_pend) octeontx_mmc_cleanup_dma(mmc, rsp_sts); } else { write_csr(mmc, MIO_EMM_DMA_INT(), read_csr(mmc, MIO_EMM_DMA_INT())); } return timed_out ? -ETIMEDOUT : (err ? -EIO : 0); } /** * Read blocks from the MMC/SD device * * @param mmc mmc device * @param cmd command * @param data data for read * @param verbose true to print out error information * * Return: number of blocks read or 0 if error */ static int octeontx_mmc_read_blocks(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data, bool verbose) { struct octeontx_mmc_host *host = mmc_to_host(mmc); union mio_emm_rsp_sts rsp_sts; dma_addr_t dma_addr = (dma_addr_t)dm_pci_virt_to_mem(host->dev, data->dest); ulong count; ulong blkcnt = data->blocks; ulong start = cmd->cmdarg; int timeout = 1000 + blkcnt * 20; bool timed_out = false; bool multi_xfer = cmd->cmdidx == MMC_CMD_READ_MULTIPLE_BLOCK; debug("%s(%s): dest: %p, dma address: 0x%llx, blkcnt: %lu, start: %lu\n", __func__, mmc->dev->name, data->dest, dma_addr, blkcnt, start); debug("%s: rsp_sts: 0x%llx\n", __func__, read_csr(mmc, MIO_EMM_RSP_STS())); /* use max timeout for multi-block transfers */ /* timeout = 0; */ /* * If we have a valid SD card in the slot, we set the response bit * mask to check for CRC errors and timeouts only. * Otherwise, use the default power on reset value. */ write_csr(mmc, MIO_EMM_STS_MASK(), IS_SD(mmc) ? 0x00b00000ull : 0xe4390080ull); invalidate_dcache_range((u64)data->dest, (u64)data->dest + blkcnt * data->blocksize); if (multi_xfer) { octeontx_mmc_start_dma(mmc, false, false, start, dma_addr, blkcnt, timeout); timed_out = !!octeontx_mmc_wait_dma(mmc, false, timeout, verbose); rsp_sts.u = read_csr(mmc, MIO_EMM_RSP_STS()); if (timed_out || rsp_sts.s.dma_val || rsp_sts.s.dma_pend) { if (!verbose) return 0; pr_err("%s(%s): Error: DMA timed out. rsp_sts: 0x%llx, emm_int: 0x%llx, dma_int: 0x%llx, rsp_lo: 0x%llx\n", __func__, mmc->dev->name, rsp_sts.u, read_csr(mmc, MIO_EMM_INT()), read_csr(mmc, MIO_EMM_DMA_INT()), read_csr(mmc, MIO_EMM_RSP_LO())); pr_err("%s: block count: %lu, start: 0x%lx\n", __func__, blkcnt, start); octeontx_mmc_print_registers(mmc); return 0; } } else { count = blkcnt; timeout = 1000; do { octeontx_mmc_start_dma(mmc, false, false, start, dma_addr, 1, timeout); dma_addr += mmc->read_bl_len; start++; timed_out = !!octeontx_mmc_wait_dma(mmc, false, timeout, verbose); rsp_sts.u = read_csr(mmc, MIO_EMM_RSP_STS()); if (timed_out || rsp_sts.s.dma_val || rsp_sts.s.dma_pend) { if (verbose) { pr_err("%s: Error: DMA timed out. rsp_sts: 0x%llx, emm_int: 0x%llx, dma_int: 0x%llx, rsp_lo: 0x%llx\n", __func__, rsp_sts.u, read_csr(mmc, MIO_EMM_INT()), read_csr(mmc, MIO_EMM_DMA_INT()), read_csr(mmc, MIO_EMM_RSP_LO())); pr_err("%s: block count: 1, start: 0x%lx\n", __func__, start); octeontx_mmc_print_registers(mmc); } return blkcnt - count; } schedule(); } while (--count); } #ifdef DEBUG debug("%s(%s): Read %lu (0x%lx) blocks starting at block %u (0x%x) to address %p (dma address 0x%llx)\n", __func__, mmc->dev->name, blkcnt, blkcnt, cmd->cmdarg, cmd->cmdarg, data->dest, dm_pci_virt_to_mem(host->dev, data->dest)); print_buffer(0, data->dest, 1, 0x200, 0); #endif return blkcnt; } static int octeontx_mmc_poll_ready(struct mmc *mmc, ulong timeout) { ulong start; struct mmc_cmd cmd; int err; bool not_ready = false; memset(&cmd, 0, sizeof(cmd)); cmd.cmdidx = MMC_CMD_SEND_STATUS; cmd.cmdarg = mmc->rca << 16; cmd.resp_type = MMC_RSP_R1; start = get_timer(0); do { err = octeontx_mmc_send_cmd(mmc, &cmd, NULL); if (err) { pr_err("%s(%s): MMC command error: %d; Retry...\n", __func__, mmc->dev->name, err); not_ready = true; } else if (cmd.response[0] & R1_READY_FOR_DATA) { return 0; } schedule(); } while (get_timer(start) < timeout); if (not_ready) pr_err("%s(%s): MMC command error; Retry timeout\n", __func__, mmc->dev->name); return -ETIMEDOUT; } static ulong octeontx_mmc_write_blocks(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) { struct octeontx_mmc_host *host = mmc_to_host(mmc); ulong start = cmd->cmdarg; ulong blkcnt = data->blocks; dma_addr_t dma_addr; union mio_emm_rsp_sts rsp_sts; union mio_emm_sts_mask emm_sts_mask; ulong timeout; int count; bool timed_out = false; bool multi_xfer = (blkcnt > 1) && ((IS_SD(mmc) && mmc->scr[0] & 2) || !IS_SD(mmc)); octeontx_mmc_switch_to(mmc); emm_sts_mask.u = 0; emm_sts_mask.s.sts_msk = R1_BLOCK_WRITE_MASK; write_csr(mmc, MIO_EMM_STS_MASK(), emm_sts_mask.u); if (octeontx_mmc_poll_ready(mmc, 10000)) { pr_err("%s(%s): Ready timed out\n", __func__, mmc->dev->name); return 0; } flush_dcache_range((u64)data->src, (u64)data->src + blkcnt * mmc->write_bl_len); dma_addr = (u64)dm_pci_virt_to_mem(host->dev, (void *)data->src); if (multi_xfer) { timeout = 5000 + 100 * blkcnt; octeontx_mmc_start_dma(mmc, true, false, start, dma_addr, blkcnt, timeout); timed_out = !!octeontx_mmc_wait_dma(mmc, true, timeout, true); rsp_sts.u = read_csr(mmc, MIO_EMM_RSP_STS()); if (timed_out || rsp_sts.s.dma_val || rsp_sts.s.dma_pend) { pr_err("%s(%s): Error: multi-DMA timed out after %lums. rsp_sts: 0x%llx, emm_int: 0x%llx, emm_dma_int: 0x%llx, rsp_sts_lo: 0x%llx, emm_dma: 0x%llx\n", __func__, mmc->dev->name, timeout, rsp_sts.u, read_csr(mmc, MIO_EMM_INT()), read_csr(mmc, MIO_EMM_DMA_INT()), read_csr(mmc, MIO_EMM_RSP_LO()), read_csr(mmc, MIO_EMM_DMA())); return 0; } } else { timeout = 5000; count = blkcnt; do { octeontx_mmc_start_dma(mmc, true, false, start, dma_addr, 1, timeout); dma_addr += mmc->read_bl_len; start++; timed_out = !!octeontx_mmc_wait_dma(mmc, true, timeout, true); rsp_sts.u = read_csr(mmc, MIO_EMM_RSP_STS()); if (timed_out || rsp_sts.s.dma_val || rsp_sts.s.dma_pend) { pr_err("%s(%s): Error: single-DMA timed out after %lums. rsp_sts: 0x%llx, emm_int: 0x%llx, emm_dma_int: 0x%llx, rsp_sts_lo: 0x%llx, emm_dma: 0x%llx\n", __func__, mmc->dev->name, timeout, rsp_sts.u, read_csr(mmc, MIO_EMM_RSP_STS()), read_csr(mmc, MIO_EMM_DMA_INT()), read_csr(mmc, MIO_EMM_RSP_LO()), read_csr(mmc, MIO_EMM_DMA())); return blkcnt - count; } schedule(); } while (--count); } return blkcnt; } /** * Send a command to the eMMC/SD device * * @param mmc mmc device * @param cmd cmd to send and response * @param data additional data * @param flags * Return: 0 for success, otherwise error */ static int octeontx_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) { struct octeontx_mmc_slot *slot = mmc_to_slot(mmc); const char *name = slot->dev->name; struct octeontx_mmc_cr_mods mods = {0, 0}; union mio_emm_rsp_sts rsp_sts; union mio_emm_cmd emm_cmd; union mio_emm_rsp_lo rsp_lo; union mio_emm_buf_idx emm_buf_idx; union mio_emm_buf_dat emm_buf_dat; ulong start; int i; ulong blkcnt; /** * This constant has a 1 bit for each command which should have a short * timeout and a 0 for each bit with a long timeout. Currently the * following commands have a long timeout: * CMD6, CMD17, CMD18, CMD24, CMD25, CMD32, CMD33, CMD35, CMD36 and * CMD38. */ static const u64 timeout_short = 0xFFFFFFA4FCF9FFDFull; uint timeout; if (cmd->cmdidx == MMC_CMD_SEND_EXT_CSD) { union mio_emm_rca emm_rca; emm_rca.u = 0; emm_rca.s.card_rca = mmc->rca; write_csr(mmc, MIO_EMM_RCA(), emm_rca.u); } if (timeout_short & (1ull << cmd->cmdidx)) timeout = MMC_TIMEOUT_SHORT; else if (cmd->cmdidx == MMC_CMD_SWITCH && IS_SD(mmc)) timeout = 2560; else if (cmd->cmdidx == MMC_CMD_ERASE) timeout = MMC_TIMEOUT_ERASE; else timeout = MMC_TIMEOUT_LONG; debug("%s(%s): cmd idx: %u, arg: 0x%x, resp type: 0x%x, timeout: %u\n", __func__, name, cmd->cmdidx, cmd->cmdarg, cmd->resp_type, timeout); if (data) debug(" data: addr: %p, flags: 0x%x, blocks: %u, blocksize: %u\n", data->dest, data->flags, data->blocks, data->blocksize); octeontx_mmc_switch_to(mmc); /* Clear any interrupts */ write_csr(mmc, MIO_EMM_INT(), read_csr(mmc, MIO_EMM_INT())); /* * We need to override the default command types and response types * when dealing with SD cards. */ mods = octeontx_mmc_get_cr_mods(mmc, cmd, data); /* Handle block read/write/stop operations */ switch (cmd->cmdidx) { case MMC_CMD_GO_IDLE_STATE: slot->tuned = false; slot->hs200_tuned = false; slot->hs400_tuned = false; break; case MMC_CMD_STOP_TRANSMISSION: return 0; case MMC_CMD_READ_MULTIPLE_BLOCK: case MMC_CMD_READ_SINGLE_BLOCK: pr_debug("%s(%s): Reading blocks\n", __func__, name); blkcnt = octeontx_mmc_read_blocks(mmc, cmd, data, true); return (blkcnt > 0) ? 0 : -1; case MMC_CMD_WRITE_MULTIPLE_BLOCK: case MMC_CMD_WRITE_SINGLE_BLOCK: blkcnt = octeontx_mmc_write_blocks(mmc, cmd, data); return (blkcnt > 0) ? 0 : -1; case MMC_CMD_SELECT_CARD: /* Set the RCA register (is it set automatically?) */ if (IS_SD(mmc)) { union mio_emm_rca emm_rca; emm_rca.u = 0; emm_rca.s.card_rca = (cmd->cmdarg >> 16); write_csr(mmc, MIO_EMM_RCA(), emm_rca.u); debug("%s: Set SD relative address (RCA) to 0x%x\n", __func__, emm_rca.s.card_rca); } break; case MMC_CMD_SWITCH: if (!data && !slot->is_acmd) octeontx_mmc_track_switch(mmc, cmd->cmdarg); break; } emm_cmd.u = 0; emm_cmd.s.cmd_val = 1; emm_cmd.s.bus_id = slot->bus_id; emm_cmd.s.cmd_idx = cmd->cmdidx; emm_cmd.s.arg = cmd->cmdarg; emm_cmd.s.ctype_xor = mods.ctype_xor; emm_cmd.s.rtype_xor = mods.rtype_xor; if (data && data->blocks == 1 && data->blocksize != 512) { emm_cmd.s.offset = 64 - ((data->blocks * data->blocksize) / 8); debug("%s: offset set to %u\n", __func__, emm_cmd.s.offset); } if (data && data->flags & MMC_DATA_WRITE) { u8 *src = (u8 *)data->src; if (!src) { pr_err("%s(%s): Error: data source for cmd 0x%x is NULL!\n", __func__, name, cmd->cmdidx); return -1; } if (data->blocksize > 512) { pr_err("%s(%s): Error: data for cmd 0x%x exceeds 512 bytes\n", __func__, name, cmd->cmdidx); return -1; } #ifdef DEBUG debug("%s: Sending %d bytes data\n", __func__, data->blocksize); print_buffer(0, src, 1, data->blocksize, 0); #endif emm_buf_idx.u = 0; emm_buf_idx.s.inc = 1; write_csr(mmc, MIO_EMM_BUF_IDX(), emm_buf_idx.u); for (i = 0; i < (data->blocksize + 7) / 8; i++) { memcpy(&emm_buf_dat.u, src, sizeof(emm_buf_dat.u)); write_csr(mmc, MIO_EMM_BUF_DAT(), cpu_to_be64(emm_buf_dat.u)); src += sizeof(emm_buf_dat.u); } write_csr(mmc, MIO_EMM_BUF_IDX(), 0); } debug("%s(%s): Sending command %u (emm_cmd: 0x%llx)\n", __func__, name, cmd->cmdidx, emm_cmd.u); set_wdog(mmc, timeout * 1000); write_csr(mmc, MIO_EMM_CMD(), emm_cmd.u); /* Wait for command to finish or time out */ start = get_timer(0); do { rsp_sts.u = read_csr(mmc, MIO_EMM_RSP_STS()); schedule(); } while (!rsp_sts.s.cmd_done && !rsp_sts.s.rsp_timeout && (get_timer(start) < timeout + 10)); octeontx_mmc_print_rsp_errors(mmc, rsp_sts); if (rsp_sts.s.rsp_timeout || !rsp_sts.s.cmd_done) { debug("%s(%s): Error: command %u(0x%x) timed out. rsp_sts: 0x%llx\n", __func__, name, cmd->cmdidx, cmd->cmdarg, rsp_sts.u); octeontx_mmc_print_registers(mmc); return -ETIMEDOUT; } if (rsp_sts.s.rsp_crc_err) { debug("%s(%s): RSP CRC error, rsp_sts: 0x%llx, cmdidx: %u, arg: 0x%08x\n", __func__, name, rsp_sts.u, cmd->cmdidx, cmd->cmdarg); octeontx_mmc_print_registers(mmc); return -1; } if (slot->bus_id != rsp_sts.s.bus_id) { pr_warn("%s(%s): bus id mismatch, got %d, expected %d for command 0x%x(0x%x)\n", __func__, name, rsp_sts.s.bus_id, slot->bus_id, cmd->cmdidx, cmd->cmdarg); goto error; } if (rsp_sts.s.rsp_bad_sts) { rsp_lo.u = read_csr(mmc, MIO_EMM_RSP_LO()); debug("%s: Bad response for bus id %d, cmd id %d:\n" " rsp_timeout: %d\n" " rsp_bad_sts: %d\n" " rsp_crc_err: %d\n", __func__, slot->bus_id, cmd->cmdidx, rsp_sts.s.rsp_timeout, rsp_sts.s.rsp_bad_sts, rsp_sts.s.rsp_crc_err); if (rsp_sts.s.rsp_type == 1 && rsp_sts.s.rsp_bad_sts) { debug(" Response status: 0x%llx\n", (rsp_lo.u >> 8) & 0xffffffff); #ifdef DEBUG mmc_print_status((rsp_lo.u >> 8) & 0xffffffff); #endif } goto error; } if (rsp_sts.s.cmd_idx != cmd->cmdidx) { debug("%s(%s): Command response index %d does not match command index %d\n", __func__, name, rsp_sts.s.cmd_idx, cmd->cmdidx); octeontx_print_rsp_sts(mmc); debug("%s: rsp_lo: 0x%llx\n", __func__, read_csr(mmc, MIO_EMM_RSP_LO())); goto error; } slot->is_acmd = (cmd->cmdidx == MMC_CMD_APP_CMD); if (!cmd->resp_type & MMC_RSP_PRESENT) debug(" Response type: 0x%x, no response expected\n", cmd->resp_type); /* Get the response if present */ if (rsp_sts.s.rsp_val && (cmd->resp_type & MMC_RSP_PRESENT)) { union mio_emm_rsp_hi rsp_hi; rsp_lo.u = read_csr(mmc, MIO_EMM_RSP_LO()); switch (rsp_sts.s.rsp_type) { case 1: case 3: case 4: case 5: cmd->response[0] = (rsp_lo.u >> 8) & 0xffffffffull; debug(" response: 0x%08x\n", cmd->response[0]); cmd->response[1] = 0; cmd->response[2] = 0; cmd->response[3] = 0; break; case 2: cmd->response[3] = rsp_lo.u & 0xffffffff; cmd->response[2] = (rsp_lo.u >> 32) & 0xffffffff; rsp_hi.u = read_csr(mmc, MIO_EMM_RSP_HI()); cmd->response[1] = rsp_hi.u & 0xffffffff; cmd->response[0] = (rsp_hi.u >> 32) & 0xffffffff; debug(" response: 0x%08x 0x%08x 0x%08x 0x%08x\n", cmd->response[0], cmd->response[1], cmd->response[2], cmd->response[3]); break; default: pr_err("%s(%s): Unknown response type 0x%x for command %d, arg: 0x%x, rsp_sts: 0x%llx\n", __func__, name, rsp_sts.s.rsp_type, cmd->cmdidx, cmd->cmdarg, rsp_sts.u); return -1; } } else { debug(" Response not expected\n"); } if (data && data->flags & MMC_DATA_READ) { u8 *dest = (u8 *)data->dest; if (!dest) { pr_err("%s(%s): Error, destination buffer NULL!\n", __func__, mmc->dev->name); goto error; } if (data->blocksize > 512) { printf("%s(%s): Error: data size %u exceeds 512\n", __func__, mmc->dev->name, data->blocksize); goto error; } emm_buf_idx.u = 0; emm_buf_idx.s.inc = 1; write_csr(mmc, MIO_EMM_BUF_IDX(), emm_buf_idx.u); for (i = 0; i < (data->blocksize + 7) / 8; i++) { emm_buf_dat.u = read_csr(mmc, MIO_EMM_BUF_DAT()); emm_buf_dat.u = be64_to_cpu(emm_buf_dat.u); memcpy(dest, &emm_buf_dat.u, sizeof(emm_buf_dat.u)); dest += sizeof(emm_buf_dat.u); } write_csr(mmc, MIO_EMM_BUF_IDX(), 0); #ifdef DEBUG debug("%s: Received %d bytes data\n", __func__, data->blocksize); print_buffer(0, data->dest, 1, data->blocksize, 0); #endif } return 0; error: #ifdef DEBUG octeontx_mmc_print_registers(mmc); #endif return -1; } static int octeontx_mmc_dev_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, struct mmc_data *data) { return octeontx_mmc_send_cmd(dev_to_mmc(dev), cmd, data); } #ifdef MMC_SUPPORTS_TUNING static int octeontx_mmc_test_cmd(struct mmc *mmc, u32 opcode, int *statp) { struct mmc_cmd cmd; int err; memset(&cmd, 0, sizeof(cmd)); debug("%s(%s, %u, %p)\n", __func__, mmc->dev->name, opcode, statp); cmd.cmdidx = opcode; cmd.resp_type = MMC_RSP_R1; cmd.cmdarg = mmc->rca << 16; err = octeontx_mmc_send_cmd(mmc, &cmd, NULL); if (err) debug("%s(%s, %u) returned %d\n", __func__, mmc->dev->name, opcode, err); if (statp) *statp = cmd.response[0]; return err; } static int octeontx_mmc_send_tuning(struct mmc *mmc, u32 opcode, int *error) { *error = 0; return mmc_send_tuning(mmc, opcode); } static int octeontx_mmc_test_get_ext_csd(struct mmc *mmc, u32 opcode, int *statp) { struct mmc_cmd cmd; struct mmc_data data; int err; u8 ext_csd[MMC_MAX_BLOCK_LEN]; debug("%s(%s, %u, %p)\n", __func__, mmc->dev->name, opcode, statp); memset(&cmd, 0, sizeof(cmd)); cmd.cmdidx = MMC_CMD_SEND_EXT_CSD; cmd.resp_type = MMC_RSP_R1; cmd.cmdarg = 0; data.dest = (char *)ext_csd; data.blocks = 1; data.blocksize = MMC_MAX_BLOCK_LEN; data.flags = MMC_DATA_READ; err = octeontx_mmc_send_cmd(mmc, &cmd, &data); if (statp) *statp = cmd.response[0]; return err; } /** * Wrapper to set the MIO_EMM_TIMING register * * @param mmc pointer to mmc data structure * @param emm_timing New emm_timing register value * * On some devices it is possible that changing the data out value can * cause a glitch on an internal fifo. This works around this problem * by performing a soft-reset immediately before setting the timing register. * * Note: this function should not be called from any function that * performs DMA or block operations since not all registers are * preserved. */ static void octeontx_mmc_set_emm_timing(struct mmc *mmc, union mio_emm_timing emm_timing) { union mio_emm_cfg emm_cfg; struct octeontx_mmc_slot *slot = mmc->priv; union mio_emm_debug emm_debug; debug("%s(%s, 0x%llx) din: %u\n", __func__, mmc->dev->name, emm_timing.u, emm_timing.s.data_in_tap); udelay(1); if (slot->host->tap_requires_noclk) { /* Turn off the clock */ emm_debug.u = read_csr(mmc, MIO_EMM_DEBUG()); emm_debug.s.emmc_clk_disable = 1; write_csr(mmc, MIO_EMM_DEBUG(), emm_debug.u); udelay(1); emm_debug.s.rdsync_rst = 1; write_csr(mmc, MIO_EMM_DEBUG(), emm_debug.u); } emm_cfg.u = read_csr(mmc, MIO_EMM_CFG()); emm_cfg.s.bus_ena = 1 << 3; write_csr(mmc, MIO_EMM_CFG(), emm_cfg.u); udelay(1); write_csr(mmc, MIO_EMM_TIMING(), emm_timing.u); udelay(1); if (slot->host->tap_requires_noclk) { /* Turn on the clock */ emm_debug.s.rdsync_rst = 0; write_csr(mmc, MIO_EMM_DEBUG(), emm_debug.u); udelay(1); emm_debug.s.emmc_clk_disable = 0; write_csr(mmc, MIO_EMM_DEBUG(), emm_debug.u); udelay(1); } emm_cfg.s.bus_ena = 1 << mmc_to_slot(mmc)->bus_id; write_csr(mmc, MIO_EMM_CFG(), emm_cfg.u); } static const u8 octeontx_hs400_tuning_block[512] = { 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, 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, 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, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x55, 0xaa, 0x55, 0xaa, 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, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x01, 0xfe, 0x01, 0xfe, 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, }; /** * Perform tuning in HS400 mode * * @param[in] mmc mmc data structure * * @ret 0 for success, otherwise error */ static int octeontx_tune_hs400(struct mmc *mmc) { struct octeontx_mmc_slot *slot = mmc_to_slot(mmc); struct mmc_cmd cmd; struct mmc_data data; union mio_emm_timing emm_timing; u8 buffer[mmc->read_bl_len]; int tap_adj; int err = -1; int tap; int run = 0; int start_run = -1; int best_run = 0; int best_start = -1; bool prev_ok = false; char env_name[64]; char how[MAX_NO_OF_TAPS + 1] = ""; if (slot->hs400_tuning_block == -1) return 0; /* The eMMC standard disables all tuning support when operating in * DDR modes like HS400. The problem with this is that there are * many cases where the HS200 tuning does not work for HS400 mode. * In order to perform this tuning, while in HS200 a block is written * to a block specified in the device tree (marvell,hs400-tuning-block) * which is used for tuning in this function by repeatedly reading * this block and comparing the data and return code. This function * chooses the data input tap in the middle of the longest run of * successful read operations. */ emm_timing = slot->hs200_taps; debug("%s(%s): Start ci: %d, co: %d, di: %d, do: %d\n", __func__, mmc->dev->name, emm_timing.s.cmd_in_tap, emm_timing.s.cmd_out_tap, emm_timing.s.data_in_tap, emm_timing.s.data_out_tap); memset(buffer, 0xdb, sizeof(buffer)); snprintf(env_name, sizeof(env_name), "emmc%d_data_in_tap_hs400", slot->bus_id); tap = env_get_ulong(env_name, 10, -1L); if (tap >= 0 && tap < MAX_NO_OF_TAPS) { printf("Overriding data input tap for HS400 mode to %d\n", tap); emm_timing.s.data_in_tap = tap; octeontx_mmc_set_emm_timing(mmc, emm_timing); return 0; } for (tap = 0; tap <= MAX_NO_OF_TAPS; tap++, prev_ok = !err) { if (tap < MAX_NO_OF_TAPS) { debug("%s: Testing data in tap %d\n", __func__, tap); emm_timing.s.data_in_tap = tap; octeontx_mmc_set_emm_timing(mmc, emm_timing); cmd.cmdidx = MMC_CMD_READ_SINGLE_BLOCK; cmd.cmdarg = slot->hs400_tuning_block; cmd.resp_type = MMC_RSP_R1; data.dest = (void *)buffer; data.blocks = 1; data.blocksize = mmc->read_bl_len; data.flags = MMC_DATA_READ; err = !octeontx_mmc_read_blocks(mmc, &cmd, &data, false); if (err || memcmp(buffer, octeontx_hs400_tuning_block, sizeof(buffer))) { #ifdef DEBUG if (!err) { debug("%s: data mismatch. Read:\n", __func__); print_buffer(0, buffer, 1, sizeof(buffer), 0); debug("\nExpected:\n"); print_buffer(0, octeontx_hs400_tuning_block, 1, sizeof(octeontx_hs400_tuning_block), 0); } else { debug("%s: Error %d reading block\n", __func__, err); } #endif err = -EINVAL; } else { debug("%s: tap %d good\n", __func__, tap); } how[tap] = "-+"[!err]; } else { err = -EINVAL; } if (!err) { if (!prev_ok) start_run = tap; } else if (prev_ok) { run = tap - 1 - start_run; if (start_run >= 0 && run > best_run) { best_start = start_run; best_run = run; } } } how[tap - 1] = '\0'; if (best_start < 0) { printf("%s(%s): %lldMHz tuning failed for HS400\n", __func__, mmc->dev->name, slot->clock / 1000000); return -EINVAL; } tap = best_start + best_run / 2; snprintf(env_name, sizeof(env_name), "emmc%d_data_in_tap_adj_hs400", slot->bus_id); tap_adj = env_get_ulong(env_name, 10, slot->hs400_tap_adj); /* * Keep it in range and if out of range force it back in with a small * buffer. */ if (best_run > 3) { tap = tap + tap_adj; if (tap >= best_start + best_run) tap = best_start + best_run - 2; if (tap <= best_start) tap = best_start + 2; } how[tap] = '@'; debug("Tuning: %s\n", how); debug("%s(%s): HS400 tap: best run start: %d, length: %d, tap: %d\n", __func__, mmc->dev->name, best_start, best_run, tap); slot->hs400_taps = slot->hs200_taps; slot->hs400_taps.s.data_in_tap = tap; slot->hs400_tuned = true; if (env_get_yesno("emmc_export_hs400_taps") > 0) { debug("%s(%s): Exporting HS400 taps\n", __func__, mmc->dev->name); env_set_ulong("emmc_timing_tap", slot->host->timing_taps); snprintf(env_name, sizeof(env_name), "emmc%d_hs400_data_in_tap_debug", slot->bus_id); env_set(env_name, how); snprintf(env_name, sizeof(env_name), "emmc%d_hs400_data_in_tap_val", slot->bus_id); env_set_ulong(env_name, tap); snprintf(env_name, sizeof(env_name), "emmc%d_hs400_data_in_tap_start", slot->bus_id); env_set_ulong(env_name, best_start); snprintf(env_name, sizeof(env_name), "emmc%d_hs400_data_in_tap_end", slot->bus_id); env_set_ulong(env_name, best_start + best_run); snprintf(env_name, sizeof(env_name), "emmc%d_hs400_cmd_in_tap", slot->bus_id); env_set_ulong(env_name, slot->hs400_taps.s.cmd_in_tap); snprintf(env_name, sizeof(env_name), "emmc%d_hs400_cmd_out_tap", slot->bus_id); env_set_ulong(env_name, slot->hs400_taps.s.cmd_out_tap); snprintf(env_name, sizeof(env_name), "emmc%d_hs400_cmd_out_delay", slot->bus_id); env_set_ulong(env_name, slot->cmd_out_hs400_delay); snprintf(env_name, sizeof(env_name), "emmc%d_hs400_data_out_tap", slot->bus_id); env_set_ulong(env_name, slot->hs400_taps.s.data_out_tap); snprintf(env_name, sizeof(env_name), "emmc%d_hs400_data_out_delay", slot->bus_id); env_set_ulong(env_name, slot->data_out_hs400_delay); } else { debug("%s(%s): HS400 environment export disabled\n", __func__, mmc->dev->name); } octeontx_mmc_set_timing(mmc); return 0; } struct adj { const char *name; u8 mask_shift; int (*test)(struct mmc *mmc, u32 opcode, int *error); u32 opcode; bool ddr_only; bool hs200_only; bool not_hs200_only; u8 num_runs; }; struct adj adj[] = { { "CMD_IN", 48, octeontx_mmc_test_cmd, MMC_CMD_SEND_STATUS, false, false, false, 2, }, /* { "CMD_OUT", 32, octeontx_mmc_test_cmd, MMC_CMD_SEND_STATUS, },*/ { "DATA_IN(HS200)", 16, octeontx_mmc_send_tuning, MMC_CMD_SEND_TUNING_BLOCK_HS200, false, true, false, 2, }, { "DATA_IN", 16, octeontx_mmc_test_get_ext_csd, 0, false, false, true, 2, }, /* { "DATA_OUT", 0, octeontx_mmc_test_cmd, 0, true, false},*/ { NULL, }, }; /** * Perform tuning tests to find optimal timing * * @param mmc mmc device * @param adj parameter to tune * @param opcode command opcode to use * * Return: 0 for success, -1 if tuning failed */ static int octeontx_mmc_adjust_tuning(struct mmc *mmc, struct adj *adj, u32 opcode) { struct octeontx_mmc_slot *slot = mmc_to_slot(mmc); union mio_emm_timing timing; union mio_emm_debug emm_debug; int tap; int err = -1; int run = 0; int count; int start_run = -1; int best_run = 0; int best_start = -1; bool prev_ok = false; u64 tap_status = 0; const int tap_adj = slot->hs200_tap_adj; char how[MAX_NO_OF_TAPS + 1] = ""; bool is_hs200 = mmc->selected_mode == MMC_HS_200; debug("%s(%s, %s, %d), hs200: %d\n", __func__, mmc->dev->name, adj->name, opcode, is_hs200); octeontx_mmc_set_emm_timing(mmc, is_hs200 ? slot->hs200_taps : slot->taps); #ifdef DEBUG if (opcode == MMC_CMD_SEND_TUNING_BLOCK_HS200) { printf("%s(%s): Before tuning %s, opcode: %d\n", __func__, mmc->dev->name, adj->name, opcode); octeontx_mmc_print_registers2(mmc, NULL); } #endif /* * The algorithm to find the optimal timing is to start * at the end and work backwards and select the second * value that passes. Each test is repeated twice. */ for (tap = 0; tap <= MAX_NO_OF_TAPS; tap++, prev_ok = !err) { if (tap < MAX_NO_OF_TAPS) { if (slot->host->tap_requires_noclk) { /* Turn off the clock */ emm_debug.u = read_csr(mmc, MIO_EMM_DEBUG()); emm_debug.s.emmc_clk_disable = 1; write_csr(mmc, MIO_EMM_DEBUG(), emm_debug.u); udelay(1); emm_debug.s.rdsync_rst = 1; write_csr(mmc, MIO_EMM_DEBUG(), emm_debug.u); udelay(1); } timing.u = read_csr(mmc, MIO_EMM_TIMING()); timing.u &= ~(0x3full << adj->mask_shift); timing.u |= (u64)tap << adj->mask_shift; write_csr(mmc, MIO_EMM_TIMING(), timing.u); debug("%s(%s): Testing ci: %d, co: %d, di: %d, do: %d\n", __func__, mmc->dev->name, timing.s.cmd_in_tap, timing.s.cmd_out_tap, timing.s.data_in_tap, timing.s.data_out_tap); if (slot->host->tap_requires_noclk) { /* Turn off the clock */ emm_debug.s.rdsync_rst = 0; write_csr(mmc, MIO_EMM_DEBUG(), emm_debug.u); udelay(1); emm_debug.u = read_csr(mmc, MIO_EMM_DEBUG()); emm_debug.s.emmc_clk_disable = 0; write_csr(mmc, MIO_EMM_DEBUG(), emm_debug.u); udelay(1); } for (count = 0; count < 2; count++) { err = adj->test(mmc, opcode, NULL); if (err) { debug("%s(%s, %s): tap %d failed, count: %d, rsp_sts: 0x%llx, rsp_lo: 0x%llx\n", __func__, mmc->dev->name, adj->name, tap, count, read_csr(mmc, MIO_EMM_RSP_STS()), read_csr(mmc, MIO_EMM_RSP_LO())); debug("%s(%s, %s): tap: %d, do: %d, di: %d, co: %d, ci: %d\n", __func__, mmc->dev->name, adj->name, tap, timing.s.data_out_tap, timing.s.data_in_tap, timing.s.cmd_out_tap, timing.s.cmd_in_tap); break; } debug("%s(%s, %s): tap %d passed, count: %d, rsp_sts: 0x%llx, rsp_lo: 0x%llx\n", __func__, mmc->dev->name, adj->name, tap, count, read_csr(mmc, MIO_EMM_RSP_STS()), read_csr(mmc, MIO_EMM_RSP_LO())); } tap_status |= (u64)(!err) << tap; how[tap] = "-+"[!err]; } else { /* * Putting the end+1 case in the loop simplifies * logic, allowing 'prev_ok' to process a sweet * spot in tuning which extends to the wall. */ err = -EINVAL; } if (!err) { /* * If no CRC/etc errors in the response, but previous * failed, note the start of a new run. */ debug(" prev_ok: %d\n", prev_ok); if (!prev_ok) start_run = tap; } else if (prev_ok) { run = tap - 1 - start_run; /* did we just exit a wider sweet spot? */ if (start_run >= 0 && run > best_run) { best_start = start_run; best_run = run; } } } how[tap - 1] = '\0'; if (best_start < 0) { printf("%s(%s, %s): %lldMHz tuning %s failed\n", __func__, mmc->dev->name, adj->name, slot->clock / 1000000, adj->name); return -EINVAL; } tap = best_start + best_run / 2; debug(" tap %d is center, start: %d, run: %d\n", tap, best_start, best_run); if (is_hs200) { slot->hs200_taps.u &= ~(0x3full << adj->mask_shift); slot->hs200_taps.u |= (u64)tap << adj->mask_shift; } else { slot->taps.u &= ~(0x3full << adj->mask_shift); slot->taps.u |= (u64)tap << adj->mask_shift; } if (best_start < 0) { printf("%s(%s, %s): %lldMHz tuning %s failed\n", __func__, mmc->dev->name, adj->name, slot->clock / 1000000, adj->name); return -EINVAL; } tap = best_start + best_run / 2; if (is_hs200 && (tap + tap_adj >= 0) && (tap + tap_adj < 64) && tap_status & (1ULL << (tap + tap_adj))) { debug("Adjusting tap from %d by %d to %d\n", tap, tap_adj, tap + tap_adj); tap += tap_adj; } how[tap] = '@'; debug("%s/%s %d/%d/%d %s\n", mmc->dev->name, adj->name, best_start, tap, best_start + best_run, how); if (is_hs200) { slot->hs200_taps.u &= ~(0x3full << adj->mask_shift); slot->hs200_taps.u |= (u64)tap << adj->mask_shift; } else { slot->taps.u &= ~(0x3full << adj->mask_shift); slot->taps.u |= (u64)tap << adj->mask_shift; } #ifdef DEBUG if (opcode == MMC_CMD_SEND_TUNING_BLOCK_HS200) { debug("%s(%s, %s): After successful tuning\n", __func__, mmc->dev->name, adj->name); debug("%s(%s, %s): tap: %d, new do: %d, di: %d, co: %d, ci: %d\n", __func__, mmc->dev->name, adj->name, tap, slot->taps.s.data_out_tap, slot->taps.s.data_in_tap, slot->taps.s.cmd_out_tap, slot->taps.s.cmd_in_tap); debug("%s(%s, %s): tap: %d, new do HS200: %d, di: %d, co: %d, ci: %d\n", __func__, mmc->dev->name, adj->name, tap, slot->hs200_taps.s.data_out_tap, slot->hs200_taps.s.data_in_tap, slot->hs200_taps.s.cmd_out_tap, slot->hs200_taps.s.cmd_in_tap); } #endif octeontx_mmc_set_timing(mmc); if (is_hs200 && env_get_yesno("emmc_export_hs200_taps")) { char env_name[64]; env_set_ulong("emmc_timing_tap", slot->host->timing_taps); switch (opcode) { case MMC_CMD_SEND_TUNING_BLOCK: snprintf(env_name, sizeof(env_name), "emmc%d_hs200_data_in_tap_debug", slot->bus_id); env_set(env_name, how); snprintf(env_name, sizeof(env_name), "emmc%d_hs200_data_in_tap_val", slot->bus_id); env_set_ulong(env_name, tap); snprintf(env_name, sizeof(env_name), "emmc%d_hs200_data_in_tap_start", slot->bus_id); env_set_ulong(env_name, best_start); snprintf(env_name, sizeof(env_name), "emmc%d_hs200_data_in_tap_end", slot->bus_id); env_set_ulong(env_name, best_start + best_run); break; case MMC_CMD_SEND_STATUS: snprintf(env_name, sizeof(env_name), "emmc%d_hs200_cmd_in_tap_debug", slot->bus_id); env_set(env_name, how); snprintf(env_name, sizeof(env_name), "emmc%d_hs200_cmd_in_tap_val", slot->bus_id); env_set_ulong(env_name, tap); snprintf(env_name, sizeof(env_name), "emmc%d_hs200_cmd_in_tap_start", slot->bus_id); env_set_ulong(env_name, best_start); snprintf(env_name, sizeof(env_name), "emmc%d_hs200_cmd_in_tap_end", slot->bus_id); env_set_ulong(env_name, best_start + best_run); break; default: snprintf(env_name, sizeof(env_name), "emmc%d_hs200_data_out_tap", slot->bus_id); env_set_ulong(env_name, slot->data_out_hs200_delay); snprintf(env_name, sizeof(env_name), "emmc%d_hs200_cmd_out_tap", slot->bus_id); env_set_ulong(env_name, slot->cmd_out_hs200_delay); break; } } return 0; } static int octeontx_mmc_execute_tuning(struct udevice *dev, u32 opcode) { struct mmc *mmc = dev_to_mmc(dev); struct octeontx_mmc_slot *slot = mmc_to_slot(mmc); union mio_emm_timing emm_timing; int err; struct adj *a; bool is_hs200; char env_name[64]; pr_info("%s re-tuning, opcode 0x%x\n", dev->name, opcode); if (slot->is_asim || slot->is_emul) return 0; is_hs200 = (mmc->selected_mode == MMC_HS_200); if (is_hs200) { slot->hs200_tuned = false; slot->hs400_tuned = false; } else { slot->tuned = false; } octeontx_mmc_set_output_bus_timing(mmc); octeontx_mmc_set_input_bus_timing(mmc); emm_timing.u = read_csr(mmc, MIO_EMM_TIMING()); if (mmc->selected_mode == MMC_HS_200) { slot->hs200_taps.s.cmd_out_tap = emm_timing.s.cmd_out_tap; slot->hs200_taps.s.data_out_tap = emm_timing.s.data_out_tap; } else { slot->taps.s.cmd_out_tap = emm_timing.s.cmd_out_tap; slot->taps.s.data_out_tap = emm_timing.s.data_out_tap; } octeontx_mmc_set_input_bus_timing(mmc); octeontx_mmc_set_output_bus_timing(mmc); for (a = adj; a->name; a++) { ulong in_tap; if (!strcmp(a->name, "CMD_IN")) { snprintf(env_name, sizeof(env_name), "emmc%d_cmd_in_tap", slot->bus_id); in_tap = env_get_ulong(env_name, 10, (ulong)-1); if (in_tap != (ulong)-1) { if (mmc->selected_mode == MMC_HS_200 || a->hs200_only) { slot->hs200_taps.s.cmd_in_tap = in_tap; slot->hs400_taps.s.cmd_in_tap = in_tap; } else { slot->taps.s.cmd_in_tap = in_tap; } continue; } } else if (a->hs200_only && !strcmp(a->name, "DATA_IN(HS200)")) { snprintf(env_name, sizeof(env_name), "emmc%d_data_in_tap_hs200", slot->bus_id); in_tap = env_get_ulong(env_name, 10, (ulong)-1); if (in_tap != (ulong)-1) { debug("%s(%s): Overriding HS200 data in tap to %d\n", __func__, dev->name, (int)in_tap); slot->hs200_taps.s.data_in_tap = in_tap; continue; } } else if (!a->hs200_only && !strcmp(a->name, "DATA_IN")) { snprintf(env_name, sizeof(env_name), "emmc%d_data_in_tap", slot->bus_id); in_tap = env_get_ulong(env_name, 10, (ulong)-1); if (in_tap != (ulong)-1) { debug("%s(%s): Overriding non-HS200 data in tap to %d\n", __func__, dev->name, (int)in_tap); slot->taps.s.data_in_tap = in_tap; continue; } } debug("%s(%s): Testing: %s, mode: %s, opcode: %u\n", __func__, dev->name, a->name, mmc_mode_name(mmc->selected_mode), opcode); /* Skip DDR only test when not in DDR mode */ if (a->ddr_only && !mmc->ddr_mode) { debug("%s(%s): Skipping %s due to non-DDR mode\n", __func__, dev->name, a->name); continue; } /* Skip hs200 tests in non-hs200 mode and * non-hs200 tests in hs200 mode */ if (is_hs200) { if (a->not_hs200_only) { debug("%s(%s): Skipping %s\n", __func__, dev->name, a->name); continue; } } else { if (a->hs200_only) { debug("%s(%s): Skipping %s\n", __func__, dev->name, a->name); continue; } } err = octeontx_mmc_adjust_tuning(mmc, a, a->opcode ? a->opcode : opcode); if (err) { pr_err("%s(%s, %u): tuning %s failed\n", __func__, dev->name, opcode, a->name); return err; } } octeontx_mmc_set_timing(mmc); if (is_hs200) slot->hs200_tuned = true; else slot->tuned = true; if (slot->hs400_tuning_block != -1) { struct mmc_cmd cmd; struct mmc_data data; u8 buffer[mmc->read_bl_len]; cmd.cmdidx = MMC_CMD_READ_SINGLE_BLOCK; cmd.cmdarg = slot->hs400_tuning_block; cmd.resp_type = MMC_RSP_R1; data.dest = (void *)buffer; data.blocks = 1; data.blocksize = mmc->read_bl_len; data.flags = MMC_DATA_READ; err = octeontx_mmc_read_blocks(mmc, &cmd, &data, true) != 1; if (err) { printf("%s: Cannot read HS400 tuning block %u\n", dev->name, slot->hs400_tuning_block); return err; } if (memcmp(buffer, octeontx_hs400_tuning_block, sizeof(buffer))) { debug("%s(%s): Writing new HS400 tuning block to block %d\n", __func__, dev->name, slot->hs400_tuning_block); cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK; data.src = (void *)octeontx_hs400_tuning_block; data.flags = MMC_DATA_WRITE; err = !octeontx_mmc_write_blocks(mmc, &cmd, &data); if (err) { printf("%s: Cannot write HS400 tuning block %u\n", dev->name, slot->hs400_tuning_block); return -EINVAL; } } } return 0; } #else /* MMC_SUPPORTS_TUNING */ static void octeontx_mmc_set_emm_timing(struct mmc *mmc, union mio_emm_timing emm_timing) { } #endif /* MMC_SUPPORTS_TUNING */ /** * Calculate the clock period with rounding up * * @param mmc mmc device * Return: clock period in system clocks for clk_lo + clk_hi */ static u32 octeontx_mmc_calc_clk_period(struct mmc *mmc) { struct octeontx_mmc_slot *slot = mmc_to_slot(mmc); struct octeontx_mmc_host *host = slot->host; if (mmc->clock) return DIV_ROUND_UP(host->sys_freq, mmc->clock); return 0; } static int octeontx_mmc_set_ios(struct udevice *dev) { struct octeontx_mmc_slot *slot = dev_to_mmc_slot(dev); struct mmc *mmc = &slot->mmc; struct octeontx_mmc_host *host = slot->host; union mio_emm_switch emm_switch; union mio_emm_modex mode; uint clock; int bus_width = 0; int clk_period = 0; int power_class = 10; int err = 0; bool is_hs200 = false; bool is_hs400 = false; debug("%s(%s): Entry\n", __func__, dev->name); debug(" clock: %u, bus width: %u, mode: %u\n", mmc->clock, mmc->bus_width, mmc->selected_mode); debug(" host caps: 0x%x, card caps: 0x%x\n", mmc->host_caps, mmc->card_caps); octeontx_mmc_switch_to(mmc); clock = mmc->clock; if (!clock) clock = mmc->cfg->f_min; switch (mmc->bus_width) { case 8: bus_width = 2; break; case 4: bus_width = 1; break; case 1: bus_width = 0; break; default: pr_warn("%s(%s): Invalid bus width %d, defaulting to 1\n", __func__, dev->name, mmc->bus_width); bus_width = 0; } /* DDR is available for 4/8 bit bus width */ if (mmc->ddr_mode && bus_width) bus_width |= 4; debug("%s: sys_freq: %llu\n", __func__, host->sys_freq); clk_period = octeontx_mmc_calc_clk_period(mmc); emm_switch.u = 0; emm_switch.s.bus_width = bus_width; emm_switch.s.power_class = power_class; emm_switch.s.clk_hi = clk_period / 2; emm_switch.s.clk_lo = clk_period / 2; debug("%s: last mode: %d, mode: %d, last clock: %u, clock: %u, ddr: %d\n", __func__, slot->last_mode, mmc->selected_mode, slot->last_clock, mmc->clock, mmc->ddr_mode); switch (mmc->selected_mode) { case MMC_LEGACY: break; case MMC_HS: case SD_HS: case MMC_HS_52: emm_switch.s.hs_timing = 1; break; case MMC_HS_200: is_hs200 = true; fallthrough; case UHS_SDR12: case UHS_SDR25: case UHS_SDR50: case UHS_SDR104: #if !defined(CONFIG_ARCH_OCTEON) emm_switch.s.hs200_timing = 1; #endif break; case MMC_HS_400: is_hs400 = true; fallthrough; case UHS_DDR50: case MMC_DDR_52: #if !defined(CONFIG_ARCH_OCTEON) emm_switch.s.hs400_timing = 1; #endif break; default: pr_err("%s(%s): Unsupported mode 0x%x\n", __func__, dev->name, mmc->selected_mode); return -1; } emm_switch.s.bus_id = slot->bus_id; if (!is_hs200 && !is_hs400 && (mmc->selected_mode != slot->last_mode || mmc->clock != slot->last_clock) && !mmc->ddr_mode) { slot->tuned = false; slot->last_mode = mmc->selected_mode; slot->last_clock = mmc->clock; } if (CONFIG_IS_ENABLED(MMC_VERBOSE)) { debug("%s(%s): Setting bus mode to %s\n", __func__, dev->name, mmc_mode_name(mmc->selected_mode)); } else { debug("%s(%s): Setting bus mode to 0x%x\n", __func__, dev->name, mmc->selected_mode); } #if !defined(CONFIG_ARCH_OCTEON) debug(" Trying switch 0x%llx w%d hs:%d hs200:%d hs400:%d\n", emm_switch.u, emm_switch.s.bus_width, emm_switch.s.hs_timing, emm_switch.s.hs200_timing, emm_switch.s.hs400_timing); #endif set_wdog(mmc, 1000); do_switch(mmc, emm_switch); mdelay(100); mode.u = read_csr(mmc, MIO_EMM_MODEX(slot->bus_id)); #if !defined(CONFIG_ARCH_OCTEON) debug("%s(%s): mode: 0x%llx w:%d, hs:%d, hs200:%d, hs400:%d\n", __func__, dev->name, mode.u, mode.s.bus_width, mode.s.hs_timing, mode.s.hs200_timing, mode.s.hs400_timing); #endif err = octeontx_mmc_configure_delay(mmc); #ifdef MMC_SUPPORTS_TUNING if (!err && mmc->selected_mode == MMC_HS_400 && !slot->hs400_tuned) { debug("%s: Tuning HS400 mode\n", __func__); err = octeontx_tune_hs400(mmc); } #endif return err; } /** * Gets the status of the card detect pin */ static int octeontx_mmc_get_cd(struct udevice *dev) { struct octeontx_mmc_slot *slot = dev_to_mmc_slot(dev); int val = 1; if (dm_gpio_is_valid(&slot->cd_gpio)) { val = dm_gpio_get_value(&slot->cd_gpio); val ^= slot->cd_inverted; } debug("%s(%s): cd: %d\n", __func__, dev->name, val); return val; } /** * Gets the status of the write protect pin */ static int octeontx_mmc_get_wp(struct udevice *dev) { struct octeontx_mmc_slot *slot = dev_to_mmc_slot(dev); int val = 0; if (dm_gpio_is_valid(&slot->wp_gpio)) { val = dm_gpio_get_value(&slot->wp_gpio); val ^= slot->wp_inverted; } debug("%s(%s): wp: %d\n", __func__, dev->name, val); return val; } #if defined(CONFIG_ARCH_OCTEON) static int octeontx_mmc_configure_delay(struct mmc *mmc) { struct octeontx_mmc_slot *slot = mmc_to_slot(mmc); union mio_emm_sample emm_sample; debug("%s(%s)\n", __func__, mmc->dev->name); emm_sample.u = 0; emm_sample.s.cmd_cnt = slot->cmd_cnt; emm_sample.s.dat_cnt = slot->dat_cnt; write_csr(mmc, MIO_EMM_SAMPLE(), emm_sample.u); return 0; } static void octeontx_mmc_io_drive_setup(struct mmc *mmc) { } #else static void octeontx_mmc_set_timing(struct mmc *mmc) { union mio_emm_timing timing; struct octeontx_mmc_slot *slot = mmc_to_slot(mmc); switch (mmc->selected_mode) { case MMC_HS_200: timing = slot->hs200_taps; break; case MMC_HS_400: timing = slot->hs400_tuned ? slot->hs400_taps : slot->hs200_taps; break; default: timing = slot->taps; break; } debug("%s(%s):\n cmd_in_tap: %u\n cmd_out_tap: %u\n data_in_tap: %u\n data_out_tap: %u\n", __func__, mmc->dev->name, timing.s.cmd_in_tap, timing.s.cmd_out_tap, timing.s.data_in_tap, timing.s.data_out_tap); octeontx_mmc_set_emm_timing(mmc, timing); } static int octeontx_mmc_configure_delay(struct mmc *mmc) { struct octeontx_mmc_slot *slot = mmc_to_slot(mmc); struct octeontx_mmc_host *host __maybe_unused = slot->host; bool __maybe_unused is_hs200; bool __maybe_unused is_hs400; debug("%s(%s)\n", __func__, mmc->dev->name); if (IS_ENABLED(CONFIG_ARCH_OCTEON) || IS_ENABLED(CONFIG_ARCH_OCTEONTX)) { union mio_emm_sample emm_sample; emm_sample.u = 0; emm_sample.s.cmd_cnt = slot->cmd_cnt; emm_sample.s.dat_cnt = slot->dat_cnt; write_csr(mmc, MIO_EMM_SAMPLE(), emm_sample.u); } else { is_hs200 = (mmc->selected_mode == MMC_HS_200); is_hs400 = (mmc->selected_mode == MMC_HS_400); if ((is_hs200 && slot->hs200_tuned) || (is_hs400 && slot->hs400_tuned) || (!is_hs200 && !is_hs400 && slot->tuned)) { octeontx_mmc_set_output_bus_timing(mmc); } else { int half = MAX_NO_OF_TAPS / 2; int dout, cout; switch (mmc->selected_mode) { case MMC_LEGACY: if (IS_SD(mmc)) { cout = MMC_SD_LEGACY_DEFAULT_CMD_OUT_TAP; dout = MMC_SD_LEGACY_DEFAULT_DATA_OUT_TAP; } else { cout = MMC_LEGACY_DEFAULT_CMD_OUT_TAP; dout = MMC_LEGACY_DEFAULT_DATA_OUT_TAP; } break; case MMC_HS: cout = MMC_HS_CMD_OUT_TAP; dout = MMC_HS_DATA_OUT_TAP; break; case SD_HS: case UHS_SDR12: case UHS_SDR25: case UHS_SDR50: cout = MMC_SD_HS_CMD_OUT_TAP; dout = MMC_SD_HS_DATA_OUT_TAP; break; case UHS_SDR104: case UHS_DDR50: case MMC_HS_52: case MMC_DDR_52: cout = MMC_DEFAULT_CMD_OUT_TAP; dout = MMC_DEFAULT_DATA_OUT_TAP; break; case MMC_HS_200: cout = -1; dout = -1; if (host->timing_calibrated) { cout = octeontx2_mmc_calc_delay( mmc, slot->cmd_out_hs200_delay); dout = octeontx2_mmc_calc_delay( mmc, slot->data_out_hs200_delay); debug("%s(%s): Calibrated HS200/HS400 cmd out delay: %dps tap: %d, data out delay: %d, tap: %d\n", __func__, mmc->dev->name, slot->cmd_out_hs200_delay, cout, slot->data_out_hs200_delay, dout); } else { cout = MMC_DEFAULT_HS200_CMD_OUT_TAP; dout = MMC_DEFAULT_HS200_DATA_OUT_TAP; } is_hs200 = true; break; case MMC_HS_400: cout = -1; dout = -1; if (host->timing_calibrated) { if (slot->cmd_out_hs400_delay) cout = octeontx2_mmc_calc_delay( mmc, slot->cmd_out_hs400_delay); if (slot->data_out_hs400_delay) dout = octeontx2_mmc_calc_delay( mmc, slot->data_out_hs400_delay); debug("%s(%s): Calibrated HS200/HS400 cmd out delay: %dps tap: %d, data out delay: %d, tap: %d\n", __func__, mmc->dev->name, slot->cmd_out_hs400_delay, cout, slot->data_out_hs400_delay, dout); } else { cout = MMC_DEFAULT_HS400_CMD_OUT_TAP; dout = MMC_DEFAULT_HS400_DATA_OUT_TAP; } is_hs400 = true; break; default: pr_err("%s(%s): Invalid mode %d\n", __func__, mmc->dev->name, mmc->selected_mode); return -1; } debug("%s(%s): Not tuned, hs200: %d, hs200 tuned: %d, hs400: %d, hs400 tuned: %d, tuned: %d\n", __func__, mmc->dev->name, is_hs200, slot->hs200_tuned, is_hs400, slot->hs400_tuned, slot->tuned); /* Set some defaults */ if (is_hs200) { slot->hs200_taps.u = 0; slot->hs200_taps.s.cmd_out_tap = cout; slot->hs200_taps.s.data_out_tap = dout; slot->hs200_taps.s.cmd_in_tap = half; slot->hs200_taps.s.data_in_tap = half; } else if (is_hs400) { slot->hs400_taps.u = 0; slot->hs400_taps.s.cmd_out_tap = cout; slot->hs400_taps.s.data_out_tap = dout; slot->hs400_taps.s.cmd_in_tap = half; slot->hs400_taps.s.data_in_tap = half; } else { slot->taps.u = 0; slot->taps.s.cmd_out_tap = cout; slot->taps.s.data_out_tap = dout; slot->taps.s.cmd_in_tap = half; slot->taps.s.data_in_tap = half; } } if (is_hs200) debug("%s(%s): hs200 taps: ci: %u, co: %u, di: %u, do: %u\n", __func__, mmc->dev->name, slot->hs200_taps.s.cmd_in_tap, slot->hs200_taps.s.cmd_out_tap, slot->hs200_taps.s.data_in_tap, slot->hs200_taps.s.data_out_tap); else if (is_hs400) debug("%s(%s): hs400 taps: ci: %u, co: %u, di: %u, do: %u\n", __func__, mmc->dev->name, slot->hs400_taps.s.cmd_in_tap, slot->hs400_taps.s.cmd_out_tap, slot->hs400_taps.s.data_in_tap, slot->hs400_taps.s.data_out_tap); else debug("%s(%s): taps: ci: %u, co: %u, di: %u, do: %u\n", __func__, mmc->dev->name, slot->taps.s.cmd_in_tap, slot->taps.s.cmd_out_tap, slot->taps.s.data_in_tap, slot->taps.s.data_out_tap); octeontx_mmc_set_timing(mmc); debug("%s: Done\n", __func__); } return 0; } /** * Set the IO drive strength and slew * * @param mmc mmc device */ static void octeontx_mmc_io_drive_setup(struct mmc *mmc) { if (IS_ENABLED(CONFIG_ARCH_OCTEONTX2)) { struct octeontx_mmc_slot *slot = mmc_to_slot(mmc); union mio_emm_io_ctl io_ctl; if (slot->drive < 0 || slot->slew < 0) return; io_ctl.u = 0; io_ctl.s.drive = slot->drive; io_ctl.s.slew = slot->slew; write_csr(mmc, MIO_EMM_IO_CTL(), io_ctl.u); } } #endif /** * Sets the MMC watchdog timer in microseconds * * @param mmc mmc device * @param us timeout in microseconds, 0 for maximum timeout */ static void set_wdog(struct mmc *mmc, u64 us) { union mio_emm_wdog wdog; u64 val; val = (us * mmc->clock) / 1000000; if (val >= (1 << 26) || !us) { if (us) pr_debug("%s: warning: timeout %llu exceeds max value %llu, truncating\n", __func__, us, (u64)(((1ULL << 26) - 1) * 1000000ULL) / mmc->clock); val = (1 << 26) - 1; } wdog.u = 0; wdog.s.clk_cnt = val; write_csr(mmc, MIO_EMM_WDOG(), wdog.u); } /** * Print switch errors * * @param mmc mmc device */ static void check_switch_errors(struct mmc *mmc) { union mio_emm_switch emm_switch; emm_switch.u = read_csr(mmc, MIO_EMM_SWITCH()); if (emm_switch.s.switch_err0) pr_err("%s: Switch power class error\n", mmc->cfg->name); if (emm_switch.s.switch_err1) pr_err("%s: Switch HS timing error\n", mmc->cfg->name); if (emm_switch.s.switch_err2) pr_err("%s: Switch bus width error\n", mmc->cfg->name); } static void do_switch(struct mmc *mmc, union mio_emm_switch emm_switch) { union mio_emm_rsp_sts rsp_sts; struct octeontx_mmc_slot *slot = mmc_to_slot(mmc); int bus_id = emm_switch.s.bus_id; ulong start; if (emm_switch.s.bus_id != 0) { emm_switch.s.bus_id = 0; write_csr(mmc, MIO_EMM_SWITCH(), emm_switch.u); udelay(100); emm_switch.s.bus_id = bus_id; } debug("%s(%s, 0x%llx)\n", __func__, mmc->dev->name, emm_switch.u); write_csr(mmc, MIO_EMM_SWITCH(), emm_switch.u); start = get_timer(0); do { rsp_sts.u = read_csr(mmc, MIO_EMM_RSP_STS()); if (!rsp_sts.s.switch_val) break; udelay(100); } while (get_timer(start) < 10); if (rsp_sts.s.switch_val) { pr_warn("%s(%s): Warning: writing 0x%llx to emm_switch timed out, status: 0x%llx\n", __func__, mmc->dev->name, emm_switch.u, rsp_sts.u); } slot->cached_switch = emm_switch; check_switch_errors(mmc); slot->cached_switch.u = emm_switch.u; debug("%s: emm_switch: 0x%llx, rsp_lo: 0x%llx\n", __func__, read_csr(mmc, MIO_EMM_SWITCH()), read_csr(mmc, MIO_EMM_RSP_LO())); } /** * Calibrates the delay based on the internal clock * * @param mmc Pointer to mmc data structure * * Return: 0 for success or -ETIMEDOUT on error * * NOTE: On error a default value will be calculated. */ #if defined(CONFIG_ARCH_OCTEON) static int octeontx_mmc_set_input_bus_timing(struct mmc *mmc) { return 0; } static int octeontx_mmc_set_output_bus_timing(struct mmc *mmc) { return 0; } static int octeontx_mmc_calibrate_delay(struct mmc *mmc) { return 0; } #else /** * Given a delay in ps, return the tap delay count * * @param mmc mmc data structure * @param delay delay in picoseconds * * Return: Number of tap cycles or error if -1 */ static int octeontx2_mmc_calc_delay(struct mmc *mmc, int delay) { struct octeontx_mmc_host *host = mmc_to_host(mmc); if (host->is_asim || host->is_emul) return 63; if (!host->timing_taps) { pr_err("%s(%s): Error: host timing not calibrated\n", __func__, mmc->dev->name); return -1; } debug("%s(%s, %d) timing taps: %llu\n", __func__, mmc->dev->name, delay, host->timing_taps); return min_t(int, DIV_ROUND_UP(delay, host->timing_taps), 63); } static int octeontx_mmc_calibrate_delay(struct mmc *mmc) { union mio_emm_calb emm_calb; union mio_emm_tap emm_tap; union mio_emm_cfg emm_cfg; union mio_emm_io_ctl emm_io_ctl; union mio_emm_switch emm_switch; union mio_emm_wdog emm_wdog; union mio_emm_sts_mask emm_sts_mask; union mio_emm_debug emm_debug; union mio_emm_timing emm_timing; struct octeontx_mmc_host *host = mmc_to_host(mmc); ulong start; u8 bus_id, bus_ena; debug("%s: Calibrating delay\n", __func__); if (host->is_asim || host->is_emul) { debug(" No calibration for ASIM\n"); return 0; } emm_tap.u = 0; if (host->calibrate_glitch) { emm_tap.s.delay = MMC_DEFAULT_TAP_DELAY; } else { /* Save registers */ emm_cfg.u = read_csr(mmc, MIO_EMM_CFG()); emm_io_ctl.u = read_csr(mmc, MIO_EMM_IO_CTL()); emm_switch.u = read_csr(mmc, MIO_EMM_SWITCH()); emm_wdog.u = read_csr(mmc, MIO_EMM_WDOG()); emm_sts_mask.u = read_csr(mmc, MIO_EMM_STS_MASK()); emm_debug.u = read_csr(mmc, MIO_EMM_DEBUG()); emm_timing.u = read_csr(mmc, MIO_EMM_TIMING()); bus_ena = emm_cfg.s.bus_ena; bus_id = emm_switch.s.bus_id; emm_cfg.s.bus_ena = 0; write_csr(mmc, MIO_EMM_CFG(), emm_cfg.u); udelay(1); emm_cfg.s.bus_ena = 1ULL << 3; write_csr(mmc, MIO_EMM_CFG(), emm_cfg.u); mdelay(1); emm_calb.u = 0; write_csr(mmc, MIO_EMM_CALB(), emm_calb.u); emm_calb.s.start = 1; write_csr(mmc, MIO_EMM_CALB(), emm_calb.u); start = get_timer(0); /* This should only take 3 microseconds */ do { udelay(5); emm_tap.u = read_csr(mmc, MIO_EMM_TAP()); } while (!emm_tap.s.delay && get_timer(start) < 10); emm_calb.s.start = 0; write_csr(mmc, MIO_EMM_CALB(), emm_calb.u); emm_cfg.s.bus_ena = 0; write_csr(mmc, MIO_EMM_CFG(), emm_cfg.u); udelay(1); /* Restore registers */ emm_cfg.s.bus_ena = bus_ena; write_csr(mmc, MIO_EMM_CFG(), emm_cfg.u); if (host->tap_requires_noclk) { /* Turn off the clock */ emm_debug.u = read_csr(mmc, MIO_EMM_DEBUG()); emm_debug.s.emmc_clk_disable = 1; write_csr(mmc, MIO_EMM_DEBUG(), emm_debug.u); udelay(1); emm_debug.s.rdsync_rst = 1; write_csr(mmc, MIO_EMM_DEBUG(), emm_debug.u); udelay(1); } write_csr(mmc, MIO_EMM_TIMING(), emm_timing.u); if (host->tap_requires_noclk) { /* Turn the clock back on */ udelay(1); emm_debug.s.rdsync_rst = 0; write_csr(mmc, MIO_EMM_DEBUG(), emm_debug.u); udelay(1); emm_debug.s.emmc_clk_disable = 0; write_csr(mmc, MIO_EMM_DEBUG(), emm_debug.u); } udelay(1); write_csr(mmc, MIO_EMM_IO_CTL(), emm_io_ctl.u); bus_id = emm_switch.s.bus_id; emm_switch.s.bus_id = 0; write_csr(mmc, MIO_EMM_SWITCH(), emm_switch.u); emm_switch.s.bus_id = bus_id; write_csr(mmc, MIO_EMM_SWITCH(), emm_switch.u); write_csr(mmc, MIO_EMM_WDOG(), emm_wdog.u); write_csr(mmc, MIO_EMM_STS_MASK(), emm_sts_mask.u); write_csr(mmc, MIO_EMM_RCA(), mmc->rca); write_csr(mmc, MIO_EMM_DEBUG(), emm_debug.u); if (!emm_tap.s.delay) { pr_err("%s: Error: delay calibration failed, timed out.\n", __func__); /* Set to default value if timed out */ emm_tap.s.delay = MMC_DEFAULT_TAP_DELAY; return -ETIMEDOUT; } } /* Round up */ host->timing_taps = (10 * 1000 * emm_tap.s.delay) / TOTAL_NO_OF_TAPS; debug("%s(%s): timing taps: %llu, delay: %u\n", __func__, mmc->dev->name, host->timing_taps, emm_tap.s.delay); host->timing_calibrated = true; return 0; } static int octeontx_mmc_set_input_bus_timing(struct mmc *mmc) { struct octeontx_mmc_slot *slot = mmc_to_slot(mmc); if (IS_ENABLED(CONFIG_ARCH_OCTEONTX)) { union mio_emm_sample sample; sample.u = 0; sample.s.cmd_cnt = slot->cmd_clk_skew; sample.s.dat_cnt = slot->dat_clk_skew; write_csr(mmc, MIO_EMM_SAMPLE(), sample.u); } else { union mio_emm_timing timing; timing.u = read_csr(mmc, MIO_EMM_TIMING()); if (mmc->selected_mode == MMC_HS_200) { if (slot->hs200_tuned) { timing.s.cmd_in_tap = slot->hs200_taps.s.cmd_in_tap; timing.s.data_in_tap = slot->hs200_taps.s.data_in_tap; } else { pr_warn("%s(%s): Warning: hs200 timing not tuned\n", __func__, mmc->dev->name); timing.s.cmd_in_tap = MMC_DEFAULT_HS200_CMD_IN_TAP; timing.s.data_in_tap = MMC_DEFAULT_HS200_DATA_IN_TAP; } } else if (mmc->selected_mode == MMC_HS_400) { if (slot->hs400_tuned) { timing.s.cmd_in_tap = slot->hs400_taps.s.cmd_in_tap; timing.s.data_in_tap = slot->hs400_taps.s.data_in_tap; } else if (slot->hs200_tuned) { timing.s.cmd_in_tap = slot->hs200_taps.s.cmd_in_tap; timing.s.data_in_tap = slot->hs200_taps.s.data_in_tap; } else { pr_warn("%s(%s): Warning: hs400 timing not tuned\n", __func__, mmc->dev->name); timing.s.cmd_in_tap = MMC_DEFAULT_HS200_CMD_IN_TAP; timing.s.data_in_tap = MMC_DEFAULT_HS200_DATA_IN_TAP; } } else if (slot->tuned) { timing.s.cmd_in_tap = slot->taps.s.cmd_in_tap; timing.s.data_in_tap = slot->taps.s.data_in_tap; } else { timing.s.cmd_in_tap = MMC_DEFAULT_CMD_IN_TAP; timing.s.data_in_tap = MMC_DEFAULT_DATA_IN_TAP; } octeontx_mmc_set_emm_timing(mmc, timing); } return 0; } /** * Sets the default bus timing for the current mode. * * @param mmc mmc data structure * * Return: 0 for success, error otherwise */ static int octeontx_mmc_set_output_bus_timing(struct mmc *mmc) { struct octeontx_mmc_slot *slot = mmc_to_slot(mmc); union mio_emm_timing timing; int cout_bdelay, dout_bdelay; unsigned int cout_delay, dout_delay; char env_name[32]; if (IS_ENABLED(CONFIG_ARCH_OCTEONTX)) return 0; debug("%s(%s)\n", __func__, mmc->dev->name); if (slot->is_asim || slot->is_emul) return 0; octeontx_mmc_calibrate_delay(mmc); if (mmc->clock < 26000000) { cout_delay = 5000; dout_delay = 5000; } else if (mmc->clock <= 52000000) { cout_delay = 2500; dout_delay = 2500; } else if (!mmc_is_mode_ddr(mmc->selected_mode)) { cout_delay = slot->cmd_out_hs200_delay; dout_delay = slot->data_out_hs200_delay; } else { cout_delay = slot->cmd_out_hs400_delay; dout_delay = slot->data_out_hs400_delay; } snprintf(env_name, sizeof(env_name), "mmc%d_hs200_dout_delay_ps", slot->bus_id); dout_delay = env_get_ulong(env_name, 10, dout_delay); debug("%s: dout_delay: %u\n", __func__, dout_delay); cout_bdelay = octeontx2_mmc_calc_delay(mmc, cout_delay); dout_bdelay = octeontx2_mmc_calc_delay(mmc, dout_delay); debug("%s: cmd output delay: %u, data output delay: %u, cmd bdelay: %d, data bdelay: %d, clock: %d\n", __func__, cout_delay, dout_delay, cout_bdelay, dout_bdelay, mmc->clock); if (cout_bdelay < 0 || dout_bdelay < 0) { pr_err("%s: Error: could not calculate command and/or data clock skew\n", __func__); return -1; } timing.u = read_csr(mmc, MIO_EMM_TIMING()); timing.s.cmd_out_tap = cout_bdelay; timing.s.data_out_tap = dout_bdelay; if (mmc->selected_mode == MMC_HS_200) { slot->hs200_taps.s.cmd_out_tap = cout_bdelay; slot->hs200_taps.s.data_out_tap = dout_bdelay; } else if (mmc->selected_mode == MMC_HS_400) { slot->hs400_taps.s.cmd_out_tap = cout_bdelay; slot->hs400_taps.s.data_out_tap = dout_bdelay; } else { slot->taps.s.cmd_out_tap = cout_bdelay; slot->taps.s.data_out_tap = dout_bdelay; } octeontx_mmc_set_emm_timing(mmc, timing); debug("%s(%s): bdelay: %d/%d, clock: %d, ddr: %s, timing taps: %llu, do: %d, di: %d, co: %d, ci: %d\n", __func__, mmc->dev->name, cout_bdelay, dout_bdelay, mmc->clock, mmc->ddr_mode ? "yes" : "no", mmc_to_host(mmc)->timing_taps, timing.s.data_out_tap, timing.s.data_in_tap, timing.s.cmd_out_tap, timing.s.cmd_in_tap); return 0; } #endif static void octeontx_mmc_set_clock(struct mmc *mmc) { struct octeontx_mmc_slot *slot = mmc_to_slot(mmc); uint clock; clock = min(mmc->cfg->f_max, (uint)slot->clock); clock = max(mmc->cfg->f_min, clock); debug("%s(%s): f_min: %u, f_max: %u, clock: %u\n", __func__, mmc->dev->name, mmc->cfg->f_min, mmc->cfg->f_max, clock); slot->clock = clock; mmc->clock = clock; } /** * This switches I/O power as needed when switching between slots. * * @param mmc mmc data structure */ static void octeontx_mmc_switch_io(struct mmc *mmc) { struct octeontx_mmc_slot *slot = mmc_to_slot(mmc); struct octeontx_mmc_host *host = slot->host; struct mmc *last_mmc = host->last_mmc; static struct udevice *last_reg; union mio_emm_cfg emm_cfg; int bus; static bool initialized; /* First time? */ if (!initialized || mmc != host->last_mmc) { struct mmc *ommc; /* Switch to bus 3 which is unused */ emm_cfg.u = read_csr(mmc, MIO_EMM_CFG()); emm_cfg.s.bus_ena = 1 << 3; write_csr(mmc, MIO_EMM_CFG(), emm_cfg.u); /* Turn off all other I/O interfaces with first initialization * if at least one supply was found. */ for (bus = 0; bus <= OCTEONTX_MAX_MMC_SLOT; bus++) { ommc = &host->slots[bus].mmc; /* Handle self case later */ if (ommc == mmc || !ommc->vqmmc_supply) continue; /* Skip if we're not switching regulators */ if (last_reg == mmc->vqmmc_supply) continue; /* Turn off other regulators */ if (ommc->vqmmc_supply != mmc->vqmmc_supply) regulator_set_enable(ommc->vqmmc_supply, false); } /* Turn ourself on */ if (mmc->vqmmc_supply && last_reg != mmc->vqmmc_supply) regulator_set_enable(mmc->vqmmc_supply, true); mdelay(1); /* Settle time */ /* Switch to new bus */ emm_cfg.s.bus_ena = 1 << slot->bus_id; write_csr(mmc, MIO_EMM_CFG(), emm_cfg.u); last_reg = mmc->vqmmc_supply; initialized = true; return; } /* No change in device */ if (last_mmc == mmc) return; if (!last_mmc) { pr_warn("%s(%s): No previous slot detected in IO slot switch!\n", __func__, mmc->dev->name); return; } debug("%s(%s): last: %s, supply: %p\n", __func__, mmc->dev->name, last_mmc->dev->name, mmc->vqmmc_supply); /* The supply is the same so we do nothing */ if (last_mmc->vqmmc_supply == mmc->vqmmc_supply) return; /* Turn off the old slot I/O supply */ if (last_mmc->vqmmc_supply) { debug("%s(%s): Turning off IO to %s, supply: %s\n", __func__, mmc->dev->name, last_mmc->dev->name, last_mmc->vqmmc_supply->name); regulator_set_enable(last_mmc->vqmmc_supply, false); } /* Turn on the new slot I/O supply */ if (mmc->vqmmc_supply) { debug("%s(%s): Turning on IO to slot %d, supply: %s\n", __func__, mmc->dev->name, slot->bus_id, mmc->vqmmc_supply->name); regulator_set_enable(mmc->vqmmc_supply, true); } /* Allow power to settle */ mdelay(1); } /** * Called to switch between mmc devices * * @param mmc new mmc device */ static void octeontx_mmc_switch_to(struct mmc *mmc) { struct octeontx_mmc_slot *slot = mmc_to_slot(mmc); struct octeontx_mmc_slot *old_slot; struct octeontx_mmc_host *host = slot->host; union mio_emm_switch emm_switch; union mio_emm_sts_mask emm_sts_mask; union mio_emm_rca emm_rca; if (slot->bus_id == host->last_slotid) return; debug("%s(%s) switching from slot %d to slot %d\n", __func__, mmc->dev->name, host->last_slotid, slot->bus_id); octeontx_mmc_switch_io(mmc); if (host->last_slotid >= 0 && slot->valid) { old_slot = &host->slots[host->last_slotid]; old_slot->cached_switch.u = read_csr(mmc, MIO_EMM_SWITCH()); old_slot->cached_rca.u = read_csr(mmc, MIO_EMM_RCA()); } if (mmc->rca) write_csr(mmc, MIO_EMM_RCA(), mmc->rca); emm_switch = slot->cached_switch; do_switch(mmc, emm_switch); emm_rca.u = 0; emm_rca.s.card_rca = mmc->rca; write_csr(mmc, MIO_EMM_RCA(), emm_rca.u); mdelay(100); set_wdog(mmc, 100000); if (octeontx_mmc_set_output_bus_timing(mmc) || octeontx_mmc_set_input_bus_timing(mmc)) pr_err("%s(%s): Error setting bus timing\n", __func__, mmc->dev->name); octeontx_mmc_io_drive_setup(mmc); emm_sts_mask.u = 0; emm_sts_mask.s.sts_msk = 1 << 7 | 1 << 22 | 1 << 23 | 1 << 19; write_csr(mmc, MIO_EMM_STS_MASK(), emm_sts_mask.u); host->last_slotid = slot->bus_id; host->last_mmc = mmc; mdelay(10); } /** * Perform initial timing configuration * * @param mmc mmc device * * Return: 0 for success * * NOTE: This will need to be updated when new silicon comes out */ static int octeontx_mmc_init_timing(struct mmc *mmc) { union mio_emm_timing timing; if (mmc_to_slot(mmc)->is_asim || mmc_to_slot(mmc)->is_emul) return 0; debug("%s(%s)\n", __func__, mmc->dev->name); timing.u = 0; timing.s.cmd_out_tap = MMC_DEFAULT_CMD_OUT_TAP; timing.s.data_out_tap = MMC_DEFAULT_DATA_OUT_TAP; timing.s.cmd_in_tap = MMC_DEFAULT_CMD_IN_TAP; timing.s.data_in_tap = MMC_DEFAULT_DATA_IN_TAP; octeontx_mmc_set_emm_timing(mmc, timing); return 0; } /** * Perform low-level initialization * * @param mmc mmc device * * Return: 0 for success, error otherwise */ static int octeontx_mmc_init_lowlevel(struct mmc *mmc) { struct octeontx_mmc_slot *slot = mmc_to_slot(mmc); struct octeontx_mmc_host *host = slot->host; union mio_emm_switch emm_switch; u32 clk_period; debug("%s(%s): lowlevel init for slot %d\n", __func__, mmc->dev->name, slot->bus_id); host->emm_cfg.s.bus_ena &= ~(1 << slot->bus_id); write_csr(mmc, MIO_EMM_CFG(), host->emm_cfg.u); udelay(100); host->emm_cfg.s.bus_ena |= 1 << slot->bus_id; write_csr(mmc, MIO_EMM_CFG(), host->emm_cfg.u); udelay(10); slot->clock = mmc->cfg->f_min; octeontx_mmc_set_clock(&slot->mmc); if (IS_ENABLED(CONFIG_ARCH_OCTEONTX2)) { if (host->cond_clock_glitch) { union mio_emm_debug emm_debug; emm_debug.u = read_csr(mmc, MIO_EMM_DEBUG()); emm_debug.s.clk_on = 1; write_csr(mmc, MIO_EMM_DEBUG(), emm_debug.u); } octeontx_mmc_calibrate_delay(&slot->mmc); } clk_period = octeontx_mmc_calc_clk_period(mmc); emm_switch.u = 0; emm_switch.s.power_class = 10; emm_switch.s.clk_lo = clk_period / 2; emm_switch.s.clk_hi = clk_period / 2; emm_switch.s.bus_id = slot->bus_id; debug("%s: Performing switch\n", __func__); do_switch(mmc, emm_switch); slot->cached_switch.u = emm_switch.u; if (!IS_ENABLED(CONFIG_ARCH_OCTEONTX)) octeontx_mmc_init_timing(mmc); set_wdog(mmc, 1000000); /* Set to 1 second */ write_csr(mmc, MIO_EMM_STS_MASK(), 0xe4390080ull); write_csr(mmc, MIO_EMM_RCA(), 1); mdelay(10); debug("%s: done\n", __func__); return 0; } /** * Translates a voltage number to bits in MMC register * * @param voltage voltage in microvolts * * Return: MMC register value for voltage */ static u32 xlate_voltage(u32 voltage) { u32 volt = 0; /* Convert to millivolts. Only necessary on ARM Octeon TX/TX2 */ if (!IS_ENABLED(CONFIG_ARCH_OCTEON)) voltage /= 1000; if (voltage >= 1650 && voltage <= 1950) volt |= MMC_VDD_165_195; if (voltage >= 2000 && voltage <= 2100) volt |= MMC_VDD_20_21; if (voltage >= 2100 && voltage <= 2200) volt |= MMC_VDD_21_22; if (voltage >= 2200 && voltage <= 2300) volt |= MMC_VDD_22_23; if (voltage >= 2300 && voltage <= 2400) volt |= MMC_VDD_23_24; if (voltage >= 2400 && voltage <= 2500) volt |= MMC_VDD_24_25; if (voltage >= 2500 && voltage <= 2600) volt |= MMC_VDD_25_26; if (voltage >= 2600 && voltage <= 2700) volt |= MMC_VDD_26_27; if (voltage >= 2700 && voltage <= 2800) volt |= MMC_VDD_27_28; if (voltage >= 2800 && voltage <= 2900) volt |= MMC_VDD_28_29; if (voltage >= 2900 && voltage <= 3000) volt |= MMC_VDD_29_30; if (voltage >= 3000 && voltage <= 3100) volt |= MMC_VDD_30_31; if (voltage >= 3100 && voltage <= 3200) volt |= MMC_VDD_31_32; if (voltage >= 3200 && voltage <= 3300) volt |= MMC_VDD_32_33; if (voltage >= 3300 && voltage <= 3400) volt |= MMC_VDD_33_34; if (voltage >= 3400 && voltage <= 3500) volt |= MMC_VDD_34_35; if (voltage >= 3500 && voltage <= 3600) volt |= MMC_VDD_35_36; return volt; } /** * Check if a slot is valid in the device tree * * @param dev slot device to check * * Return: true if status reports "ok" or "okay" or if no status, * false otherwise. */ static bool octeontx_mmc_get_valid(struct udevice *dev) { const char *stat = ofnode_read_string(dev_ofnode(dev), "status"); if (!stat || !strncmp(stat, "ok", 2)) return true; else return false; } /** * Reads slot configuration from the device tree * * @param dev slot device * * Return: 0 on success, otherwise error */ static int octeontx_mmc_get_config(struct udevice *dev) { struct octeontx_mmc_slot *slot = dev_to_mmc_slot(dev); uint voltages[2]; uint low, high; char env_name[32]; int err; ofnode node = dev_ofnode(dev); int bus_width = 1; ulong new_max_freq; debug("%s(%s)", __func__, dev->name); slot->cfg.name = dev->name; slot->cfg.f_max = ofnode_read_s32_default(dev_ofnode(dev), "max-frequency", 26000000); snprintf(env_name, sizeof(env_name), "mmc_max_frequency%d", slot->bus_id); new_max_freq = env_get_ulong(env_name, 10, slot->cfg.f_max); debug("Reading %s, got %lu\n", env_name, new_max_freq); if (new_max_freq != slot->cfg.f_max) { printf("Overriding device tree MMC maximum frequency %u to %lu\n", slot->cfg.f_max, new_max_freq); slot->cfg.f_max = new_max_freq; } slot->cfg.f_min = 400000; slot->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; if (IS_ENABLED(CONFIG_ARCH_OCTEONTX2)) { slot->hs400_tuning_block = ofnode_read_s32_default(dev_ofnode(dev), "marvell,hs400-tuning-block", -1); debug("%s(%s): mmc HS400 tuning block: %d\n", __func__, dev->name, slot->hs400_tuning_block); slot->hs200_tap_adj = ofnode_read_s32_default(dev_ofnode(dev), "marvell,hs200-tap-adjust", 0); debug("%s(%s): hs200-tap-adjust: %d\n", __func__, dev->name, slot->hs200_tap_adj); slot->hs400_tap_adj = ofnode_read_s32_default(dev_ofnode(dev), "marvell,hs400-tap-adjust", 0); debug("%s(%s): hs400-tap-adjust: %d\n", __func__, dev->name, slot->hs400_tap_adj); } err = ofnode_read_u32_array(dev_ofnode(dev), "voltage-ranges", voltages, 2); if (err) { slot->cfg.voltages = MMC_VDD_32_33 | MMC_VDD_33_34; } else { low = xlate_voltage(voltages[0]); high = xlate_voltage(voltages[1]); debug(" low voltage: 0x%x (%u), high: 0x%x (%u)\n", low, voltages[0], high, voltages[1]); if (low > high || !low || !high) { pr_err("Invalid MMC voltage range [%u-%u] specified for %s\n", low, high, dev->name); return -1; } slot->cfg.voltages = 0; do { slot->cfg.voltages |= low; low <<= 1; } while (low <= high); } debug("%s: config voltages: 0x%x\n", __func__, slot->cfg.voltages); slot->slew = ofnode_read_s32_default(node, "cavium,clk-slew", -1); slot->drive = ofnode_read_s32_default(node, "cavium,drv-strength", -1); gpio_request_by_name(dev, "cd-gpios", 0, &slot->cd_gpio, GPIOD_IS_IN); slot->cd_inverted = ofnode_read_bool(node, "cd-inverted"); gpio_request_by_name(dev, "wp-gpios", 0, &slot->wp_gpio, GPIOD_IS_IN); slot->wp_inverted = ofnode_read_bool(node, "wp-inverted"); if (slot->cfg.voltages & MMC_VDD_165_195) { slot->is_1_8v = true; slot->is_3_3v = false; } else if (slot->cfg.voltages & (MMC_VDD_30_31 | MMC_VDD_31_32 | MMC_VDD_33_34 | MMC_VDD_34_35 | MMC_VDD_35_36)) { slot->is_1_8v = false; slot->is_3_3v = true; } bus_width = ofnode_read_u32_default(node, "bus-width", 1); /* Note fall-through */ switch (bus_width) { case 8: slot->cfg.host_caps |= MMC_MODE_8BIT; case 4: slot->cfg.host_caps |= MMC_MODE_4BIT; case 1: slot->cfg.host_caps |= MMC_MODE_1BIT; break; } if (ofnode_read_bool(node, "no-1-8-v")) { slot->is_3_3v = true; slot->is_1_8v = false; if (!(slot->cfg.voltages & (MMC_VDD_32_33 | MMC_VDD_33_34))) pr_warn("%s(%s): voltages indicate 3.3v but 3.3v not supported\n", __func__, dev->name); } if (ofnode_read_bool(node, "mmc-ddr-3-3v")) { slot->is_3_3v = true; slot->is_1_8v = false; if (!(slot->cfg.voltages & (MMC_VDD_32_33 | MMC_VDD_33_34))) pr_warn("%s(%s): voltages indicate 3.3v but 3.3v not supported\n", __func__, dev->name); } if (ofnode_read_bool(node, "cap-sd-highspeed") || ofnode_read_bool(node, "cap-mmc-highspeed") || ofnode_read_bool(node, "sd-uhs-sdr25")) slot->cfg.host_caps |= MMC_MODE_HS; if (slot->cfg.f_max >= 50000000 && slot->cfg.host_caps & MMC_MODE_HS) slot->cfg.host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; if (ofnode_read_bool(node, "sd-uhs-sdr50")) slot->cfg.host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; if (ofnode_read_bool(node, "sd-uhs-ddr50")) slot->cfg.host_caps |= MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_DDR_52MHz; if (IS_ENABLED(CONFIG_ARCH_OCTEONTX2)) { if (!slot->is_asim && !slot->is_emul) { if (ofnode_read_bool(node, "mmc-hs200-1_8v")) slot->cfg.host_caps |= MMC_MODE_HS200 | MMC_MODE_HS_52MHz; if (ofnode_read_bool(node, "mmc-hs400-1_8v")) slot->cfg.host_caps |= MMC_MODE_HS400 | MMC_MODE_HS_52MHz | MMC_MODE_HS200 | MMC_MODE_DDR_52MHz; slot->cmd_out_hs200_delay = ofnode_read_u32_default(node, "marvell,cmd-out-hs200-dly", MMC_DEFAULT_HS200_CMD_OUT_DLY); debug("%s(%s): HS200 cmd out delay: %d\n", __func__, dev->name, slot->cmd_out_hs200_delay); slot->data_out_hs200_delay = ofnode_read_u32_default(node, "marvell,data-out-hs200-dly", MMC_DEFAULT_HS200_DATA_OUT_DLY); debug("%s(%s): HS200 data out delay: %d\n", __func__, dev->name, slot->data_out_hs200_delay); slot->cmd_out_hs400_delay = ofnode_read_u32_default(node, "marvell,cmd-out-hs400-dly", MMC_DEFAULT_HS400_CMD_OUT_DLY); debug("%s(%s): HS400 cmd out delay: %d\n", __func__, dev->name, slot->cmd_out_hs400_delay); slot->data_out_hs400_delay = ofnode_read_u32_default(node, "marvell,data-out-hs400-dly", MMC_DEFAULT_HS400_DATA_OUT_DLY); debug("%s(%s): HS400 data out delay: %d\n", __func__, dev->name, slot->data_out_hs400_delay); } } slot->disable_ddr = ofnode_read_bool(node, "marvell,disable-ddr"); slot->non_removable = ofnode_read_bool(node, "non-removable"); slot->cmd_clk_skew = ofnode_read_u32_default(node, "cavium,cmd-clk-skew", 0); slot->dat_clk_skew = ofnode_read_u32_default(node, "cavium,dat-clk-skew", 0); debug("%s(%s): host caps: 0x%x\n", __func__, dev->name, slot->cfg.host_caps); return 0; } /** * Probes a MMC slot * * @param dev mmc device * * Return: 0 for success, error otherwise */ static int octeontx_mmc_slot_probe(struct udevice *dev) { struct octeontx_mmc_slot *slot; struct mmc *mmc; int err; debug("%s(%s)\n", __func__, dev->name); if (!host_probed) { pr_err("%s(%s): Error: host not probed yet\n", __func__, dev->name); } slot = dev_to_mmc_slot(dev); mmc = &slot->mmc; mmc->dev = dev; slot->valid = false; if (!octeontx_mmc_get_valid(dev)) { debug("%s(%s): slot is invalid\n", __func__, dev->name); return -ENODEV; } debug("%s(%s): Getting config\n", __func__, dev->name); err = octeontx_mmc_get_config(dev); if (err) { pr_err("probe(%s): Error getting config\n", dev->name); return err; } debug("%s(%s): mmc bind, mmc: %p\n", __func__, dev->name, &slot->mmc); err = mmc_bind(dev, &slot->mmc, &slot->cfg); if (err) { pr_err("%s(%s): Error binding mmc\n", __func__, dev->name); return -1; } /* For some reason, mmc_bind always assigns priv to the device */ slot->mmc.priv = slot; debug("%s(%s): lowlevel init\n", __func__, dev->name); err = octeontx_mmc_init_lowlevel(mmc); if (err) { pr_err("probe(%s): Low-level init failed\n", dev->name); return err; } slot->valid = true; debug("%s(%s):\n" " base address : %p\n" " bus id : %d\n", __func__, dev->name, slot->base_addr, slot->bus_id); return err; } /** * MMC slot driver operations */ static const struct dm_mmc_ops octeontx_hsmmc_ops = { .send_cmd = octeontx_mmc_dev_send_cmd, .set_ios = octeontx_mmc_set_ios, .get_cd = octeontx_mmc_get_cd, .get_wp = octeontx_mmc_get_wp, #ifdef MMC_SUPPORTS_TUNING .execute_tuning = octeontx_mmc_execute_tuning, #endif }; static const struct udevice_id octeontx_hsmmc_ids[] = { { .compatible = "mmc-slot" }, { } }; U_BOOT_DRIVER(octeontx_hsmmc_slot) = { .name = "octeontx_hsmmc_slot", .id = UCLASS_MMC, .of_match = of_match_ptr(octeontx_hsmmc_ids), .probe = octeontx_mmc_slot_probe, .ops = &octeontx_hsmmc_ops, }; /***************************************************************** * PCI host driver * * The PCI host driver contains the resources used by all of the * slot drivers. * * The slot drivers are pseudo drivers. */ /** * Probe the MMC host controller * * @param dev mmc host controller device * * Return: 0 for success, -1 on error */ static int octeontx_mmc_host_probe(struct udevice *dev) { struct octeontx_mmc_host *host = dev_get_priv(dev); union mio_emm_int emm_int; struct clk clk; int ret; u8 rev; debug("%s(%s): Entry host: %p\n", __func__, dev->name, host); if (!octeontx_mmc_get_valid(dev)) { debug("%s(%s): mmc host not valid\n", __func__, dev->name); return -ENODEV; } memset(host, 0, sizeof(*host)); /* Octeon TX & TX2 use PCI based probing */ if (device_is_compatible(dev, "cavium,thunder-8890-mmc")) { host->base_addr = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, 0, 0, PCI_REGION_TYPE, PCI_REGION_MEM); if (!host->base_addr) { pr_err("%s: Error: MMC base address not found\n", __func__); return -1; } } else { host->base_addr = dev_remap_addr(dev); } host->dev = dev; debug("%s(%s): Base address: %p\n", __func__, dev->name, host->base_addr); if (!dev_has_ofnode(dev)) { pr_err("%s: No device tree information found\n", __func__); return -1; } host->node = dev_ofnode(dev); host->last_slotid = -1; #if !defined(CONFIG_ARCH_OCTEON) if (otx_is_platform(PLATFORM_ASIM)) host->is_asim = true; if (otx_is_platform(PLATFORM_EMULATOR)) host->is_emul = true; #endif host->dma_wait_delay = ofnode_read_u32_default(dev_ofnode(dev), "marvell,dma-wait-delay", 1); /* Force reset of eMMC */ writeq(0, host->base_addr + MIO_EMM_CFG()); debug("%s: Clearing MIO_EMM_CFG\n", __func__); udelay(100); emm_int.u = readq(host->base_addr + MIO_EMM_INT()); debug("%s: Writing 0x%llx to MIO_EMM_INT\n", __func__, emm_int.u); writeq(emm_int.u, host->base_addr + MIO_EMM_INT()); debug("%s(%s): Getting I/O clock\n", __func__, dev->name); ret = clk_get_by_index(dev, 0, &clk); if (ret < 0) return ret; ret = clk_enable(&clk); if (ret) return ret; host->sys_freq = clk_get_rate(&clk); debug("%s(%s): I/O clock %llu\n", __func__, dev->name, host->sys_freq); if (IS_ENABLED(CONFIG_ARCH_OCTEONTX2)) { /* Flags for issues to work around */ dm_pci_read_config8(dev, PCI_REVISION_ID, &rev); if (otx_is_soc(CN96XX)) { debug("%s: CN96XX revision %d\n", __func__, rev); switch (rev) { case 0: host->calibrate_glitch = true; host->cond_clock_glitch = true; break; case 1: break; case 2: break; case 0x10: /* C0 */ host->hs400_skew_needed = true; debug("HS400 skew support enabled\n"); fallthrough; default: debug("CN96XX rev C0+ detected\n"); host->tap_requires_noclk = true; break; } } else if (otx_is_soc(CN95XX)) { if (!rev) host->cond_clock_glitch = true; } } host_probed = true; return 0; } /** * This performs some initial setup before a probe occurs. * * @param dev: MMC slot device * * Return: 0 for success, -1 on failure * * Do some pre-initialization before probing a slot. */ static int octeontx_mmc_host_child_pre_probe(struct udevice *dev) { struct octeontx_mmc_host *host = dev_get_priv(dev_get_parent(dev)); struct octeontx_mmc_slot *slot; struct mmc_uclass_priv *upriv; ofnode node = dev_ofnode(dev); u32 bus_id; char name[16]; int err; debug("%s(%s) Pre-Probe\n", __func__, dev->name); if (ofnode_read_u32(node, "reg", &bus_id)) { pr_err("%s(%s): Error: \"reg\" not found in device tree\n", __func__, dev->name); return -1; } if (bus_id > OCTEONTX_MAX_MMC_SLOT) { pr_err("%s(%s): Error: \"reg\" out of range of 0..%d\n", __func__, dev->name, OCTEONTX_MAX_MMC_SLOT); return -1; } slot = &host->slots[bus_id]; dev_set_priv(dev, slot); slot->host = host; slot->bus_id = bus_id; slot->dev = dev; slot->base_addr = host->base_addr; slot->is_asim = host->is_asim; slot->is_emul = host->is_emul; snprintf(name, sizeof(name), "octeontx-mmc%d", bus_id); err = device_set_name(dev, name); /* FIXME: This code should not be needed */ if (!dev_get_uclass_priv(dev)) { debug("%s(%s): Allocating uclass priv\n", __func__, dev->name); upriv = calloc(1, sizeof(struct mmc_uclass_priv)); if (!upriv) return -ENOMEM; /* * FIXME: This is not allowed * dev_set_uclass_priv(dev, upriv); * uclass_set_priv(dev->uclass, upriv); */ } else { upriv = dev_get_uclass_priv(dev); } upriv->mmc = &slot->mmc; debug("%s: uclass priv: %p, mmc: %p\n", dev->name, upriv, upriv->mmc); debug("%s: ret: %d\n", __func__, err); return err; } static const struct udevice_id octeontx_hsmmc_host_ids[] = { { .compatible = "cavium,thunder-8890-mmc" }, { .compatible = "cavium,octeon-7360-mmc" }, { } }; U_BOOT_DRIVER(octeontx_hsmmc_host) = { .name = "octeontx_hsmmc_host", /* FIXME: Why is this not UCLASS_MMC? */ .id = UCLASS_MISC, .of_match = of_match_ptr(octeontx_hsmmc_host_ids), .probe = octeontx_mmc_host_probe, .priv_auto = sizeof(struct octeontx_mmc_host), .child_pre_probe = octeontx_mmc_host_child_pre_probe, .flags = DM_FLAG_PRE_RELOC, }; static struct pci_device_id octeontx_mmc_supported[] = { { PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_EMMC) }, { }, }; U_BOOT_PCI_DEVICE(octeontx_hsmmc_host, octeontx_mmc_supported);