Skip to content

[Feature] SDIO signal voltage switch #9732

Closed
@GuEe-GUI

Description

@GuEe-GUI

Describe problem solved by the proposed feature

在新的主线上已经支持 switch_uhs_voltage ops,但是目前看上去他更像操作 signal voltage switch 的子集,如果让驱动 ops 实现改进如下是否更好:

struct rt_mmcsd_host_ops
{
    void (*request)(struct rt_mmcsd_host *host, struct rt_mmcsd_req *req);
    void (*set_iocfg)(struct rt_mmcsd_host *host, struct rt_mmcsd_io_cfg *io_cfg);
    rt_int32_t (*get_card_status)(struct rt_mmcsd_host *host);
    void (*enable_sdio_irq)(struct rt_mmcsd_host *host, rt_int32_t en);
    rt_int32_t (*execute_tuning)(struct rt_mmcsd_host *host, rt_int32_t opcode);
    /* 设置 uhs 模式或者其他场景可能会用到 */
    rt_bool_t (*card_busy)(struct rt_mmcsd_host *host);
    /* 切换电压模式 */
    rt_err_t (*signal_voltage_switch)(struct rt_mmcsd_host *host, struct rt_mmcsd_io_cfg *io_cfg);
};

参考实现:

rt_err_t mmcsd_set_signal_voltage(struct rt_mmcsd_host *host, unsigned char signal_voltage)
{
    rt_err_t err = RT_EOK;
    unsigned char old_signal_voltage = host->io_cfg.signal_voltage;

    host->io_cfg.signal_voltage = signal_voltage;
    if (host->ops->signal_voltage_switch)
    {
        err = host->ops->signal_voltage_switch(host, &host->io_cfg);
    }

    if (err)
    {
        host->io_cfg.signal_voltage = old_signal_voltage;
    }

    return err;
}

rt_err_t mmcsd_host_set_uhs_voltage(struct rt_mmcsd_host *host)
{
    rt_uint32_t old_clock = host->io_cfg.clock;

    host->io_cfg.clock = 0;
    mmcsd_set_iocfg(host);

    if (mmcsd_set_signal_voltage(host, MMCSD_SIGNAL_VOLTAGE_180))
    {
        return -RT_ERROR;
    }

    /* Keep clock gated for at least 10 ms, though spec only says 5 ms */
    rt_thread_mdelay(10);

    host->io_cfg.clock = old_clock;
    mmcsd_set_iocfg(host);

    return RT_EOK;
}

static void mmcsd_power_cycle(struct rt_mmcsd_host *host, rt_uint32_t ocr)
{
    mmcsd_power_off(host);

    /* Wait at least 1 ms according to SD spec */
    rt_thread_mdelay(1);

    mmcsd_power_up(host);
    mmcsd_select_voltage(host, ocr);
}

rt_err_t mmcsd_set_uhs_voltage(struct rt_mmcsd_host *host, rt_uint32_t ocr)
{
    rt_err_t err = RT_EOK;
    struct rt_mmcsd_cmd cmd;

    if (!host->ops->signal_voltage_switch)
    {
        return -RT_EINVAL;
    }

    if (!host->ops->card_busy)
    {
        LOG_W("%s: Cannot verify signal voltage switch", host->name);
    }

    rt_memset(&cmd, 0, sizeof(struct rt_mmcsd_cmd));

    cmd.cmd_code = VOLTAGE_SWITCH;
    cmd.arg = 0;
    cmd.flags = RESP_R1 | CMD_AC;

    err = mmcsd_send_cmd(host, &cmd, 0);
    if (err)
    {
        goto power_cycle;
    }

    if (!controller_is_spi(host) && (cmd.resp[0] & R1_ERROR))
    {
        return -RT_EIO;
    }

    /*
     * The card should drive cmd and dat[0:3] low immediately
     * after the response of cmd11, but wait 1 ms to be sure
     */
    rt_thread_mdelay(1);
    if (host->ops->card_busy && !host->ops->card_busy(host))
    {
        err = -RT_ERROR;
        goto power_cycle;
    }

    if (mmcsd_host_set_uhs_voltage(host))
    {
        /*
         * Voltages may not have been switched, but we've already
         * sent CMD11, so a power cycle is required anyway
         */
        err = -RT_ERROR;
        goto power_cycle;
    }

    /* Wait for at least 1 ms according to spec */
    rt_thread_mdelay(1);

    /*
     * Failure to switch is indicated by the card holding
     * dat[0:3] low
     */
    if (host->ops->card_busy && host->ops->card_busy(host))
    {
        err = -RT_ERROR;
    }

power_cycle:
    if (err)
    {
        LOG_D("%s: Signal voltage switch failed, power cycling card", host->name);
        mmcsd_power_cycle(host, ocr);
    }

    return err;
}

在原先的 mmcsd_power_up 阶段中,还需要对电压做初始化,从高调到低:

void mmcsd_set_initial_signal_voltage(struct rt_mmcsd_host *host)
{
    /* 3.3V -> 1.8v -> 1.2v */
    if (!mmcsd_set_signal_voltage(host, MMCSD_SIGNAL_VOLTAGE_330))
    {
        LOG_D("Initial signal voltage of %sv", "3.3");
    }
    else if (!mmcsd_set_signal_voltage(host, MMCSD_SIGNAL_VOLTAGE_180))
    {
        LOG_D("Initial signal voltage of %sv", "1.8");
    }
    else if (!mmcsd_set_signal_voltage(host, MMCSD_SIGNAL_VOLTAGE_120))
    {
        LOG_D("Initial signal voltage of %sv", "1.2");
    }
}

static void mmcsd_power_up(struct rt_mmcsd_host *host)
{
    [...]
    host->io_cfg.power_mode = MMCSD_POWER_UP;
    host->io_cfg.bus_width = MMCSD_BUS_WIDTH_1;
    mmcsd_set_iocfg(host);

    mmcsd_set_initial_signal_voltage(host);

    /*
     * This delay should be sufficient to allow the power supply
     * to reach the minimum voltage.
     */
    rt_thread_mdelay(10);
    [...]
}

主要目的还是为了统一一个电压切换接口并且传递更多参数。

Describe your preferred solution

No response

Describe possible alternatives

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions