blob: 45e18d462f4c1227463625d8ba70bec71a0903b9 [file] [log] [blame]
/*
* Copyright (C) 2016 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/module.h>
#include <linux/of.h>
#include <linux/mmc/host.h>
#include <linux/mmc/dw_mmc.h>
#include <crypto/smu.h>
#include "dw_mmc.h"
#include "dw_mmc-exynos.h"
#define EXYNOS_MMC_SMU_LABEL "mmc-exynos-smu"
static void exynos_mmc_smu_entry0_init(struct dw_mci *host)
{
mci_writel(host, MPSBEGIN0, 0);
mci_writel(host, MPSEND0, 0xffffffff);
mci_writel(host, MPSLUN0, 0xff);
mci_writel(host, MPSCTRL0, DWMCI_MPSCTRL_BYPASS);
}
static struct exynos_smu_variant_ops *exynos_mmc_smu_get_vops(struct device *dev)
{
struct exynos_smu_variant_ops *smu_vops = NULL;
struct device_node *node;
node = of_parse_phandle(dev->of_node, EXYNOS_MMC_SMU_LABEL, 0);
if (!node) {
dev_err(dev, "%s: exynos-mmc-smu property not specified\n",
__func__);
goto err;
}
smu_vops = exynos_smu_get_variant_ops(node);
if (!smu_vops)
dev_err(dev, "%s: Fail to get smu_vops\n", __func__);
of_node_put(node);
err:
return smu_vops;
}
static struct platform_device *exynos_mmc_smu_get_pdevice(struct device *dev)
{
struct device_node *node;
struct platform_device *smu_pdev = NULL;
node = of_parse_phandle(dev->of_node, EXYNOS_MMC_SMU_LABEL, 0);
if (!node) {
dev_err(dev, "%s: exynos-mmc-smu property not specified\n",
__func__);
goto err;
}
smu_pdev = exynos_smu_get_pdevice(node);
err:
return smu_pdev;
}
int exynos_mmc_smu_get_dev(struct dw_mci *host)
{
int ret;
struct device *dev;
struct dw_mci_exynos_priv_data *priv;
if (!host || !host->dev) {
pr_err("%s: invalid mmc host, host->dev\n", __func__);
ret = -EINVAL;
return ret;
}
priv = host->priv;
dev = host->dev;
priv->smu.vops = exynos_mmc_smu_get_vops(dev);
priv->smu.pdev = exynos_mmc_smu_get_pdevice(dev);
if (priv->smu.pdev == ERR_PTR(-EPROBE_DEFER)) {
dev_err(dev, "%s: SMU device not probed yet\n", __func__);
ret = -EPROBE_DEFER;
goto err;
}
if (!priv->smu.pdev || !priv->smu.vops) {
dev_err(dev, "%s: Host device doesn't have SMU or fail to get SMU",
__func__);
ret = -ENODEV;
goto err;
}
return 0;
err:
priv->smu.pdev = NULL;
priv->smu.vops = NULL;
return ret;
}
int exynos_mmc_smu_init(struct dw_mci *host)
{
struct smu_data_setting smu_set;
struct dw_mci_exynos_priv_data *priv = host->priv;
if (!priv->smu.vops || !priv->smu.pdev) {
exynos_mmc_smu_entry0_init(host);
return 0;
}
smu_set.id = SMU_EMBEDDED;
smu_set.command = SMU_INIT;
return priv->smu.vops->init(priv->smu.pdev, &smu_set);
}
int exynos_mmc_smu_sec_cfg(struct dw_mci *host)
{
struct smu_data_setting smu_set;
struct dw_mci_exynos_priv_data *priv = host->priv;
if (!priv->smu.vops || !priv->smu.pdev)
return 0;
smu_set.id = SMU_EMBEDDED;
smu_set.desc_type = CFG_DESCTYPE;
return priv->smu.vops->sec_config(priv->smu.pdev, &smu_set);
}
int exynos_mmc_smu_resume(struct dw_mci *host)
{
struct smu_data_setting smu_set;
struct dw_mci_exynos_priv_data *priv = host->priv;
if (!priv->smu.vops || !priv->smu.pdev) {
exynos_mmc_smu_entry0_init(host);
return 0;
}
smu_set.id = SMU_EMBEDDED;
return priv->smu.vops->resume(priv->smu.pdev, &smu_set);
}
int exynos_mmc_smu_abort(struct dw_mci *host)
{
struct smu_data_setting smu_set;
struct dw_mci_exynos_priv_data *priv = host->priv;
if (!priv->smu.vops || !priv->smu.pdev)
return 0;
smu_set.id = SMU_EMBEDDED;
smu_set.command = SMU_ABORT;
return priv->smu.vops->abort(priv->smu.pdev, &smu_set);
}