Zero, Explanation (Importantly, it is necessary to clarify the concept first to help the later understanding)
1. The mmc core-card module implements corresponding operations for the corresponding card, including initialization operations and corresponding bus operation sets. Responsible for things related to the corresponding card protocol layer.
It mainly includes three types of cards: mmc type card, sd type card and sdio type card.
Learn mmc type card first. Follow-up study sd type card.
Corresponding code:
drivers/mmc/core/mmc.c (providing interfaces)
drivers/mmc/core/mmc-ops.c. (Provides operations related to the mmc type card protocol)
drivers/mmc/core/mmc-ops.h
2. In addition, the concept of mmc is emphasized here.
mmc core refers to the core implementation of mmc subsystem, where mmc is a generic term for mmc bus, interface and device, which can be understood as a software architecture.
mmc type card refers to mmc card or emmc.
In short, there are two concepts of mmc, which need to be digested first.
3. MMC bus and mmc_bus
In this paper, these two concepts are different.
mmc_bus refers to the virtual bus abstracted from mmc core, which has nothing to do with the hardware bus corresponding to the mmc device. It is a software concept.
The mmc bus in this paper is a physical concept, a real bus, and is directly related to the host controller.
Overview of API
1. mmc type card matching correlation
-
mmc_attach_mmc
It is provided to the main module of mmc core for obtaining mmc type card information through mmc_host, initializing mmc_card, and partially driving it. Finally, it is registered on mmc_bus.
Prototype: int mmc_attach_mmc (struct mmc_host*host)
2. Operation of mmc type card protocol
mmc_ops provides some operations related to the mmc type card protocol, which will be used during the initialization of MMC in mmc.c.
It is suggested that we have a brief understanding of the content of the mmc protocol. A summary will be made later.
mmc_go_idle
Send CMD0 instruction, GO_IDLE_STATE
Enter mmc card into idle state.
Although it has entered Idle State, the power-on reset process is not necessarily completed, which is mainly determined by reading the busy bit of OCR, and the process boils down to the next step.mmc_send_op_cond
Send CMD1 instruction, SEND_OP_COND
The OCR of card's working voltage register is set here, and the bus bit (bit 31) is used to judge whether the card's power-on reset process is completed or not. If it is not completed, it needs to be sent repeatedly.
After completion, mmc card enters ready state.mmc_all_send_cid
CMD2 instruction, ALL_SEND_CID, is sent here.
Broadcast instructions that cause card to reply to the value of the corresponding CID register. Here the CID register values are obtained and stored in cid.
When completed, the MMC card enters Identification State.mmc_set_relative_addr
Send CMD3 instruction, SET_RELATIVE_ADDR
Set the associated address of the mmc card to card - > rca, which is 0x0001
After completion, the MMC card enters standby mode.mmc_send_csd
Send CMD9 instruction, MMC_SEND_CSD
mmc card is required to send a CSD register and store it in card - > raw_csd, which is the value of the original CSD register.
At this point mmc card is still in standby statemmc_select_card & mmc_deselect_cards
Send CMD7 instruction, SELECT/DESELECT CARD
Select or disconnect the specified card
At this point, the card enters the transfer state. Subsequently, data can be transmitted by various instructions into receive-data state or send-data state in turn.mmc_get_ext_csd
Send CMD8 instruction, SEND_EXT_CSD
The card in transfer state is required to send the ext_csd register, which is retrieved and stored in the ext_csd register.
This will cause card to enter send-data state and exit to transfer state after completion.mmc_switch
Send CMD6 command, MMC_SWITCH
Some bit s for setting ext_csd registersmmc_send_status
Send CMD13 command, MMC_SEND_STATUS
Request card to send its current status registermmc_send_cid
Send CMD10 command, MMC_SEND_CID
Require mmc card to reply to cid registermmc_card_sleepawake
Send CMD5 command, MMC_SLEEP_AWAKE
Make card enter or exit sleep state, depending on the parameters. Sleep state refers to a state of card, referring specifically to emmc 5.1 protocol.
Understanding the operation functions of the above MMC type cards in conjunction with the protocol is helpful to understand the initialization code of the subsequent MMC cards. Refer specifically to Section 5.
II. Data Structure
1,mmc_ops & mmc_ops_unsafe
Strct mmc_bus_ops denotes the operation set of mmc host on the bus, which is determined by the card device of the host. The corresponding operation sets of mmc type card and sd type card are different.
Mmc_ops and mmc_ops_unsafe denote the host-to-bus operation set to which mmc type card belongs.
static const struct mmc_bus_ops mmc_ops = {
.awake = mmc_awake,
.sleep = mmc_sleep,
.remove = mmc_remove,
.detect = mmc_detect,
.suspend = NULL,
.resume = NULL,
.power_restore = mmc_power_restore,
.alive = mmc_alive,
.change_bus_speed = mmc_change_bus_speed,
};
static const struct mmc_bus_ops mmc_ops_unsafe = {
.awake = mmc_awake, // Exit the mmc type card on the mmc bus from sleep state
.sleep = mmc_sleep, // Enter mmc type card of mmc bus into sleep state
.remove = mmc_remove, // Release mmc type card
.detect = mmc_detect, // Detecting whether mmc type card of mmc bus is pulled out
.suspend = mmc_suspend, // suspend drops the mmc type card on the mmc bus. Note that not only will card enter sleep state, but also clock s and mmc cache will be operated on.
.resume = mmc_resume, // mmc type card on mmc bus on resume
.power_restore = mmc_power_restore, // Restoring the power state of mmc type card on mmc bus
.alive = mmc_alive, // Detection of mmc type card status on mmc bus
.change_bus_speed = mmc_change_bus_speed, // Modify the clock frequency of mmc bus
};
The difference between mmc_ops_unsafe and mmc_ops is whether suspend and resume methods are implemented.
For card non-removable host s, the mmc_ops_unsafe mmc_bus_ops is needed to support suspend and resume.
The reason why the MMC bus is constantly explained in the above annotations is to emphasize that it should be distinguished from the mmc_bus virtual bus, where the MMC bus is a physical concept and is directly related to the host controller.
2,mmc_type
There are many properties defined for mmc_card in struct device_type mmc_type, which can be viewed in sysfs.
/sys/class/mmc_host/mmc0/mmc0:0001
//Or/sys/bus/mmc/devices/mmc0:0001You can see the following properties below
block cid csd date driver enhanced_area_offset enhanced_area_size erase_size fwrev hwrev
manfid name oemid power preferred_erase_size prv raw_rpmb_size_mult rel_sectors
runtime_pm_timeout serial subsystem type uevent
The mmc_type correspondence is implemented as follows:
static struct device_type mmc_type = {
.groups = mmc_attr_groups,
};
static const struct attribute_group *mmc_attr_groups[] = {
&mmc_std_attr_group,
NULL,
};
static struct attribute_group mmc_std_attr_group = {
.attrs = mmc_std_attrs,
};
MMC_DEV_ATTR(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1],
card->raw_cid[2], card->raw_cid[3]);
MMC_DEV_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1],
card->raw_csd[2], card->raw_csd[3]);
//Let's skip a little bit.
MMC_DEV_ATTR(raw_rpmb_size_mult, "%#x\n", card->ext_csd.raw_rpmb_size_mult);
MMC_DEV_ATTR(rel_sectors, "%#x\n", card->ext_csd.rel_sectors);
static struct attribute *mmc_std_attrs[] = {
&dev_attr_cid.attr,
&dev_attr_csd.attr,
&dev_attr_date.attr,
&dev_attr_erase_size.attr,
&dev_attr_preferred_erase_size.attr,
&dev_attr_fwrev.attr,
&dev_attr_hwrev.attr,
//...
&dev_attr_rel_sectors.attr,
NULL,
};
Additionally, it can be found that the information is read from cid register and ext_csd register of mmc_card.
3. Interface Code Description
1. Implementation of mmc_attach_mmc
It is used to get mmc type card information through mmc_host, initialize mmc_card, drive part of it, and register it on mmc_bus.
-
Main work:
- Setting Bus Mode
- Choose a minimum operating voltage supported by both card and host
- For different type s of card s, the operation protocols on the corresponding MMC bus may also be different. So you need to set up the corresponding bus operation set (mmc_host-> bus_ops)
- Initialize card to work (mmc_init_card)
- Construct the corresponding mmc_card for card and register it in mmc_bus (mmc_add_card, refer specifically to "mmc core-bus module description")
The code is as follows:
int mmc_attach_mmc(struct mmc_host *host)
{
int err;
u32 ocr;
BUG_ON(!host);
WARN_ON(!host->claimed);
/* Set correct bus mode for MMC before attempting attach */
///////////////////////////////////////////////////////////////////////// / Before trying to match, set the correct bus mode first.
if (!mmc_host_is_spi(host))
mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN);
//////////////////////////////////////////////////////////////////////// / ocr register for obtaining card
err = mmc_send_op_cond(host, 0, &ocr);
// Send the CMD1 command (MMC_SEND_OP_COND) with a parameter of 0
// Here, a 32-bit OCR including a working voltmeter supported by the card device is obtained and stored in the OCR variable.
// If the IO voltage of Host can be adjusted, the OCR needs to be read before adjusting. In order not to make the card enter Inactive State by mistake, CMD1 without parameters can be sent to the MMC card, so that only OCR registers can be obtained without changing the status of the card.
///////////////////////////////////////////////////////////////////////// / For different type s of card s, the operation protocols on the corresponding mmc bus may also be different.
///////////////////////////////////////////////////////////////////////////// / / / / So here is the set of bus operations for mmc_host, which is mmc_ops_unsafe or mmc_ops, as explained above.
mmc_attach_bus_ops(host);
// Setting host - > bus_ops, that is to say, will select an operation set for host's bus. For non-removable hosts, this corresponds to mmc_ops_unsafe.
/////////////////////////////////////////////////////////////////////////// / Choose a minimum voltage for card supported by both HOST and card.
if (host->ocr_avail_mmc)
host->ocr_avail = host->ocr_avail_mmc; // Choose the available OCR value of mmc as the ocr_avail value of host
if (ocr & 0x7F) {
ocr &= ~0x7F; // In the standard MMC protocol, bit6-0 bits of OCR register belong to reserved bits and will not be used, so it should be cleared here.
}
host->ocr = mmc_select_voltage(host, ocr); // Select a minimum voltage supported by both HOST and card through the OCR register
////////////////////////////////////////////////////////////////////////call mmc_init_card to initialize the MMC card. This is the core function, which will be explained further.
err = mmc_init_card(host, host->ocr, NULL); // Initialize the mmc type card and assign and initialize a corresponding mmc_card for it
if (err)
goto err;
//////////////////////////////////////////////////////////////////////// / / The allocated mmc_card is registered in mmc_bus.
mmc_release_host(host); // Release the host first, probably in mmc_add_card
err = mmc_add_card(host->card);
// Call to mmc_add_card and register card into device driver model.
// At this point, the mmc_card is hung on the mmc_bus, which matches the mmc driver like block on the mmc_bus. Specifically, when you study mmc card driver, explain it again.
mmc_claim_host(host); // Re-apply for host
if (err)
goto remove_card;
/////////////////////////////////////////////////////////////////// clock scaling related things, here for the time being do not care about.
mmc_init_clk_scaling(host);
register_reboot_notifier(&host->card->reboot_notify);
return 0;
remove_card:
mmc_release_host(host);
mmc_remove_card(host->card);
mmc_claim_host(host);
host->card = NULL;
err:
mmc_detach_bus(host);
pr_err("%s: error %d whilst initialising MMC card\n",
mmc_hostname(host), err);
return err;
}
Key points
(1) In the attach ment process, there is an important function mmc_init_card. The fourth section is about this function.
(2) After calling mmc_add_card, mmc_card hangs on mmc_bus, which matches the block (mmc_driver) on mmc_bus. The corresponding block (mmc_driver) will probe, drive the card, and realize the actual function of the card (that is, the function of the storage device). It will be connected to the block device subsystem. Specific in the study of mmc card driver time to explain.
IV. Interior Core Code Description of mmc type card
1,mmc_init_card
In the third section, we can see that one of the core functions of mmc_attach_mmc is mmc_init_card, which is used to initialize mmc type card substantially and assign and initialize a corresponding mmc_card for it. This part is related to the protocol. We need to learn about the MMC protocol first.
-
Main work
- Initialize the mmc type card according to the protocol and put it into the corresponding state (standby state)
- Construct and set the corresponding mmc_card for mmc type card
- Get card information from card's CSD register and ext_csd register and set it to the corresponding member of mmc_card
- Modify ext_csd register values according to host attributes and some requirements
- Setting the clock frequency and bit width of mmc bus
The code is as follows
static int mmc_init_card(struct mmc_host *host, u32 ocr,
struct mmc_card *oldcard)
{
// Struct mmc_host*host: the host used by the mmc card
// ocr: indicates the voltage to be used by the host. In mmc_attach_mmc, a minimum voltage struct mmc_card* card supported by both HOST and card has been obtained.
int err = 0;
u32 cid[4];
u32 rocr;
u8 *ext_csd = NULL;
BUG_ON(!host);
WARN_ON(!host->claimed);
/* Set correct bus mode for MMC before attempting init */
if (!mmc_host_is_spi(host))
mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN); // Set Bus Mode to Open-Leak Mode
/////////////////////////////////////////////////////////////////////////// / / Select a card from the mmc bus according to the mmc protocol (the initialization process of the protocol)
mmc_go_idle(host);
// Send CMD0 instruction, GO_IDLE_STATE
// Enter mmc card into idle state.
// Although it has entered Idle State, the power-on reset process is not necessarily completed, which is mainly determined by reading the busy bit of OCR, and the process boils down to the next step.
/* The extra bit indicates that we support high capacity */
err = mmc_send_op_cond(host, ocr | (1 << 30), &rocr);
// Send CMD1 instruction, SEND_OP_COND
// The OCR of card's working voltage register is set here, and the bus bit (bit 31) is used to judge whether the card's power-on reset process is completed or not. If it is not completed, it needs to be sent repeatedly.
// After completion, mmc card enters ready state.
/*
* Fetch CID from card.
*/
if (mmc_host_is_spi(host))
err = mmc_send_cid(host, cid);
else
err = mmc_all_send_cid(host, cid);
// CMD2 instruction, ALL_SEND_CID, is sent here.
// Broadcast instructions that cause card to reply to the value of the corresponding CID register. Here the CID register values are obtained and stored in cid.
// When completed, the MMC card enters Identification State.
if (oldcard) {
. . .
} else {
////////////////////////////////////////////////////////////////////////////////calling mmc_alloc_card to allocate a mmc_card and set it up partially
card = mmc_alloc_card(host, &mmc_type);
// A struct mmc_card structure is allocated and initialized for card, and a large number of attributes are defined for MMC in mmc_type.
// Reference to "mmc core - bus module description - mmc_alloc_card"
card->type = MMC_TYPE_MMC; // Set the type of card to MMC_TYPE_MMC
card->rca = 1; // Set the RCA address of card to 1
memcpy(card->raw_cid, cid, sizeof(card->raw_cid)); // Store the read CID in card - > raw_cid, which is the original CID value
card->reboot_notify.notifier_call = mmc_reboot_notify;
host->card = card; // Associate mmc_card with mmc_host
}
/////////////////////////////////////////////////////////////////////////// Set CardRCA Address
if (!mmc_host_is_spi(host)) {
err = mmc_set_relative_addr(card);
// Send CMD3 instruction, SET_RELATIVE_ADDR
// Set the associated address of the mmc card to card - > rca, which is 0x0001
// After completion, the MMC card enters standby mode.
mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
// Set bus mode to MMC_BUSMODE_PUSHPULL
}
///////////////////////////////////////////////////////////////////////////////// / / / Gets information from the CSD register of card and the ext_csd register and sets it in the corresponding members of mmc_card.
if (!oldcard) {
/*
* Fetch CSD from card.
*/
err = mmc_send_csd(card, card->raw_csd);
// Send CMD9 instruction, MMC_SEND_CSD
// mmc card is required to send a CSD register and store it in card - > raw_csd, which is the value of the original CSD register.
// At this point mmc card is still in standby state
err = mmc_decode_csd(card);
// Parse raw_csd, get the values of bit s and set them to the corresponding members in card - > CSD
err = mmc_decode_cid(card);
// Parse raw_cid, get the values of bit s and set them to the corresponding members in card - > CID
}
/*
* Select card, as all following commands rely on that.
*/
if (!mmc_host_is_spi(host)) {
err = mmc_select_card(card);
// Send CMD7 instruction, SELECT/DESELECT CARD
// Select or disconnect the specified card
// At this point, the card enters the transfer state. Subsequently, data can be transmitted by various instructions into receive-data state or send-data state in turn.
}
if (!oldcard) {
err = mmc_get_ext_csd(card, &ext_csd);
// Send CMD8 instruction, SEND_EXT_CSD
// The card in transfer state is required to send the ext_csd register, which is retrieved and stored in the ext_csd register.
// This will cause card to enter send-data state and exit to transfer state after completion.
card->cached_ext_csd = ext_csd; // Store the original ext_csd value in card - > cached_ext_csd, indicating that a cache used to save ext_csd may not be synchronized with ext_csd of card
err = mmc_read_ext_csd(card, ext_csd); // Resolve ext_csd values, get the values of bit s and set them to the corresponding members in card - > ext_csd
if (!(mmc_card_blockaddr(card)) && (rocr & (1<<30)))
mmc_card_set_blockaddr(card);
/* Erase size depends on CSD and Extended CSD */
mmc_set_erase_size(card); // Set erase_size of card, erase bytes in sector, read 512K
if (card->ext_csd.sectors && (rocr & MMC_CARD_SECTOR_ADDR))
mmc_card_set_blockaddr(card);
}
////////////////////////////////////////////////////////////////////////// / Modify ext_csd register according to host attributes and some requirements
/*
* If enhanced_area_en is TRUE, host needs to enable ERASE_GRP_DEF
* bit. This bit will be lost every time after a reset or power off.
*/
if (card->ext_csd.enhanced_area_en ||
(card->ext_csd.rev >= 3 && (host->caps2 & MMC_CAP2_HC_ERASE_SZ))) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_ERASE_GROUP_DEF, 1,
card->ext_csd.generic_cmd6_time);
// Send CMD6 command, MMC_SWITCH
// Some bit s for setting ext_csd registers
// When enhanced_area_en is set, the host needs to set the EXT_CSD_ERASE_GROUP_DEF bit of the ext_csd register to 1.
}
if (card->ext_csd.part_config & EXT_CSD_PART_CONFIG_ACC_MASK) {
card->ext_csd.part_config &= ~EXT_CSD_PART_CONFIG_ACC_MASK;
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONFIG,
card->ext_csd.part_config,
card->ext_csd.part_time);
// Send CMD6 command, MMC_SWITCH
// Some bit s for setting ext_csd registers
// Set the EXT_CSD_CMD_SET_NORMAL bit in the ext_csd register to EXT_CSD_PART_CONFIG
card->part_curr = card->ext_csd.part_config &
EXT_CSD_PART_CONFIG_ACC_MASK;
}
if ((host->caps2 & MMC_CAP2_POWEROFF_NOTIFY) &&
(card->ext_csd.rev >= 6)) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_POWER_OFF_NOTIFICATION,
EXT_CSD_POWER_ON,
card->ext_csd.generic_cmd6_time);
// Send CMD6 command, MMC_SWITCH
// Some bit s for setting ext_csd registers
// Set the EXT_CSD_POWER_OFF_NOTIFICATION bit in the ext_csd register to EXT_CSD_POWER_ON
}
///////////////////////////////////////////////////////////////////////// Setting the clock frequency and bit width of mmc bus
err = mmc_select_bus_speed(card, ext_csd); // Maximum bus speed supported by both host and card activation
//... Here, filter out some code that sets ext_csd
if (!oldcard) {
if (card->ext_csd.bkops_en) {
INIT_DELAYED_WORK(&card->bkops_info.dw,
mmc_start_idle_time_bkops);
// If emmc supports bkops, initialize card - > bkops_info. DW to work for mmc_start_idle_time_bkops
}
}
return 0;
}
A subsequent article, "Analyzing the Command Flow in the Initialization Process of emmc with log" will illustrate the actual process of mmc_init_card mentioned above.
2. Implementation of mmc_ops_unsafe correlation function
Select several key points for explanation:
static const struct mmc_bus_ops mmc_ops_unsafe = {
.awake = mmc_awake, // Exit the mmc type card on the mmc bus from sleep state
.sleep = mmc_sleep, // Enter mmc type card of mmc bus into sleep state
.remove = mmc_remove, // Release mmc type card
.detect = mmc_detect, // Detecting whether mmc type card of mmc bus is pulled out
.suspend = mmc_suspend, // suspend drops the mmc type card on the mmc bus. Note that not only will card enter sleep state, but also clock s and mmc cache will be operated on.
.resume = mmc_resume, // mmc type card on mmc bus on resume
.power_restore = mmc_power_restore, // Restoring the power state of mmc type card on mmc bus
.alive = mmc_alive, // Detection of mmc type card status on mmc bus
.change_bus_speed = mmc_change_bus_speed, // Modify the clock frequency of mmc bus
};
/**********************Exit mmc type card on mmc bus from sleep state******************************/
static int mmc_awake(struct mmc_host *host)
{
//...
if (card && card->ext_csd.rev >= 3) { // Determine whether the version is larger than 3
err = mmc_card_sleepawake(host, 0);
// Send CMD5 instruction, MMC_SLEEP_AWAKE, parameter 0, meaning exit sleep state. (If parameter 1, enter sleep state)
// After completion, the MMC card moves from sleep state to standby mode.
}
//...
}
/**********************Detecting whether mmc type card of mmc bus pulls out *********************************/
static void mmc_detect(struct mmc_host *host)
{
int err;
mmc_rpm_hold(host, &host->card->dev);
mmc_claim_host(host);
//////////////////////////////////////////////////////////////// / Detecting whether the card has been pulled out
err = _mmc_detect_card_removed(host);
mmc_release_host(host);
/*
* if detect fails, the device would be removed anyway;
* the rpm framework would mark the device state suspended.
*/
//////////////////////////////////////////////////////////////////// / card has not been pulled out, indicating that there is an abnormality, and the rpm status marked card is suspend.
if (!err)
mmc_rpm_release(host, &host->card->dev);
/////////////////////////////////////////////////////////////// card was pulled out and released normally.
if (err) {
mmc_remove(host);
mmc_claim_host(host);
mmc_detach_bus(host);
mmc_power_off(host);
mmc_release_host(host);
}
}
/********************** Modification of mmc bus clock frequency *******************************/
/**
* mmc_change_bus_speed() - Change MMC card bus frequency at runtime
* @host: pointer to mmc host structure
* @freq: pointer to desired frequency to be set
*
* Change the MMC card bus frequency at runtime after the card is
* initialized. Callers are expected to make sure of the card's
* state (DATA/RCV/TRANSFER) beforing changing the frequency at runtime.
*/
static int mmc_change_bus_speed(struct mmc_host *host, unsigned long *freq)
{
int err = 0;
struct mmc_card *card;
mmc_claim_host(host);
/*
* Assign card pointer after claiming host to avoid race
* conditions that may arise during removal of the card.
*/
card = host->card;
if (!card || !freq) {
err = -EINVAL;
goto out;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// / / /
if (mmc_card_highspeed(card) || mmc_card_hs200(card)
|| mmc_card_ddr_mode(card)
|| mmc_card_hs400(card)) {
if (*freq > card->ext_csd.hs_max_dtr)
*freq = card->ext_csd.hs_max_dtr;
} else if (*freq > card->csd.max_dtr) {
*freq = card->csd.max_dtr;
}
if (*freq < host->f_min)
*freq = host->f_min;
////////////////////////////////////////////////////////////////////////// / according to the actual frequency value to set the clock.
if (mmc_card_hs400(card)) {
err = mmc_set_clock_bus_speed(card, *freq);
if (err)
goto out;
} else {
mmc_set_clock(host, (unsigned int) (*freq));
}
///////////////////////////////////////////////////////////////////// / / For hs200, execute_tuning is needed to select a suitable sampling point after modifying the frequency.
if (mmc_card_hs200(card) && card->host->ops->execute_tuning) {
/*
* We try to probe host driver for tuning for any
* frequency, it is host driver responsibility to
* perform actual tuning only when required.
*/
mmc_host_clk_hold(card->host);
err = card->host->ops->execute_tuning(card->host,
MMC_SEND_TUNING_BLOCK_HS200);
mmc_host_clk_release(card->host);
if (err) {
pr_warn("%s: %s: tuning execution failed %d. Restoring to previous clock %lu\n",
mmc_hostname(card->host), __func__, err,
host->clk_scaling.curr_freq);
mmc_set_clock(host, host->clk_scaling.curr_freq); // Sampling failure, set back to the original clock frequency
}
}
out:
mmc_release_host(host);
return err;
}
5. Description of mmc ops interface
1, explain
- mmc_ops provides some operations related to the mmc type card protocol, which will be used during the initialization of MMC in mmc.c.
- These operations all initiate mmc requests, so the mmc request API of the mmc core main module is invoked and explained in the mmc core.
- It is suggested that we have a brief understanding of the content of the mmc protocol. A summary will be made later.
2. Code description
Typical and special interfaces are described below.
- mmc_send_status (typical)
Send CMD13 command, MMC_SEND_STATUS
Request card to send its current status register
int mmc_send_status(struct mmc_card *card, u32 *status)
{
int err;
struct mmc_command cmd = {0};
BUG_ON(!card);
BUG_ON(!card->host);
/////////////////////////////////////////////////////////////////// / / / / / / The struct mmc_command is constructed mainly according to corresponding commands.
cmd.opcode = MMC_SEND_STATUS; // Set the command opcode, which is set to MMC_SEND_STATUS, or CMD13
if (!mmc_host_is_spi(card->host))
cmd.arg = card->rca << 16; // Set the corresponding parameters of the command, which is set to the RCA address of card
cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC; // Set some identifiers for requests, including command type, response type, etc.
//////////////////////////////////////////////////////////////////// / / Call mmc_wait_for_cmd to send command request and wait for command processing to complete.
err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
if (err)
return err;
/* NOTE: callers are required to understand the difference
* between "native" and SPI format status words!
*/
/////////////////////////////////////////////////////////////// / / Treatment of response
if (status)
*status = cmd.resp[0]; // According to the protocol, response[0] stores status, so cmd.resp[0] is extracted here.
return 0;
}
The implementation methods of mmc_go_idle, mmc_select_card, mmc_all_send_cid, mmc_set_relative_addr, mmc_send_cxd_native and so on are similar. The main difference lies in the structure of commands and the processing of response data.
- mmc_send_op_cond (special)
Send CMD1 instruction, SEND_OP_COND
In idle state, the voltage range supported by Host is transmitted to the card, the value of OCR is returned to the card and the state of power-on reset is returned. If the voltage parameter is 0, the card only returns the value of OCR and does not make a judgment. If the transmitted voltage parameter exists, it is compared with the OCR of the card itself. If it does not, the card enters Inactive State. If it does, it returns the value of the OCR register.
In fact, it is similar to the typical interface, but its special point is that it needs to use the bus bit (bit 31) to judge whether the car's power-on and reset process is completed or not, and if it is not completed, it needs to be sent repeatedly.
int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
{
struct mmc_command cmd = {0};
int i, err = 0;
BUG_ON(!host);
/////////////////////////////////////////////////////////////////// / / / / / / The struct mmc_command is constructed mainly according to corresponding commands.
cmd.opcode = MMC_SEND_OP_COND;
cmd.arg = mmc_host_is_spi(host) ? 0 : ocr;
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR;
///////////////////////////////////////////////////////////////////// / / / / / / / It is necessary to judge whether the status busy (bit31) is completed or not, and if it is not, it is necessary to send it repeatedly.
for (i = 100; i; i--) {
err = mmc_wait_for_cmd(host, &cmd, 0);
if (err)
break;
/* if we're just probing, do a single pass */
if (ocr == 0) // ocr is 0, which means that only the value of ocr register is read, and no judgment is made.
break;
/* otherwise wait until reset completes */
if (mmc_host_is_spi(host)) {
if (!(cmd.resp[0] & R1_SPI_IDLE))
break;
} else {
if (cmd.resp[0] & MMC_CARD_BUSY)
break;
// If the transmitted voltage parameter exists, it is compared with the OCR of the card itself. If it does not, the card enters Inactive State. If it does, it returns the value of the OCR register.
// At the same time, it is necessary to judge the busy bit of OCR register to determine whether the power-on reset is completed.
}
err = -ETIMEDOUT;
mmc_delay(10);
}
if (rocr && !mmc_host_is_spi(host))
*rocr = cmd.resp[0];
return err;
}
- mmc_send_ext_csd
Send CMD8 instruction, SEND_EXT_CSD
The card in transfer state is required to send the ext_csd register, which is retrieved and stored in the ext_csd register.
This will cause card to enter send-data state and exit to transfer state after completion.
The special point is that it involves data transmission on DATA line and calls to internal interface mmc_send_cxd_data.
As follows:
int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd)
{
return mmc_send_cxd_data(card, card->host, MMC_SEND_EXT_CSD,
ext_csd, 512);
}
static int
mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host,
u32 opcode, void *buf, unsigned len)
{
struct mmc_request mrq = {NULL};
struct mmc_command cmd = {0};
struct mmc_data data = {0};
struct scatterlist sg;
void *data_buf;
int is_on_stack;
is_on_stack = object_is_on_stack(buf);
if (is_on_stack) {
/*
* dma onto stack is unsafe/nonportable, but callers to this
* routine normally provide temporary on-stack buffers ...
*/
data_buf = kmalloc(len, GFP_KERNEL);
if (!data_buf)
return -ENOMEM;
} else
data_buf = buf;
////////////////////////////////////////////////////////////////////// / / / / Because data transmission on the data line is involved, mmc_request requests need to be constructed.
mrq.cmd = &cmd; // Setting the command package in the mmc_request request request
mrq.data = &data; // Setting Packets in the mmc_request Request Request
/////////////////////////////////////////////////////////////////// / / / / / / The struct mmc_command is constructed mainly according to corresponding commands.
cmd.opcode = opcode;
cmd.arg = 0;
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
//////////////////////////////////////////////////////////////////// / / / / / / / / / The struct mmc_data is constructed mainly according to the corresponding command data package.
data.blksz = len;
data.blocks = 1;
data.flags = MMC_DATA_READ;
data.sg = &sg;
data.sg_len = 1;
sg_init_one(&sg, data_buf, len);
if (opcode == MMC_SEND_CSD || opcode == MMC_SEND_CID) {
data.timeout_ns = 0;
data.timeout_clks = 64;
} else
mmc_set_data_timeout(&data, card);
///////////////////////////////////////////////////////////////////// / / Initiate MMC request and wait for mmc_request processing to complete
mmc_wait_for_req(host, &mrq);
if (is_on_stack) {
memcpy(buf, data_buf, len);
kfree(data_buf);
}
if (cmd.error)
return cmd.error;
if (data.error)
return data.error;
return 0;
}
- mmc_switch
Send CMD6 command, MMC_SWITCH
Some bit s used to set the ext_csd register.
The special point is that the CMD6 command will be issued in u mmc_switch, which will cause card to enter programming state. Therefore, in u mmc_switch, the status of card must be obtained until card exits programming state. This part is achieved through CMD13.
int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
unsigned int timeout_ms)
{
return __mmc_switch(card, set, index, value, timeout_ms, true, false);
}
int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
unsigned int timeout_ms, bool use_busy_signal,
bool ignore_timeout)
{
int err;
struct mmc_command cmd = {0};
unsigned long timeout;
u32 status;
BUG_ON(!card);
BUG_ON(!card->host);
/////////////////////////////////////////////////////////////////// / / / / / / The struct mmc_command is constructed mainly according to corresponding commands.
cmd.opcode = MMC_SWITCH;
cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
(index << 16) |
(value << 8) |
set;
cmd.flags = MMC_CMD_AC;
if (use_busy_signal)
cmd.flags |= MMC_RSP_SPI_R1B | MMC_RSP_R1B;
else
cmd.flags |= MMC_RSP_SPI_R1 | MMC_RSP_R1;
cmd.cmd_timeout_ms = timeout_ms;
cmd.ignore_timeout = ignore_timeout;
//////////////////////////////////////////////////////////////////// / / Call mmc_wait_for_cmd to send command request and wait for command processing to complete.
err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
if (err)
return err;
/* No need to check card status in case of unblocking command */
if (!use_busy_signal)
return 0;
///////////////////////////////////////////////////////////////////// / / Call mmc_send_status to send CMD13 to get card status and wait for card to exit programming state.
timeout = jiffies + msecs_to_jiffies(MMC_OPS_TIMEOUT_MS);
do {
err = mmc_send_status(card, &status);
if (err)
return err;
if (card->host->caps & MMC_CAP_WAIT_WHILE_BUSY)
break;
if (mmc_host_is_spi(card->host))
break;
/* Timeout if the device never leaves the program state. */
if (time_after(jiffies, timeout)) {
pr_err("%s: Card stuck in programming state! %s\n",
mmc_hostname(card->host), __func__);
return -ETIMEDOUT;
}
} while (R1_CURRENT_STATE(status) == R1_STATE_PRG);
if (mmc_host_is_spi(card->host)) {
if (status & R1_SPI_ILLEGAL_COMMAND)
return -EBADMSG;
} else {
if (status & 0xFDFFA000)
pr_warning("%s: unexpected status %#x after "
"switch", mmc_hostname(card->host), status);
if (status & R1_SWITCH_ERROR)
return -EBADMSG;
}
return 0;
}
A subsequent article, "Analyzing the Command Flow in the Initialization Process of emmc with log" will illustrate the actual process of mmc_init_card mentioned above.