/*
 * Copyright (c) 2006-2024 RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author        Notes
 * 2024-08-16     zhujiale     first version
 */
#include "sdhci-platform.h"

static const struct rt_sdhci_ops sdhci_pltfm_ops = {
    .set_clock         = rt_sdhci_set_clock,
    .set_bus_width     = rt_sdhci_set_bus_width,
    .reset             = rt_sdhci_reset,
    .set_uhs_signaling = rt_sdhci_set_uhs,
};

void rt_sdhci_get_property(struct rt_platform_device *pdev)
{
    struct rt_device        *dev        = &pdev->parent;
    struct rt_sdhci_host       *host       = pdev->priv;
    struct rt_sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
    rt_uint32_t              bus_width;

    if (rt_dm_dev_prop_read_bool(dev, "sdhci,auto-cmd12"))
        host->quirks |= RT_SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;

    if (rt_dm_dev_prop_read_bool(dev, "sdhci,1-bit-only") || (rt_dm_dev_prop_read_u32(dev, "bus-width", &bus_width) == 0 && bus_width == 1))
        host->quirks |= RT_SDHCI_QUIRK_FORCE_1_BIT_DATA;

    if (rt_dm_dev_prop_read_bool(dev, "broken-cd"))
        host->quirks |= RT_SDHCI_QUIRK_BROKEN_CARD_DETECTION;

    if (rt_dm_dev_prop_read_bool(dev, "no-1-8-v"))
        host->quirks2 |= RT_SDHCI_QUIRK2_NO_1_8_V;

    rt_dm_dev_prop_read_u32(dev, "clock-frequency", &pltfm_host->clock);

    if (rt_dm_dev_prop_read_bool(dev, "keep-power-in-suspend"))
        host->mmc->pm_caps |= MMC_PM_KEEP_POWER;

    if (rt_dm_dev_prop_read_bool(dev, "wakeup-source") || rt_dm_dev_prop_read_bool(dev, "enable-sdio-wakeup")) /* legacy */
        host->mmc->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
}

struct rt_sdhci_host *rt_sdhci_pltfm_init(struct rt_platform_device     *pdev,
                                    const struct rt_sdhci_pltfm_data *pdata,
                                    size_t                         priv_size)
{
    struct rt_sdhci_host      *host;
    struct rt_device       *dev = &pdev->parent;
    void                   *ioaddr;
    int                     irq;

    ioaddr = rt_dm_dev_iomap(dev, 0);
    if (!ioaddr)
    {
        return RT_NULL;
    }

    irq = rt_dm_dev_get_irq(dev, 0);
    if (irq < 0)
    {
        return RT_NULL;
    }
    host = rt_sdhci_alloc_host(dev,sizeof(struct rt_sdhci_pltfm_host) + priv_size);
    if (!host)
    {
        return RT_NULL;
    }
    host->irq     = irq;
    host->ioaddr  = ioaddr;
    host->hw_name = rt_dm_dev_get_name(dev);

    if (pdata && pdata->ops)
        host->ops = pdata->ops;
    else
        host->ops = &sdhci_pltfm_ops;
    if (pdata)
    {
        host->quirks  = pdata->quirks;
        host->quirks2 = pdata->quirks2;
    }

    pdev->priv = host;

    return host;
}

int rt_sdhci_pltfm_init_and_add_host(struct rt_platform_device     *pdev,
                                  const struct rt_sdhci_pltfm_data *pdata,
                                  size_t                         priv_size)
{
    struct rt_sdhci_host *host;
    int                ret = 0;

    host = rt_sdhci_pltfm_init(pdev, pdata, priv_size);
    if (!host)
        return -RT_ERROR;

    rt_sdhci_get_property(pdev);

    ret = rt_sdhci_init_host(host);
    if (ret)
        rt_sdhci_pltfm_free(pdev);

    return ret;
}

void rt_sdhci_pltfm_free(struct rt_platform_device *pdev)
{
    struct rt_sdhci_host *host = pdev->priv;

    rt_sdhci_free_host(host);
}

void rt_sdhci_pltfm_remove(struct rt_platform_device *pdev)
{
    struct rt_sdhci_host *host = pdev->priv;
    int dead = (readl(host->ioaddr + RT_SDHCI_INT_STATUS) == 0xffffffff);

    rt_sdhci_uninit_host(host, dead);
    rt_sdhci_pltfm_free(pdev);
}