162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Shared part of driver for MMC/SDHC controller on Cavium OCTEON and 362306a36Sopenharmony_ci * ThunderX SOCs. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 662306a36Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 762306a36Sopenharmony_ci * for more details. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Copyright (C) 2012-2017 Cavium Inc. 1062306a36Sopenharmony_ci * Authors: 1162306a36Sopenharmony_ci * David Daney <david.daney@cavium.com> 1262306a36Sopenharmony_ci * Peter Swain <pswain@cavium.com> 1362306a36Sopenharmony_ci * Steven J. Hill <steven.hill@cavium.com> 1462306a36Sopenharmony_ci * Jan Glauber <jglauber@cavium.com> 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci#include <linux/bitfield.h> 1762306a36Sopenharmony_ci#include <linux/delay.h> 1862306a36Sopenharmony_ci#include <linux/dma-direction.h> 1962306a36Sopenharmony_ci#include <linux/dma-mapping.h> 2062306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 2162306a36Sopenharmony_ci#include <linux/interrupt.h> 2262306a36Sopenharmony_ci#include <linux/mmc/mmc.h> 2362306a36Sopenharmony_ci#include <linux/mmc/slot-gpio.h> 2462306a36Sopenharmony_ci#include <linux/module.h> 2562306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 2662306a36Sopenharmony_ci#include <linux/scatterlist.h> 2762306a36Sopenharmony_ci#include <linux/time.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include "cavium.h" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ciconst char *cvm_mmc_irq_names[] = { 3262306a36Sopenharmony_ci "MMC Buffer", 3362306a36Sopenharmony_ci "MMC Command", 3462306a36Sopenharmony_ci "MMC DMA", 3562306a36Sopenharmony_ci "MMC Command Error", 3662306a36Sopenharmony_ci "MMC DMA Error", 3762306a36Sopenharmony_ci "MMC Switch", 3862306a36Sopenharmony_ci "MMC Switch Error", 3962306a36Sopenharmony_ci "MMC DMA int Fifo", 4062306a36Sopenharmony_ci "MMC DMA int", 4162306a36Sopenharmony_ci}; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* 4462306a36Sopenharmony_ci * The Cavium MMC host hardware assumes that all commands have fixed 4562306a36Sopenharmony_ci * command and response types. These are correct if MMC devices are 4662306a36Sopenharmony_ci * being used. However, non-MMC devices like SD use command and 4762306a36Sopenharmony_ci * response types that are unexpected by the host hardware. 4862306a36Sopenharmony_ci * 4962306a36Sopenharmony_ci * The command and response types can be overridden by supplying an 5062306a36Sopenharmony_ci * XOR value that is applied to the type. We calculate the XOR value 5162306a36Sopenharmony_ci * from the values in this table and the flags passed from the MMC 5262306a36Sopenharmony_ci * core. 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_cistatic struct cvm_mmc_cr_type cvm_mmc_cr_types[] = { 5562306a36Sopenharmony_ci {0, 0}, /* CMD0 */ 5662306a36Sopenharmony_ci {0, 3}, /* CMD1 */ 5762306a36Sopenharmony_ci {0, 2}, /* CMD2 */ 5862306a36Sopenharmony_ci {0, 1}, /* CMD3 */ 5962306a36Sopenharmony_ci {0, 0}, /* CMD4 */ 6062306a36Sopenharmony_ci {0, 1}, /* CMD5 */ 6162306a36Sopenharmony_ci {0, 1}, /* CMD6 */ 6262306a36Sopenharmony_ci {0, 1}, /* CMD7 */ 6362306a36Sopenharmony_ci {1, 1}, /* CMD8 */ 6462306a36Sopenharmony_ci {0, 2}, /* CMD9 */ 6562306a36Sopenharmony_ci {0, 2}, /* CMD10 */ 6662306a36Sopenharmony_ci {1, 1}, /* CMD11 */ 6762306a36Sopenharmony_ci {0, 1}, /* CMD12 */ 6862306a36Sopenharmony_ci {0, 1}, /* CMD13 */ 6962306a36Sopenharmony_ci {1, 1}, /* CMD14 */ 7062306a36Sopenharmony_ci {0, 0}, /* CMD15 */ 7162306a36Sopenharmony_ci {0, 1}, /* CMD16 */ 7262306a36Sopenharmony_ci {1, 1}, /* CMD17 */ 7362306a36Sopenharmony_ci {1, 1}, /* CMD18 */ 7462306a36Sopenharmony_ci {3, 1}, /* CMD19 */ 7562306a36Sopenharmony_ci {2, 1}, /* CMD20 */ 7662306a36Sopenharmony_ci {0, 0}, /* CMD21 */ 7762306a36Sopenharmony_ci {0, 0}, /* CMD22 */ 7862306a36Sopenharmony_ci {0, 1}, /* CMD23 */ 7962306a36Sopenharmony_ci {2, 1}, /* CMD24 */ 8062306a36Sopenharmony_ci {2, 1}, /* CMD25 */ 8162306a36Sopenharmony_ci {2, 1}, /* CMD26 */ 8262306a36Sopenharmony_ci {2, 1}, /* CMD27 */ 8362306a36Sopenharmony_ci {0, 1}, /* CMD28 */ 8462306a36Sopenharmony_ci {0, 1}, /* CMD29 */ 8562306a36Sopenharmony_ci {1, 1}, /* CMD30 */ 8662306a36Sopenharmony_ci {1, 1}, /* CMD31 */ 8762306a36Sopenharmony_ci {0, 0}, /* CMD32 */ 8862306a36Sopenharmony_ci {0, 0}, /* CMD33 */ 8962306a36Sopenharmony_ci {0, 0}, /* CMD34 */ 9062306a36Sopenharmony_ci {0, 1}, /* CMD35 */ 9162306a36Sopenharmony_ci {0, 1}, /* CMD36 */ 9262306a36Sopenharmony_ci {0, 0}, /* CMD37 */ 9362306a36Sopenharmony_ci {0, 1}, /* CMD38 */ 9462306a36Sopenharmony_ci {0, 4}, /* CMD39 */ 9562306a36Sopenharmony_ci {0, 5}, /* CMD40 */ 9662306a36Sopenharmony_ci {0, 0}, /* CMD41 */ 9762306a36Sopenharmony_ci {2, 1}, /* CMD42 */ 9862306a36Sopenharmony_ci {0, 0}, /* CMD43 */ 9962306a36Sopenharmony_ci {0, 0}, /* CMD44 */ 10062306a36Sopenharmony_ci {0, 0}, /* CMD45 */ 10162306a36Sopenharmony_ci {0, 0}, /* CMD46 */ 10262306a36Sopenharmony_ci {0, 0}, /* CMD47 */ 10362306a36Sopenharmony_ci {0, 0}, /* CMD48 */ 10462306a36Sopenharmony_ci {0, 0}, /* CMD49 */ 10562306a36Sopenharmony_ci {0, 0}, /* CMD50 */ 10662306a36Sopenharmony_ci {0, 0}, /* CMD51 */ 10762306a36Sopenharmony_ci {0, 0}, /* CMD52 */ 10862306a36Sopenharmony_ci {0, 0}, /* CMD53 */ 10962306a36Sopenharmony_ci {0, 0}, /* CMD54 */ 11062306a36Sopenharmony_ci {0, 1}, /* CMD55 */ 11162306a36Sopenharmony_ci {0xff, 0xff}, /* CMD56 */ 11262306a36Sopenharmony_ci {0, 0}, /* CMD57 */ 11362306a36Sopenharmony_ci {0, 0}, /* CMD58 */ 11462306a36Sopenharmony_ci {0, 0}, /* CMD59 */ 11562306a36Sopenharmony_ci {0, 0}, /* CMD60 */ 11662306a36Sopenharmony_ci {0, 0}, /* CMD61 */ 11762306a36Sopenharmony_ci {0, 0}, /* CMD62 */ 11862306a36Sopenharmony_ci {0, 0} /* CMD63 */ 11962306a36Sopenharmony_ci}; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic struct cvm_mmc_cr_mods cvm_mmc_get_cr_mods(struct mmc_command *cmd) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci struct cvm_mmc_cr_type *cr; 12462306a36Sopenharmony_ci u8 hardware_ctype, hardware_rtype; 12562306a36Sopenharmony_ci u8 desired_ctype = 0, desired_rtype = 0; 12662306a36Sopenharmony_ci struct cvm_mmc_cr_mods r; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci cr = cvm_mmc_cr_types + (cmd->opcode & 0x3f); 12962306a36Sopenharmony_ci hardware_ctype = cr->ctype; 13062306a36Sopenharmony_ci hardware_rtype = cr->rtype; 13162306a36Sopenharmony_ci if (cmd->opcode == MMC_GEN_CMD) 13262306a36Sopenharmony_ci hardware_ctype = (cmd->arg & 1) ? 1 : 2; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci switch (mmc_cmd_type(cmd)) { 13562306a36Sopenharmony_ci case MMC_CMD_ADTC: 13662306a36Sopenharmony_ci desired_ctype = (cmd->data->flags & MMC_DATA_WRITE) ? 2 : 1; 13762306a36Sopenharmony_ci break; 13862306a36Sopenharmony_ci case MMC_CMD_AC: 13962306a36Sopenharmony_ci case MMC_CMD_BC: 14062306a36Sopenharmony_ci case MMC_CMD_BCR: 14162306a36Sopenharmony_ci desired_ctype = 0; 14262306a36Sopenharmony_ci break; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci switch (mmc_resp_type(cmd)) { 14662306a36Sopenharmony_ci case MMC_RSP_NONE: 14762306a36Sopenharmony_ci desired_rtype = 0; 14862306a36Sopenharmony_ci break; 14962306a36Sopenharmony_ci case MMC_RSP_R1:/* MMC_RSP_R5, MMC_RSP_R6, MMC_RSP_R7 */ 15062306a36Sopenharmony_ci case MMC_RSP_R1B: 15162306a36Sopenharmony_ci desired_rtype = 1; 15262306a36Sopenharmony_ci break; 15362306a36Sopenharmony_ci case MMC_RSP_R2: 15462306a36Sopenharmony_ci desired_rtype = 2; 15562306a36Sopenharmony_ci break; 15662306a36Sopenharmony_ci case MMC_RSP_R3: /* MMC_RSP_R4 */ 15762306a36Sopenharmony_ci desired_rtype = 3; 15862306a36Sopenharmony_ci break; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci r.ctype_xor = desired_ctype ^ hardware_ctype; 16162306a36Sopenharmony_ci r.rtype_xor = desired_rtype ^ hardware_rtype; 16262306a36Sopenharmony_ci return r; 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic void check_switch_errors(struct cvm_mmc_host *host) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci u64 emm_switch; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci emm_switch = readq(host->base + MIO_EMM_SWITCH(host)); 17062306a36Sopenharmony_ci if (emm_switch & MIO_EMM_SWITCH_ERR0) 17162306a36Sopenharmony_ci dev_err(host->dev, "Switch power class error\n"); 17262306a36Sopenharmony_ci if (emm_switch & MIO_EMM_SWITCH_ERR1) 17362306a36Sopenharmony_ci dev_err(host->dev, "Switch hs timing error\n"); 17462306a36Sopenharmony_ci if (emm_switch & MIO_EMM_SWITCH_ERR2) 17562306a36Sopenharmony_ci dev_err(host->dev, "Switch bus width error\n"); 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic void clear_bus_id(u64 *reg) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci u64 bus_id_mask = GENMASK_ULL(61, 60); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci *reg &= ~bus_id_mask; 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic void set_bus_id(u64 *reg, int bus_id) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci clear_bus_id(reg); 18862306a36Sopenharmony_ci *reg |= FIELD_PREP(GENMASK(61, 60), bus_id); 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic int get_bus_id(u64 reg) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci return FIELD_GET(GENMASK_ULL(61, 60), reg); 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci/* 19762306a36Sopenharmony_ci * We never set the switch_exe bit since that would interfere 19862306a36Sopenharmony_ci * with the commands send by the MMC core. 19962306a36Sopenharmony_ci */ 20062306a36Sopenharmony_cistatic void do_switch(struct cvm_mmc_host *host, u64 emm_switch) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci int retries = 100; 20362306a36Sopenharmony_ci u64 rsp_sts; 20462306a36Sopenharmony_ci int bus_id; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci /* 20762306a36Sopenharmony_ci * Modes setting only taken from slot 0. Work around that hardware 20862306a36Sopenharmony_ci * issue by first switching to slot 0. 20962306a36Sopenharmony_ci */ 21062306a36Sopenharmony_ci bus_id = get_bus_id(emm_switch); 21162306a36Sopenharmony_ci clear_bus_id(&emm_switch); 21262306a36Sopenharmony_ci writeq(emm_switch, host->base + MIO_EMM_SWITCH(host)); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci set_bus_id(&emm_switch, bus_id); 21562306a36Sopenharmony_ci writeq(emm_switch, host->base + MIO_EMM_SWITCH(host)); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* wait for the switch to finish */ 21862306a36Sopenharmony_ci do { 21962306a36Sopenharmony_ci rsp_sts = readq(host->base + MIO_EMM_RSP_STS(host)); 22062306a36Sopenharmony_ci if (!(rsp_sts & MIO_EMM_RSP_STS_SWITCH_VAL)) 22162306a36Sopenharmony_ci break; 22262306a36Sopenharmony_ci udelay(10); 22362306a36Sopenharmony_ci } while (--retries); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci check_switch_errors(host); 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic bool switch_val_changed(struct cvm_mmc_slot *slot, u64 new_val) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci /* Match BUS_ID, HS_TIMING, BUS_WIDTH, POWER_CLASS, CLK_HI, CLK_LO */ 23162306a36Sopenharmony_ci u64 match = 0x3001070fffffffffull; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci return (slot->cached_switch & match) != (new_val & match); 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic void set_wdog(struct cvm_mmc_slot *slot, unsigned int ns) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci u64 timeout; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci if (!slot->clock) 24162306a36Sopenharmony_ci return; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (ns) 24462306a36Sopenharmony_ci timeout = (slot->clock * ns) / NSEC_PER_SEC; 24562306a36Sopenharmony_ci else 24662306a36Sopenharmony_ci timeout = (slot->clock * 850ull) / 1000ull; 24762306a36Sopenharmony_ci writeq(timeout, slot->host->base + MIO_EMM_WDOG(slot->host)); 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic void cvm_mmc_reset_bus(struct cvm_mmc_slot *slot) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci struct cvm_mmc_host *host = slot->host; 25362306a36Sopenharmony_ci u64 emm_switch, wdog; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci emm_switch = readq(slot->host->base + MIO_EMM_SWITCH(host)); 25662306a36Sopenharmony_ci emm_switch &= ~(MIO_EMM_SWITCH_EXE | MIO_EMM_SWITCH_ERR0 | 25762306a36Sopenharmony_ci MIO_EMM_SWITCH_ERR1 | MIO_EMM_SWITCH_ERR2); 25862306a36Sopenharmony_ci set_bus_id(&emm_switch, slot->bus_id); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci wdog = readq(slot->host->base + MIO_EMM_WDOG(host)); 26162306a36Sopenharmony_ci do_switch(slot->host, emm_switch); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci slot->cached_switch = emm_switch; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci msleep(20); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci writeq(wdog, slot->host->base + MIO_EMM_WDOG(host)); 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci/* Switch to another slot if needed */ 27162306a36Sopenharmony_cistatic void cvm_mmc_switch_to(struct cvm_mmc_slot *slot) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci struct cvm_mmc_host *host = slot->host; 27462306a36Sopenharmony_ci struct cvm_mmc_slot *old_slot; 27562306a36Sopenharmony_ci u64 emm_sample, emm_switch; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci if (slot->bus_id == host->last_slot) 27862306a36Sopenharmony_ci return; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci if (host->last_slot >= 0 && host->slot[host->last_slot]) { 28162306a36Sopenharmony_ci old_slot = host->slot[host->last_slot]; 28262306a36Sopenharmony_ci old_slot->cached_switch = readq(host->base + MIO_EMM_SWITCH(host)); 28362306a36Sopenharmony_ci old_slot->cached_rca = readq(host->base + MIO_EMM_RCA(host)); 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci writeq(slot->cached_rca, host->base + MIO_EMM_RCA(host)); 28762306a36Sopenharmony_ci emm_switch = slot->cached_switch; 28862306a36Sopenharmony_ci set_bus_id(&emm_switch, slot->bus_id); 28962306a36Sopenharmony_ci do_switch(host, emm_switch); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci emm_sample = FIELD_PREP(MIO_EMM_SAMPLE_CMD_CNT, slot->cmd_cnt) | 29262306a36Sopenharmony_ci FIELD_PREP(MIO_EMM_SAMPLE_DAT_CNT, slot->dat_cnt); 29362306a36Sopenharmony_ci writeq(emm_sample, host->base + MIO_EMM_SAMPLE(host)); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci host->last_slot = slot->bus_id; 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic void do_read(struct cvm_mmc_host *host, struct mmc_request *req, 29962306a36Sopenharmony_ci u64 dbuf) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci struct sg_mapping_iter *smi = &host->smi; 30262306a36Sopenharmony_ci int data_len = req->data->blocks * req->data->blksz; 30362306a36Sopenharmony_ci int bytes_xfered, shift = -1; 30462306a36Sopenharmony_ci u64 dat = 0; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci /* Auto inc from offset zero */ 30762306a36Sopenharmony_ci writeq((0x10000 | (dbuf << 6)), host->base + MIO_EMM_BUF_IDX(host)); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci for (bytes_xfered = 0; bytes_xfered < data_len;) { 31062306a36Sopenharmony_ci if (smi->consumed >= smi->length) { 31162306a36Sopenharmony_ci if (!sg_miter_next(smi)) 31262306a36Sopenharmony_ci break; 31362306a36Sopenharmony_ci smi->consumed = 0; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci if (shift < 0) { 31762306a36Sopenharmony_ci dat = readq(host->base + MIO_EMM_BUF_DAT(host)); 31862306a36Sopenharmony_ci shift = 56; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci while (smi->consumed < smi->length && shift >= 0) { 32262306a36Sopenharmony_ci ((u8 *)smi->addr)[smi->consumed] = (dat >> shift) & 0xff; 32362306a36Sopenharmony_ci bytes_xfered++; 32462306a36Sopenharmony_ci smi->consumed++; 32562306a36Sopenharmony_ci shift -= 8; 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci sg_miter_stop(smi); 33062306a36Sopenharmony_ci req->data->bytes_xfered = bytes_xfered; 33162306a36Sopenharmony_ci req->data->error = 0; 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic void do_write(struct mmc_request *req) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci req->data->bytes_xfered = req->data->blocks * req->data->blksz; 33762306a36Sopenharmony_ci req->data->error = 0; 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic void set_cmd_response(struct cvm_mmc_host *host, struct mmc_request *req, 34162306a36Sopenharmony_ci u64 rsp_sts) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci u64 rsp_hi, rsp_lo; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (!(rsp_sts & MIO_EMM_RSP_STS_RSP_VAL)) 34662306a36Sopenharmony_ci return; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci rsp_lo = readq(host->base + MIO_EMM_RSP_LO(host)); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci switch (FIELD_GET(MIO_EMM_RSP_STS_RSP_TYPE, rsp_sts)) { 35162306a36Sopenharmony_ci case 1: 35262306a36Sopenharmony_ci case 3: 35362306a36Sopenharmony_ci req->cmd->resp[0] = (rsp_lo >> 8) & 0xffffffff; 35462306a36Sopenharmony_ci req->cmd->resp[1] = 0; 35562306a36Sopenharmony_ci req->cmd->resp[2] = 0; 35662306a36Sopenharmony_ci req->cmd->resp[3] = 0; 35762306a36Sopenharmony_ci break; 35862306a36Sopenharmony_ci case 2: 35962306a36Sopenharmony_ci req->cmd->resp[3] = rsp_lo & 0xffffffff; 36062306a36Sopenharmony_ci req->cmd->resp[2] = (rsp_lo >> 32) & 0xffffffff; 36162306a36Sopenharmony_ci rsp_hi = readq(host->base + MIO_EMM_RSP_HI(host)); 36262306a36Sopenharmony_ci req->cmd->resp[1] = rsp_hi & 0xffffffff; 36362306a36Sopenharmony_ci req->cmd->resp[0] = (rsp_hi >> 32) & 0xffffffff; 36462306a36Sopenharmony_ci break; 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cistatic int get_dma_dir(struct mmc_data *data) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci return (data->flags & MMC_DATA_WRITE) ? DMA_TO_DEVICE : DMA_FROM_DEVICE; 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cistatic int finish_dma_single(struct cvm_mmc_host *host, struct mmc_data *data) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci data->bytes_xfered = data->blocks * data->blksz; 37662306a36Sopenharmony_ci data->error = 0; 37762306a36Sopenharmony_ci dma_unmap_sg(host->dev, data->sg, data->sg_len, get_dma_dir(data)); 37862306a36Sopenharmony_ci return 1; 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cistatic int finish_dma_sg(struct cvm_mmc_host *host, struct mmc_data *data) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci u64 fifo_cfg; 38462306a36Sopenharmony_ci int count; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci /* Check if there are any pending requests left */ 38762306a36Sopenharmony_ci fifo_cfg = readq(host->dma_base + MIO_EMM_DMA_FIFO_CFG(host)); 38862306a36Sopenharmony_ci count = FIELD_GET(MIO_EMM_DMA_FIFO_CFG_COUNT, fifo_cfg); 38962306a36Sopenharmony_ci if (count) 39062306a36Sopenharmony_ci dev_err(host->dev, "%u requests still pending\n", count); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci data->bytes_xfered = data->blocks * data->blksz; 39362306a36Sopenharmony_ci data->error = 0; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci /* Clear and disable FIFO */ 39662306a36Sopenharmony_ci writeq(BIT_ULL(16), host->dma_base + MIO_EMM_DMA_FIFO_CFG(host)); 39762306a36Sopenharmony_ci dma_unmap_sg(host->dev, data->sg, data->sg_len, get_dma_dir(data)); 39862306a36Sopenharmony_ci return 1; 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic int finish_dma(struct cvm_mmc_host *host, struct mmc_data *data) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci if (host->use_sg && data->sg_len > 1) 40462306a36Sopenharmony_ci return finish_dma_sg(host, data); 40562306a36Sopenharmony_ci else 40662306a36Sopenharmony_ci return finish_dma_single(host, data); 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cistatic int check_status(u64 rsp_sts) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci if (rsp_sts & MIO_EMM_RSP_STS_RSP_BAD_STS || 41262306a36Sopenharmony_ci rsp_sts & MIO_EMM_RSP_STS_RSP_CRC_ERR || 41362306a36Sopenharmony_ci rsp_sts & MIO_EMM_RSP_STS_BLK_CRC_ERR) 41462306a36Sopenharmony_ci return -EILSEQ; 41562306a36Sopenharmony_ci if (rsp_sts & MIO_EMM_RSP_STS_RSP_TIMEOUT || 41662306a36Sopenharmony_ci rsp_sts & MIO_EMM_RSP_STS_BLK_TIMEOUT) 41762306a36Sopenharmony_ci return -ETIMEDOUT; 41862306a36Sopenharmony_ci if (rsp_sts & MIO_EMM_RSP_STS_DBUF_ERR) 41962306a36Sopenharmony_ci return -EIO; 42062306a36Sopenharmony_ci return 0; 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci/* Try to clean up failed DMA. */ 42462306a36Sopenharmony_cistatic void cleanup_dma(struct cvm_mmc_host *host, u64 rsp_sts) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci u64 emm_dma; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci emm_dma = readq(host->base + MIO_EMM_DMA(host)); 42962306a36Sopenharmony_ci emm_dma |= FIELD_PREP(MIO_EMM_DMA_VAL, 1) | 43062306a36Sopenharmony_ci FIELD_PREP(MIO_EMM_DMA_DAT_NULL, 1); 43162306a36Sopenharmony_ci set_bus_id(&emm_dma, get_bus_id(rsp_sts)); 43262306a36Sopenharmony_ci writeq(emm_dma, host->base + MIO_EMM_DMA(host)); 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ciirqreturn_t cvm_mmc_interrupt(int irq, void *dev_id) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci struct cvm_mmc_host *host = dev_id; 43862306a36Sopenharmony_ci struct mmc_request *req; 43962306a36Sopenharmony_ci u64 emm_int, rsp_sts; 44062306a36Sopenharmony_ci bool host_done; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (host->need_irq_handler_lock) 44362306a36Sopenharmony_ci spin_lock(&host->irq_handler_lock); 44462306a36Sopenharmony_ci else 44562306a36Sopenharmony_ci __acquire(&host->irq_handler_lock); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci /* Clear interrupt bits (write 1 clears ). */ 44862306a36Sopenharmony_ci emm_int = readq(host->base + MIO_EMM_INT(host)); 44962306a36Sopenharmony_ci writeq(emm_int, host->base + MIO_EMM_INT(host)); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci if (emm_int & MIO_EMM_INT_SWITCH_ERR) 45262306a36Sopenharmony_ci check_switch_errors(host); 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci req = host->current_req; 45562306a36Sopenharmony_ci if (!req) 45662306a36Sopenharmony_ci goto out; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci rsp_sts = readq(host->base + MIO_EMM_RSP_STS(host)); 45962306a36Sopenharmony_ci /* 46062306a36Sopenharmony_ci * dma_val set means DMA is still in progress. Don't touch 46162306a36Sopenharmony_ci * the request and wait for the interrupt indicating that 46262306a36Sopenharmony_ci * the DMA is finished. 46362306a36Sopenharmony_ci */ 46462306a36Sopenharmony_ci if ((rsp_sts & MIO_EMM_RSP_STS_DMA_VAL) && host->dma_active) 46562306a36Sopenharmony_ci goto out; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if (!host->dma_active && req->data && 46862306a36Sopenharmony_ci (emm_int & MIO_EMM_INT_BUF_DONE)) { 46962306a36Sopenharmony_ci unsigned int type = (rsp_sts >> 7) & 3; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci if (type == 1) 47262306a36Sopenharmony_ci do_read(host, req, rsp_sts & MIO_EMM_RSP_STS_DBUF); 47362306a36Sopenharmony_ci else if (type == 2) 47462306a36Sopenharmony_ci do_write(req); 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci host_done = emm_int & MIO_EMM_INT_CMD_DONE || 47862306a36Sopenharmony_ci emm_int & MIO_EMM_INT_DMA_DONE || 47962306a36Sopenharmony_ci emm_int & MIO_EMM_INT_CMD_ERR || 48062306a36Sopenharmony_ci emm_int & MIO_EMM_INT_DMA_ERR; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci if (!(host_done && req->done)) 48362306a36Sopenharmony_ci goto no_req_done; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci req->cmd->error = check_status(rsp_sts); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci if (host->dma_active && req->data) 48862306a36Sopenharmony_ci if (!finish_dma(host, req->data)) 48962306a36Sopenharmony_ci goto no_req_done; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci set_cmd_response(host, req, rsp_sts); 49262306a36Sopenharmony_ci if ((emm_int & MIO_EMM_INT_DMA_ERR) && 49362306a36Sopenharmony_ci (rsp_sts & MIO_EMM_RSP_STS_DMA_PEND)) 49462306a36Sopenharmony_ci cleanup_dma(host, rsp_sts); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci host->current_req = NULL; 49762306a36Sopenharmony_ci req->done(req); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_cino_req_done: 50062306a36Sopenharmony_ci if (host->dmar_fixup_done) 50162306a36Sopenharmony_ci host->dmar_fixup_done(host); 50262306a36Sopenharmony_ci if (host_done) 50362306a36Sopenharmony_ci host->release_bus(host); 50462306a36Sopenharmony_ciout: 50562306a36Sopenharmony_ci if (host->need_irq_handler_lock) 50662306a36Sopenharmony_ci spin_unlock(&host->irq_handler_lock); 50762306a36Sopenharmony_ci else 50862306a36Sopenharmony_ci __release(&host->irq_handler_lock); 50962306a36Sopenharmony_ci return IRQ_RETVAL(emm_int != 0); 51062306a36Sopenharmony_ci} 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci/* 51362306a36Sopenharmony_ci * Program DMA_CFG and if needed DMA_ADR. 51462306a36Sopenharmony_ci * Returns 0 on error, DMA address otherwise. 51562306a36Sopenharmony_ci */ 51662306a36Sopenharmony_cistatic u64 prepare_dma_single(struct cvm_mmc_host *host, struct mmc_data *data) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci u64 dma_cfg, addr; 51962306a36Sopenharmony_ci int count, rw; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci count = dma_map_sg(host->dev, data->sg, data->sg_len, 52262306a36Sopenharmony_ci get_dma_dir(data)); 52362306a36Sopenharmony_ci if (!count) 52462306a36Sopenharmony_ci return 0; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci rw = (data->flags & MMC_DATA_WRITE) ? 1 : 0; 52762306a36Sopenharmony_ci dma_cfg = FIELD_PREP(MIO_EMM_DMA_CFG_EN, 1) | 52862306a36Sopenharmony_ci FIELD_PREP(MIO_EMM_DMA_CFG_RW, rw); 52962306a36Sopenharmony_ci#ifdef __LITTLE_ENDIAN 53062306a36Sopenharmony_ci dma_cfg |= FIELD_PREP(MIO_EMM_DMA_CFG_ENDIAN, 1); 53162306a36Sopenharmony_ci#endif 53262306a36Sopenharmony_ci dma_cfg |= FIELD_PREP(MIO_EMM_DMA_CFG_SIZE, 53362306a36Sopenharmony_ci (sg_dma_len(&data->sg[0]) / 8) - 1); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci addr = sg_dma_address(&data->sg[0]); 53662306a36Sopenharmony_ci if (!host->big_dma_addr) 53762306a36Sopenharmony_ci dma_cfg |= FIELD_PREP(MIO_EMM_DMA_CFG_ADR, addr); 53862306a36Sopenharmony_ci writeq(dma_cfg, host->dma_base + MIO_EMM_DMA_CFG(host)); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci pr_debug("[%s] sg_dma_len: %u total sg_elem: %d\n", 54162306a36Sopenharmony_ci (rw) ? "W" : "R", sg_dma_len(&data->sg[0]), count); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci if (host->big_dma_addr) 54462306a36Sopenharmony_ci writeq(addr, host->dma_base + MIO_EMM_DMA_ADR(host)); 54562306a36Sopenharmony_ci return addr; 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci/* 54962306a36Sopenharmony_ci * Queue complete sg list into the FIFO. 55062306a36Sopenharmony_ci * Returns 0 on error, 1 otherwise. 55162306a36Sopenharmony_ci */ 55262306a36Sopenharmony_cistatic u64 prepare_dma_sg(struct cvm_mmc_host *host, struct mmc_data *data) 55362306a36Sopenharmony_ci{ 55462306a36Sopenharmony_ci struct scatterlist *sg; 55562306a36Sopenharmony_ci u64 fifo_cmd, addr; 55662306a36Sopenharmony_ci int count, i, rw; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci count = dma_map_sg(host->dev, data->sg, data->sg_len, 55962306a36Sopenharmony_ci get_dma_dir(data)); 56062306a36Sopenharmony_ci if (!count) 56162306a36Sopenharmony_ci return 0; 56262306a36Sopenharmony_ci if (count > 16) 56362306a36Sopenharmony_ci goto error; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci /* Enable FIFO by removing CLR bit */ 56662306a36Sopenharmony_ci writeq(0, host->dma_base + MIO_EMM_DMA_FIFO_CFG(host)); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci for_each_sg(data->sg, sg, count, i) { 56962306a36Sopenharmony_ci /* Program DMA address */ 57062306a36Sopenharmony_ci addr = sg_dma_address(sg); 57162306a36Sopenharmony_ci if (addr & 7) 57262306a36Sopenharmony_ci goto error; 57362306a36Sopenharmony_ci writeq(addr, host->dma_base + MIO_EMM_DMA_FIFO_ADR(host)); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci /* 57662306a36Sopenharmony_ci * If we have scatter-gather support we also have an extra 57762306a36Sopenharmony_ci * register for the DMA addr, so no need to check 57862306a36Sopenharmony_ci * host->big_dma_addr here. 57962306a36Sopenharmony_ci */ 58062306a36Sopenharmony_ci rw = (data->flags & MMC_DATA_WRITE) ? 1 : 0; 58162306a36Sopenharmony_ci fifo_cmd = FIELD_PREP(MIO_EMM_DMA_FIFO_CMD_RW, rw); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci /* enable interrupts on the last element */ 58462306a36Sopenharmony_ci fifo_cmd |= FIELD_PREP(MIO_EMM_DMA_FIFO_CMD_INTDIS, 58562306a36Sopenharmony_ci (i + 1 == count) ? 0 : 1); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci#ifdef __LITTLE_ENDIAN 58862306a36Sopenharmony_ci fifo_cmd |= FIELD_PREP(MIO_EMM_DMA_FIFO_CMD_ENDIAN, 1); 58962306a36Sopenharmony_ci#endif 59062306a36Sopenharmony_ci fifo_cmd |= FIELD_PREP(MIO_EMM_DMA_FIFO_CMD_SIZE, 59162306a36Sopenharmony_ci sg_dma_len(sg) / 8 - 1); 59262306a36Sopenharmony_ci /* 59362306a36Sopenharmony_ci * The write copies the address and the command to the FIFO 59462306a36Sopenharmony_ci * and increments the FIFO's COUNT field. 59562306a36Sopenharmony_ci */ 59662306a36Sopenharmony_ci writeq(fifo_cmd, host->dma_base + MIO_EMM_DMA_FIFO_CMD(host)); 59762306a36Sopenharmony_ci pr_debug("[%s] sg_dma_len: %u sg_elem: %d/%d\n", 59862306a36Sopenharmony_ci (rw) ? "W" : "R", sg_dma_len(sg), i, count); 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci /* 60262306a36Sopenharmony_ci * In difference to prepare_dma_single we don't return the 60362306a36Sopenharmony_ci * address here, as it would not make sense for scatter-gather. 60462306a36Sopenharmony_ci * The dma fixup is only required on models that don't support 60562306a36Sopenharmony_ci * scatter-gather, so that is not a problem. 60662306a36Sopenharmony_ci */ 60762306a36Sopenharmony_ci return 1; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cierror: 61062306a36Sopenharmony_ci WARN_ON_ONCE(1); 61162306a36Sopenharmony_ci dma_unmap_sg(host->dev, data->sg, data->sg_len, get_dma_dir(data)); 61262306a36Sopenharmony_ci /* Disable FIFO */ 61362306a36Sopenharmony_ci writeq(BIT_ULL(16), host->dma_base + MIO_EMM_DMA_FIFO_CFG(host)); 61462306a36Sopenharmony_ci return 0; 61562306a36Sopenharmony_ci} 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_cistatic u64 prepare_dma(struct cvm_mmc_host *host, struct mmc_data *data) 61862306a36Sopenharmony_ci{ 61962306a36Sopenharmony_ci if (host->use_sg && data->sg_len > 1) 62062306a36Sopenharmony_ci return prepare_dma_sg(host, data); 62162306a36Sopenharmony_ci else 62262306a36Sopenharmony_ci return prepare_dma_single(host, data); 62362306a36Sopenharmony_ci} 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_cistatic u64 prepare_ext_dma(struct mmc_host *mmc, struct mmc_request *mrq) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci struct cvm_mmc_slot *slot = mmc_priv(mmc); 62862306a36Sopenharmony_ci u64 emm_dma; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci emm_dma = FIELD_PREP(MIO_EMM_DMA_VAL, 1) | 63162306a36Sopenharmony_ci FIELD_PREP(MIO_EMM_DMA_SECTOR, 63262306a36Sopenharmony_ci mmc_card_is_blockaddr(mmc->card) ? 1 : 0) | 63362306a36Sopenharmony_ci FIELD_PREP(MIO_EMM_DMA_RW, 63462306a36Sopenharmony_ci (mrq->data->flags & MMC_DATA_WRITE) ? 1 : 0) | 63562306a36Sopenharmony_ci FIELD_PREP(MIO_EMM_DMA_BLOCK_CNT, mrq->data->blocks) | 63662306a36Sopenharmony_ci FIELD_PREP(MIO_EMM_DMA_CARD_ADDR, mrq->cmd->arg); 63762306a36Sopenharmony_ci set_bus_id(&emm_dma, slot->bus_id); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci if (mmc_card_mmc(mmc->card) || (mmc_card_sd(mmc->card) && 64062306a36Sopenharmony_ci (mmc->card->scr.cmds & SD_SCR_CMD23_SUPPORT))) 64162306a36Sopenharmony_ci emm_dma |= FIELD_PREP(MIO_EMM_DMA_MULTI, 1); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci pr_debug("[%s] blocks: %u multi: %d\n", 64462306a36Sopenharmony_ci (emm_dma & MIO_EMM_DMA_RW) ? "W" : "R", 64562306a36Sopenharmony_ci mrq->data->blocks, (emm_dma & MIO_EMM_DMA_MULTI) ? 1 : 0); 64662306a36Sopenharmony_ci return emm_dma; 64762306a36Sopenharmony_ci} 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_cistatic void cvm_mmc_dma_request(struct mmc_host *mmc, 65062306a36Sopenharmony_ci struct mmc_request *mrq) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci struct cvm_mmc_slot *slot = mmc_priv(mmc); 65362306a36Sopenharmony_ci struct cvm_mmc_host *host = slot->host; 65462306a36Sopenharmony_ci struct mmc_data *data; 65562306a36Sopenharmony_ci u64 emm_dma, addr; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci if (!mrq->data || !mrq->data->sg || !mrq->data->sg_len || 65862306a36Sopenharmony_ci !mrq->stop || mrq->stop->opcode != MMC_STOP_TRANSMISSION) { 65962306a36Sopenharmony_ci dev_err(&mmc->card->dev, "Error: %s no data\n", __func__); 66062306a36Sopenharmony_ci goto error; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci cvm_mmc_switch_to(slot); 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci data = mrq->data; 66662306a36Sopenharmony_ci pr_debug("DMA request blocks: %d block_size: %d total_size: %d\n", 66762306a36Sopenharmony_ci data->blocks, data->blksz, data->blocks * data->blksz); 66862306a36Sopenharmony_ci if (data->timeout_ns) 66962306a36Sopenharmony_ci set_wdog(slot, data->timeout_ns); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci WARN_ON(host->current_req); 67262306a36Sopenharmony_ci host->current_req = mrq; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci emm_dma = prepare_ext_dma(mmc, mrq); 67562306a36Sopenharmony_ci addr = prepare_dma(host, data); 67662306a36Sopenharmony_ci if (!addr) { 67762306a36Sopenharmony_ci dev_err(host->dev, "prepare_dma failed\n"); 67862306a36Sopenharmony_ci goto error; 67962306a36Sopenharmony_ci } 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci host->dma_active = true; 68262306a36Sopenharmony_ci host->int_enable(host, MIO_EMM_INT_CMD_ERR | MIO_EMM_INT_DMA_DONE | 68362306a36Sopenharmony_ci MIO_EMM_INT_DMA_ERR); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci if (host->dmar_fixup) 68662306a36Sopenharmony_ci host->dmar_fixup(host, mrq->cmd, data, addr); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci /* 68962306a36Sopenharmony_ci * If we have a valid SD card in the slot, we set the response 69062306a36Sopenharmony_ci * bit mask to check for CRC errors and timeouts only. 69162306a36Sopenharmony_ci * Otherwise, use the default power reset value. 69262306a36Sopenharmony_ci */ 69362306a36Sopenharmony_ci if (mmc_card_sd(mmc->card)) 69462306a36Sopenharmony_ci writeq(0x00b00000ull, host->base + MIO_EMM_STS_MASK(host)); 69562306a36Sopenharmony_ci else 69662306a36Sopenharmony_ci writeq(0xe4390080ull, host->base + MIO_EMM_STS_MASK(host)); 69762306a36Sopenharmony_ci writeq(emm_dma, host->base + MIO_EMM_DMA(host)); 69862306a36Sopenharmony_ci return; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_cierror: 70162306a36Sopenharmony_ci mrq->cmd->error = -EINVAL; 70262306a36Sopenharmony_ci if (mrq->done) 70362306a36Sopenharmony_ci mrq->done(mrq); 70462306a36Sopenharmony_ci host->release_bus(host); 70562306a36Sopenharmony_ci} 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_cistatic void do_read_request(struct cvm_mmc_host *host, struct mmc_request *mrq) 70862306a36Sopenharmony_ci{ 70962306a36Sopenharmony_ci sg_miter_start(&host->smi, mrq->data->sg, mrq->data->sg_len, 71062306a36Sopenharmony_ci SG_MITER_ATOMIC | SG_MITER_TO_SG); 71162306a36Sopenharmony_ci} 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_cistatic void do_write_request(struct cvm_mmc_host *host, struct mmc_request *mrq) 71462306a36Sopenharmony_ci{ 71562306a36Sopenharmony_ci unsigned int data_len = mrq->data->blocks * mrq->data->blksz; 71662306a36Sopenharmony_ci struct sg_mapping_iter *smi = &host->smi; 71762306a36Sopenharmony_ci unsigned int bytes_xfered; 71862306a36Sopenharmony_ci int shift = 56; 71962306a36Sopenharmony_ci u64 dat = 0; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci /* Copy data to the xmit buffer before issuing the command. */ 72262306a36Sopenharmony_ci sg_miter_start(smi, mrq->data->sg, mrq->data->sg_len, SG_MITER_FROM_SG); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci /* Auto inc from offset zero, dbuf zero */ 72562306a36Sopenharmony_ci writeq(0x10000ull, host->base + MIO_EMM_BUF_IDX(host)); 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci for (bytes_xfered = 0; bytes_xfered < data_len;) { 72862306a36Sopenharmony_ci if (smi->consumed >= smi->length) { 72962306a36Sopenharmony_ci if (!sg_miter_next(smi)) 73062306a36Sopenharmony_ci break; 73162306a36Sopenharmony_ci smi->consumed = 0; 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci while (smi->consumed < smi->length && shift >= 0) { 73562306a36Sopenharmony_ci dat |= (u64)((u8 *)smi->addr)[smi->consumed] << shift; 73662306a36Sopenharmony_ci bytes_xfered++; 73762306a36Sopenharmony_ci smi->consumed++; 73862306a36Sopenharmony_ci shift -= 8; 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci if (shift < 0) { 74262306a36Sopenharmony_ci writeq(dat, host->base + MIO_EMM_BUF_DAT(host)); 74362306a36Sopenharmony_ci shift = 56; 74462306a36Sopenharmony_ci dat = 0; 74562306a36Sopenharmony_ci } 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci sg_miter_stop(smi); 74862306a36Sopenharmony_ci} 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_cistatic void cvm_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) 75162306a36Sopenharmony_ci{ 75262306a36Sopenharmony_ci struct cvm_mmc_slot *slot = mmc_priv(mmc); 75362306a36Sopenharmony_ci struct cvm_mmc_host *host = slot->host; 75462306a36Sopenharmony_ci struct mmc_command *cmd = mrq->cmd; 75562306a36Sopenharmony_ci struct cvm_mmc_cr_mods mods; 75662306a36Sopenharmony_ci u64 emm_cmd, rsp_sts; 75762306a36Sopenharmony_ci int retries = 100; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci /* 76062306a36Sopenharmony_ci * Note about locking: 76162306a36Sopenharmony_ci * All MMC devices share the same bus and controller. Allow only a 76262306a36Sopenharmony_ci * single user of the bootbus/MMC bus at a time. The lock is acquired 76362306a36Sopenharmony_ci * on all entry points from the MMC layer. 76462306a36Sopenharmony_ci * 76562306a36Sopenharmony_ci * For requests the lock is only released after the completion 76662306a36Sopenharmony_ci * interrupt! 76762306a36Sopenharmony_ci */ 76862306a36Sopenharmony_ci host->acquire_bus(host); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci if (cmd->opcode == MMC_READ_MULTIPLE_BLOCK || 77162306a36Sopenharmony_ci cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK) 77262306a36Sopenharmony_ci return cvm_mmc_dma_request(mmc, mrq); 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci cvm_mmc_switch_to(slot); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci mods = cvm_mmc_get_cr_mods(cmd); 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci WARN_ON(host->current_req); 77962306a36Sopenharmony_ci host->current_req = mrq; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci if (cmd->data) { 78262306a36Sopenharmony_ci if (cmd->data->flags & MMC_DATA_READ) 78362306a36Sopenharmony_ci do_read_request(host, mrq); 78462306a36Sopenharmony_ci else 78562306a36Sopenharmony_ci do_write_request(host, mrq); 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci if (cmd->data->timeout_ns) 78862306a36Sopenharmony_ci set_wdog(slot, cmd->data->timeout_ns); 78962306a36Sopenharmony_ci } else 79062306a36Sopenharmony_ci set_wdog(slot, 0); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci host->dma_active = false; 79362306a36Sopenharmony_ci host->int_enable(host, MIO_EMM_INT_CMD_DONE | MIO_EMM_INT_CMD_ERR); 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci emm_cmd = FIELD_PREP(MIO_EMM_CMD_VAL, 1) | 79662306a36Sopenharmony_ci FIELD_PREP(MIO_EMM_CMD_CTYPE_XOR, mods.ctype_xor) | 79762306a36Sopenharmony_ci FIELD_PREP(MIO_EMM_CMD_RTYPE_XOR, mods.rtype_xor) | 79862306a36Sopenharmony_ci FIELD_PREP(MIO_EMM_CMD_IDX, cmd->opcode) | 79962306a36Sopenharmony_ci FIELD_PREP(MIO_EMM_CMD_ARG, cmd->arg); 80062306a36Sopenharmony_ci set_bus_id(&emm_cmd, slot->bus_id); 80162306a36Sopenharmony_ci if (cmd->data && mmc_cmd_type(cmd) == MMC_CMD_ADTC) 80262306a36Sopenharmony_ci emm_cmd |= FIELD_PREP(MIO_EMM_CMD_OFFSET, 80362306a36Sopenharmony_ci 64 - ((cmd->data->blocks * cmd->data->blksz) / 8)); 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci writeq(0, host->base + MIO_EMM_STS_MASK(host)); 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ciretry: 80862306a36Sopenharmony_ci rsp_sts = readq(host->base + MIO_EMM_RSP_STS(host)); 80962306a36Sopenharmony_ci if (rsp_sts & MIO_EMM_RSP_STS_DMA_VAL || 81062306a36Sopenharmony_ci rsp_sts & MIO_EMM_RSP_STS_CMD_VAL || 81162306a36Sopenharmony_ci rsp_sts & MIO_EMM_RSP_STS_SWITCH_VAL || 81262306a36Sopenharmony_ci rsp_sts & MIO_EMM_RSP_STS_DMA_PEND) { 81362306a36Sopenharmony_ci udelay(10); 81462306a36Sopenharmony_ci if (--retries) 81562306a36Sopenharmony_ci goto retry; 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci if (!retries) 81862306a36Sopenharmony_ci dev_err(host->dev, "Bad status: %llx before command write\n", rsp_sts); 81962306a36Sopenharmony_ci writeq(emm_cmd, host->base + MIO_EMM_CMD(host)); 82062306a36Sopenharmony_ci} 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_cistatic void cvm_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) 82362306a36Sopenharmony_ci{ 82462306a36Sopenharmony_ci struct cvm_mmc_slot *slot = mmc_priv(mmc); 82562306a36Sopenharmony_ci struct cvm_mmc_host *host = slot->host; 82662306a36Sopenharmony_ci int clk_period = 0, power_class = 10, bus_width = 0; 82762306a36Sopenharmony_ci u64 clock, emm_switch; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci host->acquire_bus(host); 83062306a36Sopenharmony_ci cvm_mmc_switch_to(slot); 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci /* Set the power state */ 83362306a36Sopenharmony_ci switch (ios->power_mode) { 83462306a36Sopenharmony_ci case MMC_POWER_ON: 83562306a36Sopenharmony_ci break; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci case MMC_POWER_OFF: 83862306a36Sopenharmony_ci cvm_mmc_reset_bus(slot); 83962306a36Sopenharmony_ci if (host->global_pwr_gpiod) 84062306a36Sopenharmony_ci host->set_shared_power(host, 0); 84162306a36Sopenharmony_ci else if (!IS_ERR(mmc->supply.vmmc)) 84262306a36Sopenharmony_ci mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); 84362306a36Sopenharmony_ci break; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci case MMC_POWER_UP: 84662306a36Sopenharmony_ci if (host->global_pwr_gpiod) 84762306a36Sopenharmony_ci host->set_shared_power(host, 1); 84862306a36Sopenharmony_ci else if (!IS_ERR(mmc->supply.vmmc)) 84962306a36Sopenharmony_ci mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd); 85062306a36Sopenharmony_ci break; 85162306a36Sopenharmony_ci } 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci /* Convert bus width to HW definition */ 85462306a36Sopenharmony_ci switch (ios->bus_width) { 85562306a36Sopenharmony_ci case MMC_BUS_WIDTH_8: 85662306a36Sopenharmony_ci bus_width = 2; 85762306a36Sopenharmony_ci break; 85862306a36Sopenharmony_ci case MMC_BUS_WIDTH_4: 85962306a36Sopenharmony_ci bus_width = 1; 86062306a36Sopenharmony_ci break; 86162306a36Sopenharmony_ci case MMC_BUS_WIDTH_1: 86262306a36Sopenharmony_ci bus_width = 0; 86362306a36Sopenharmony_ci break; 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci /* DDR is available for 4/8 bit bus width */ 86762306a36Sopenharmony_ci if (ios->bus_width && ios->timing == MMC_TIMING_MMC_DDR52) 86862306a36Sopenharmony_ci bus_width |= 4; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci /* Change the clock frequency. */ 87162306a36Sopenharmony_ci clock = ios->clock; 87262306a36Sopenharmony_ci if (clock > 52000000) 87362306a36Sopenharmony_ci clock = 52000000; 87462306a36Sopenharmony_ci slot->clock = clock; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci if (clock) 87762306a36Sopenharmony_ci clk_period = (host->sys_freq + clock - 1) / (2 * clock); 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci emm_switch = FIELD_PREP(MIO_EMM_SWITCH_HS_TIMING, 88062306a36Sopenharmony_ci (ios->timing == MMC_TIMING_MMC_HS)) | 88162306a36Sopenharmony_ci FIELD_PREP(MIO_EMM_SWITCH_BUS_WIDTH, bus_width) | 88262306a36Sopenharmony_ci FIELD_PREP(MIO_EMM_SWITCH_POWER_CLASS, power_class) | 88362306a36Sopenharmony_ci FIELD_PREP(MIO_EMM_SWITCH_CLK_HI, clk_period) | 88462306a36Sopenharmony_ci FIELD_PREP(MIO_EMM_SWITCH_CLK_LO, clk_period); 88562306a36Sopenharmony_ci set_bus_id(&emm_switch, slot->bus_id); 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci if (!switch_val_changed(slot, emm_switch)) 88862306a36Sopenharmony_ci goto out; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci set_wdog(slot, 0); 89162306a36Sopenharmony_ci do_switch(host, emm_switch); 89262306a36Sopenharmony_ci slot->cached_switch = emm_switch; 89362306a36Sopenharmony_ciout: 89462306a36Sopenharmony_ci host->release_bus(host); 89562306a36Sopenharmony_ci} 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_cistatic const struct mmc_host_ops cvm_mmc_ops = { 89862306a36Sopenharmony_ci .request = cvm_mmc_request, 89962306a36Sopenharmony_ci .set_ios = cvm_mmc_set_ios, 90062306a36Sopenharmony_ci .get_ro = mmc_gpio_get_ro, 90162306a36Sopenharmony_ci .get_cd = mmc_gpio_get_cd, 90262306a36Sopenharmony_ci}; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_cistatic void cvm_mmc_set_clock(struct cvm_mmc_slot *slot, unsigned int clock) 90562306a36Sopenharmony_ci{ 90662306a36Sopenharmony_ci struct mmc_host *mmc = slot->mmc; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci clock = min(clock, mmc->f_max); 90962306a36Sopenharmony_ci clock = max(clock, mmc->f_min); 91062306a36Sopenharmony_ci slot->clock = clock; 91162306a36Sopenharmony_ci} 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_cistatic int cvm_mmc_init_lowlevel(struct cvm_mmc_slot *slot) 91462306a36Sopenharmony_ci{ 91562306a36Sopenharmony_ci struct cvm_mmc_host *host = slot->host; 91662306a36Sopenharmony_ci u64 emm_switch; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci /* Enable this bus slot. */ 91962306a36Sopenharmony_ci host->emm_cfg |= (1ull << slot->bus_id); 92062306a36Sopenharmony_ci writeq(host->emm_cfg, slot->host->base + MIO_EMM_CFG(host)); 92162306a36Sopenharmony_ci udelay(10); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci /* Program initial clock speed and power. */ 92462306a36Sopenharmony_ci cvm_mmc_set_clock(slot, slot->mmc->f_min); 92562306a36Sopenharmony_ci emm_switch = FIELD_PREP(MIO_EMM_SWITCH_POWER_CLASS, 10); 92662306a36Sopenharmony_ci emm_switch |= FIELD_PREP(MIO_EMM_SWITCH_CLK_HI, 92762306a36Sopenharmony_ci (host->sys_freq / slot->clock) / 2); 92862306a36Sopenharmony_ci emm_switch |= FIELD_PREP(MIO_EMM_SWITCH_CLK_LO, 92962306a36Sopenharmony_ci (host->sys_freq / slot->clock) / 2); 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci /* Make the changes take effect on this bus slot. */ 93262306a36Sopenharmony_ci set_bus_id(&emm_switch, slot->bus_id); 93362306a36Sopenharmony_ci do_switch(host, emm_switch); 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci slot->cached_switch = emm_switch; 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci /* 93862306a36Sopenharmony_ci * Set watchdog timeout value and default reset value 93962306a36Sopenharmony_ci * for the mask register. Finally, set the CARD_RCA 94062306a36Sopenharmony_ci * bit so that we can get the card address relative 94162306a36Sopenharmony_ci * to the CMD register for CMD7 transactions. 94262306a36Sopenharmony_ci */ 94362306a36Sopenharmony_ci set_wdog(slot, 0); 94462306a36Sopenharmony_ci writeq(0xe4390080ull, host->base + MIO_EMM_STS_MASK(host)); 94562306a36Sopenharmony_ci writeq(1, host->base + MIO_EMM_RCA(host)); 94662306a36Sopenharmony_ci return 0; 94762306a36Sopenharmony_ci} 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_cistatic int cvm_mmc_of_parse(struct device *dev, struct cvm_mmc_slot *slot) 95062306a36Sopenharmony_ci{ 95162306a36Sopenharmony_ci u32 id, cmd_skew = 0, dat_skew = 0, bus_width = 0; 95262306a36Sopenharmony_ci struct device_node *node = dev->of_node; 95362306a36Sopenharmony_ci struct mmc_host *mmc = slot->mmc; 95462306a36Sopenharmony_ci u64 clock_period; 95562306a36Sopenharmony_ci int ret; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci ret = of_property_read_u32(node, "reg", &id); 95862306a36Sopenharmony_ci if (ret) { 95962306a36Sopenharmony_ci dev_err(dev, "Missing or invalid reg property on %pOF\n", node); 96062306a36Sopenharmony_ci return ret; 96162306a36Sopenharmony_ci } 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci if (id >= CAVIUM_MAX_MMC || slot->host->slot[id]) { 96462306a36Sopenharmony_ci dev_err(dev, "Invalid reg property on %pOF\n", node); 96562306a36Sopenharmony_ci return -EINVAL; 96662306a36Sopenharmony_ci } 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci ret = mmc_regulator_get_supply(mmc); 96962306a36Sopenharmony_ci if (ret) 97062306a36Sopenharmony_ci return ret; 97162306a36Sopenharmony_ci /* 97262306a36Sopenharmony_ci * Legacy Octeon firmware has no regulator entry, fall-back to 97362306a36Sopenharmony_ci * a hard-coded voltage to get a sane OCR. 97462306a36Sopenharmony_ci */ 97562306a36Sopenharmony_ci if (IS_ERR(mmc->supply.vmmc)) 97662306a36Sopenharmony_ci mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci /* Common MMC bindings */ 97962306a36Sopenharmony_ci ret = mmc_of_parse(mmc); 98062306a36Sopenharmony_ci if (ret) 98162306a36Sopenharmony_ci return ret; 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci /* Set bus width */ 98462306a36Sopenharmony_ci if (!(mmc->caps & (MMC_CAP_8_BIT_DATA | MMC_CAP_4_BIT_DATA))) { 98562306a36Sopenharmony_ci of_property_read_u32(node, "cavium,bus-max-width", &bus_width); 98662306a36Sopenharmony_ci if (bus_width == 8) 98762306a36Sopenharmony_ci mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_4_BIT_DATA; 98862306a36Sopenharmony_ci else if (bus_width == 4) 98962306a36Sopenharmony_ci mmc->caps |= MMC_CAP_4_BIT_DATA; 99062306a36Sopenharmony_ci } 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci /* Set maximum and minimum frequency */ 99362306a36Sopenharmony_ci if (!mmc->f_max) 99462306a36Sopenharmony_ci of_property_read_u32(node, "spi-max-frequency", &mmc->f_max); 99562306a36Sopenharmony_ci if (!mmc->f_max || mmc->f_max > 52000000) 99662306a36Sopenharmony_ci mmc->f_max = 52000000; 99762306a36Sopenharmony_ci mmc->f_min = 400000; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci /* Sampling register settings, period in picoseconds */ 100062306a36Sopenharmony_ci clock_period = 1000000000000ull / slot->host->sys_freq; 100162306a36Sopenharmony_ci of_property_read_u32(node, "cavium,cmd-clk-skew", &cmd_skew); 100262306a36Sopenharmony_ci of_property_read_u32(node, "cavium,dat-clk-skew", &dat_skew); 100362306a36Sopenharmony_ci slot->cmd_cnt = (cmd_skew + clock_period / 2) / clock_period; 100462306a36Sopenharmony_ci slot->dat_cnt = (dat_skew + clock_period / 2) / clock_period; 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci return id; 100762306a36Sopenharmony_ci} 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ciint cvm_mmc_of_slot_probe(struct device *dev, struct cvm_mmc_host *host) 101062306a36Sopenharmony_ci{ 101162306a36Sopenharmony_ci struct cvm_mmc_slot *slot; 101262306a36Sopenharmony_ci struct mmc_host *mmc; 101362306a36Sopenharmony_ci int ret, id; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci mmc = mmc_alloc_host(sizeof(struct cvm_mmc_slot), dev); 101662306a36Sopenharmony_ci if (!mmc) 101762306a36Sopenharmony_ci return -ENOMEM; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci slot = mmc_priv(mmc); 102062306a36Sopenharmony_ci slot->mmc = mmc; 102162306a36Sopenharmony_ci slot->host = host; 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci ret = cvm_mmc_of_parse(dev, slot); 102462306a36Sopenharmony_ci if (ret < 0) 102562306a36Sopenharmony_ci goto error; 102662306a36Sopenharmony_ci id = ret; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci /* Set up host parameters */ 102962306a36Sopenharmony_ci mmc->ops = &cvm_mmc_ops; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci /* 103262306a36Sopenharmony_ci * We only have a 3.3v supply, we cannot support any 103362306a36Sopenharmony_ci * of the UHS modes. We do support the high speed DDR 103462306a36Sopenharmony_ci * modes up to 52MHz. 103562306a36Sopenharmony_ci * 103662306a36Sopenharmony_ci * Disable bounce buffers for max_segs = 1 103762306a36Sopenharmony_ci */ 103862306a36Sopenharmony_ci mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED | 103962306a36Sopenharmony_ci MMC_CAP_CMD23 | MMC_CAP_POWER_OFF_CARD | MMC_CAP_3_3V_DDR; 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci if (host->use_sg) 104262306a36Sopenharmony_ci mmc->max_segs = 16; 104362306a36Sopenharmony_ci else 104462306a36Sopenharmony_ci mmc->max_segs = 1; 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci /* DMA size field can address up to 8 MB */ 104762306a36Sopenharmony_ci mmc->max_seg_size = min_t(unsigned int, 8 * 1024 * 1024, 104862306a36Sopenharmony_ci dma_get_max_seg_size(host->dev)); 104962306a36Sopenharmony_ci mmc->max_req_size = mmc->max_seg_size; 105062306a36Sopenharmony_ci /* External DMA is in 512 byte blocks */ 105162306a36Sopenharmony_ci mmc->max_blk_size = 512; 105262306a36Sopenharmony_ci /* DMA block count field is 15 bits */ 105362306a36Sopenharmony_ci mmc->max_blk_count = 32767; 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci slot->clock = mmc->f_min; 105662306a36Sopenharmony_ci slot->bus_id = id; 105762306a36Sopenharmony_ci slot->cached_rca = 1; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci host->acquire_bus(host); 106062306a36Sopenharmony_ci host->slot[id] = slot; 106162306a36Sopenharmony_ci cvm_mmc_switch_to(slot); 106262306a36Sopenharmony_ci cvm_mmc_init_lowlevel(slot); 106362306a36Sopenharmony_ci host->release_bus(host); 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci ret = mmc_add_host(mmc); 106662306a36Sopenharmony_ci if (ret) { 106762306a36Sopenharmony_ci dev_err(dev, "mmc_add_host() returned %d\n", ret); 106862306a36Sopenharmony_ci slot->host->slot[id] = NULL; 106962306a36Sopenharmony_ci goto error; 107062306a36Sopenharmony_ci } 107162306a36Sopenharmony_ci return 0; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_cierror: 107462306a36Sopenharmony_ci mmc_free_host(slot->mmc); 107562306a36Sopenharmony_ci return ret; 107662306a36Sopenharmony_ci} 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ciint cvm_mmc_of_slot_remove(struct cvm_mmc_slot *slot) 107962306a36Sopenharmony_ci{ 108062306a36Sopenharmony_ci mmc_remove_host(slot->mmc); 108162306a36Sopenharmony_ci slot->host->slot[slot->bus_id] = NULL; 108262306a36Sopenharmony_ci mmc_free_host(slot->mmc); 108362306a36Sopenharmony_ci return 0; 108462306a36Sopenharmony_ci} 1085