在调用mmc_start_host 来enable一个emmc host的时候
void mmc_start_host(struct mmc_host *host)
{
host->f_init = max(freqs[0], host->f_min);
host->rescan_disable = 0;
host->ios.power_mode = MMC_POWER_UNDEFINED;
mmc_claim_host(host);
if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP)
mmc_power_off(host);
else
mmc_power_up(host, host->ocr_avail);
mmc_release_host(host);
mmc_gpiod_request_cd_irq(host);
_mmc_detect_change(host, 0, false);
}
在mmc_start_host 通过_mmc_detect_change 来启动scan连接在这个host上的mmc device
static void _mmc_detect_change(struct mmc_host *host, unsigned long delay,
bool cd_irq)
{
/*
* If the device is configured as wakeup, we prevent a new sleep for
* 5 s to give provision for user space to consume the event.
*/
if (cd_irq && !(host->caps & MMC_CAP_NEEDS_POLL) &&
device_can_wakeup(mmc_dev(host)))
pm_wakeup_event(mmc_dev(host), 5000);
host->detect_change = 1;
mmc_schedule_delayed_work(&host->detect, delay);
}
置位host->detect_change,并调用mmc_schedule_delayed_work 来scan device
static int mmc_schedule_delayed_work(struct delayed_work *work,
unsigned long delay)
{
return queue_delayed_work(system_freezable_wq, work, delay);
}
但是这个host->detect这个work对应的func 是通过下面的方式赋值
而在mmc_alloc_host 中的赋值如下:
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
这样当调用mmc_schedule_delayed_work的时候,会调用mmc_rescan
在mmc_rescan 中最要的如下:
for (i = 0; i < ARRAY_SIZE(freqs); i++) {
if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))
break;
if (freqs[i] <= host->f_min)
break;
}
static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
可以看到每个host 最多scan四次,在当前freqs上找到device就退出
static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
{
host->f_init = freq;
#ifdef CONFIG_MMC_DEBUG
pr_info("%s: %s: trying to init card at %u Hz\n",
mmc_hostname(host), __func__, host->f_init);
#endif
mmc_power_up(host, host->ocr_avail);
/*
* Some eMMCs (with VCCQ always on) may not be reset after power up, so
* do a hardware reset if possible.
*/
mmc_hw_reset_for_init(host);
/*
* sdio_reset sends CMD52 to reset card. Since we do not know
* if the card is being re-initialized, just send it. CMD52
* should be ignored by SD/eMMC cards.
* Skip it if we already know that we do not support SDIO commands
*/
if (!(host->caps2 & MMC_CAP2_NO_SDIO))
sdio_reset(host);
mmc_go_idle(host);
if (!(host->caps2 & MMC_CAP2_NO_SD))
mmc_send_if_cond(host, host->ocr_avail);
/* Order's important: probe SDIO, then SD, then MMC */
if (!(host->caps2 & MMC_CAP2_NO_SDIO))
if (!mmc_attach_sdio(host))
return 0;
if (!(host->caps2 & MMC_CAP2_NO_SD))
if (!mmc_attach_sd(host))
return 0;
if (!(host->caps2 & MMC_CAP2_NO_MMC))
if (!mmc_attach_mmc(host))
return 0;
mmc_power_off(host);
return -EIO;
}
可以从mmc_rescan_try_freq看到,emmc host上可以挂三种设备,这三种设别只能挂一个不能共存,且有一定的优先级
其中优先级为:mmc->sd->sdio
以mmc_attach_sdio->mmc_add_card为例开机会打印如下log
[ 11.565233] mmc_host mmc2: Bus speed (slot 0) = 25000000Hz (slot req 2500000)
++ Setting up mdev
[ 11.587289] mmc2: queuing unknown CIS tuple 0x91 (3 bytes)
[ 11.599206] mmc2: new SDIO card at address 0001
在mmc_rescan_try_freq 中是通过host->caps2 & MMC_CAP2_NO_SDIO 来判断是否有SDIO的,也就是如果在mmc_of_parse中没有添加这个flag就默认调用mmc_attach_sdio。而mmc_of_parse 是在parse host对应的dts,也就是说在dts中如果没有
if (of_property_read_bool(np, "no-sdio"))
host->caps2 |= MMC_CAP2_NO_SDIO;
就默认调用mmc_attach_sdio
int mmc_of_parse(struct mmc_host *host)
{
if (of_property_read_bool(np, "disable-wp"))
host->caps2 |= MMC_CAP2_NO_WRITE_PROTECT;
/* See the comment on CD inversion above */
if (ro_cap_invert ^ ro_gpio_invert)
host->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
if (of_property_read_bool(np, "cap-sd-highspeed"))
host->caps |= MMC_CAP_SD_HIGHSPEED;
if (of_property_read_bool(np, "cap-mmc-highspeed"))
host->caps |= MMC_CAP_MMC_HIGHSPEED;
if (of_property_read_bool(np, "sd-uhs-sdr12"))
host->caps |= MMC_CAP_UHS_SDR12;
if (of_property_read_bool(np, "sd-uhs-sdr25"))
host->caps |= MMC_CAP_UHS_SDR25;
if (of_property_read_bool(np, "sd-uhs-sdr50"))
host->caps |= MMC_CAP_UHS_SDR50;
if (of_property_read_bool(np, "sd-uhs-sdr104"))
host->caps |= MMC_CAP_UHS_SDR104;
if (of_property_read_bool(np, "sd-uhs-ddr50"))
host->caps |= MMC_CAP_UHS_DDR50;
if (of_property_read_bool(np, "cap-power-off-card"))
host->caps |= MMC_CAP_POWER_OFF_CARD;
if (of_property_read_bool(np, "cap-mmc-hw-reset"))
host->caps |= MMC_CAP_HW_RESET;
if (of_property_read_bool(np, "cap-sdio-irq"))
host->caps |= MMC_CAP_SDIO_IRQ;
if (of_property_read_bool(np, "full-pwr-cycle"))
host->caps2 |= MMC_CAP2_FULL_PWR_CYCLE;
if (of_property_read_bool(np, "keep-power-in-suspend"))
host->pm_caps |= MMC_PM_KEEP_POWER;
}
void mmc_start_host(struct mmc_host *host)
{
host->f_init = max(freqs[0], host->f_min);
host->rescan_disable = 0;
host->ios.power_mode = MMC_POWER_UNDEFINED;
mmc_claim_host(host);
if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP)
mmc_power_off(host);
else
mmc_power_up(host, host->ocr_avail);
mmc_release_host(host);
mmc_gpiod_request_cd_irq(host);
_mmc_detect_change(host, 0, false);
}
在mmc_start_host 通过_mmc_detect_change 来启动scan连接在这个host上的mmc device
static void _mmc_detect_change(struct mmc_host *host, unsigned long delay,
bool cd_irq)
{
/*
* If the device is configured as wakeup, we prevent a new sleep for
* 5 s to give provision for user space to consume the event.
*/
if (cd_irq && !(host->caps & MMC_CAP_NEEDS_POLL) &&
device_can_wakeup(mmc_dev(host)))
pm_wakeup_event(mmc_dev(host), 5000);
host->detect_change = 1;
mmc_schedule_delayed_work(&host->detect, delay);
}
置位host->detect_change,并调用mmc_schedule_delayed_work 来scan device
static int mmc_schedule_delayed_work(struct delayed_work *work,
unsigned long delay)
{
return queue_delayed_work(system_freezable_wq, work, delay);
}
但是这个host->detect这个work对应的func 是通过下面的方式赋值
而在mmc_alloc_host 中的赋值如下:
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
这样当调用mmc_schedule_delayed_work的时候,会调用mmc_rescan
在mmc_rescan 中最要的如下:
for (i = 0; i < ARRAY_SIZE(freqs); i++) {
if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))
break;
if (freqs[i] <= host->f_min)
break;
}
static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
可以看到每个host 最多scan四次,在当前freqs上找到device就退出
static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
{
host->f_init = freq;
#ifdef CONFIG_MMC_DEBUG
pr_info("%s: %s: trying to init card at %u Hz\n",
mmc_hostname(host), __func__, host->f_init);
#endif
mmc_power_up(host, host->ocr_avail);
/*
* Some eMMCs (with VCCQ always on) may not be reset after power up, so
* do a hardware reset if possible.
*/
mmc_hw_reset_for_init(host);
/*
* sdio_reset sends CMD52 to reset card. Since we do not know
* if the card is being re-initialized, just send it. CMD52
* should be ignored by SD/eMMC cards.
* Skip it if we already know that we do not support SDIO commands
*/
if (!(host->caps2 & MMC_CAP2_NO_SDIO))
sdio_reset(host);
mmc_go_idle(host);
if (!(host->caps2 & MMC_CAP2_NO_SD))
mmc_send_if_cond(host, host->ocr_avail);
/* Order's important: probe SDIO, then SD, then MMC */
if (!(host->caps2 & MMC_CAP2_NO_SDIO))
if (!mmc_attach_sdio(host))
return 0;
if (!(host->caps2 & MMC_CAP2_NO_SD))
if (!mmc_attach_sd(host))
return 0;
if (!(host->caps2 & MMC_CAP2_NO_MMC))
if (!mmc_attach_mmc(host))
return 0;
mmc_power_off(host);
return -EIO;
}
可以从mmc_rescan_try_freq看到,emmc host上可以挂三种设备,这三种设别只能挂一个不能共存,且有一定的优先级
其中优先级为:mmc->sd->sdio
以mmc_attach_sdio->mmc_add_card为例开机会打印如下log
[ 11.565233] mmc_host mmc2: Bus speed (slot 0) = 25000000Hz (slot req 2500000)
++ Setting up mdev
[ 11.587289] mmc2: queuing unknown CIS tuple 0x91 (3 bytes)
[ 11.599206] mmc2: new SDIO card at address 0001
在mmc_rescan_try_freq 中是通过host->caps2 & MMC_CAP2_NO_SDIO 来判断是否有SDIO的,也就是如果在mmc_of_parse中没有添加这个flag就默认调用mmc_attach_sdio。而mmc_of_parse 是在parse host对应的dts,也就是说在dts中如果没有
if (of_property_read_bool(np, "no-sdio"))
host->caps2 |= MMC_CAP2_NO_SDIO;
就默认调用mmc_attach_sdio
int mmc_of_parse(struct mmc_host *host)
{
if (of_property_read_bool(np, "disable-wp"))
host->caps2 |= MMC_CAP2_NO_WRITE_PROTECT;
/* See the comment on CD inversion above */
if (ro_cap_invert ^ ro_gpio_invert)
host->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
if (of_property_read_bool(np, "cap-sd-highspeed"))
host->caps |= MMC_CAP_SD_HIGHSPEED;
if (of_property_read_bool(np, "cap-mmc-highspeed"))
host->caps |= MMC_CAP_MMC_HIGHSPEED;
if (of_property_read_bool(np, "sd-uhs-sdr12"))
host->caps |= MMC_CAP_UHS_SDR12;
if (of_property_read_bool(np, "sd-uhs-sdr25"))
host->caps |= MMC_CAP_UHS_SDR25;
if (of_property_read_bool(np, "sd-uhs-sdr50"))
host->caps |= MMC_CAP_UHS_SDR50;
if (of_property_read_bool(np, "sd-uhs-sdr104"))
host->caps |= MMC_CAP_UHS_SDR104;
if (of_property_read_bool(np, "sd-uhs-ddr50"))
host->caps |= MMC_CAP_UHS_DDR50;
if (of_property_read_bool(np, "cap-power-off-card"))
host->caps |= MMC_CAP_POWER_OFF_CARD;
if (of_property_read_bool(np, "cap-mmc-hw-reset"))
host->caps |= MMC_CAP_HW_RESET;
if (of_property_read_bool(np, "cap-sdio-irq"))
host->caps |= MMC_CAP_SDIO_IRQ;
if (of_property_read_bool(np, "full-pwr-cycle"))
host->caps2 |= MMC_CAP2_FULL_PWR_CYCLE;
if (of_property_read_bool(np, "keep-power-in-suspend"))
host->pm_caps |= MMC_PM_KEEP_POWER;
}