blob: 740d939db8de9cf4342abe89ae98be05ea09c745 [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 <crypto/smu.h>
#include "ufshcd.h"
#include "ufs-exynos.h"
#include "ufs-exynos-smu.h"
#define EXYNOS_UFS_SMU_LABEL "ufs-exynos-smu"
static struct exynos_smu_variant_ops *exynos_ufs_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_UFS_SMU_LABEL, 0);
if (!node) {
dev_err(dev, "%s: exynos-ufs-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_ufs_smu_get_pdevice(struct device *dev)
{
struct device_node *node;
struct platform_device *smu_pdev = NULL;
node = of_parse_phandle(dev->of_node, EXYNOS_UFS_SMU_LABEL, 0);
if (!node) {
dev_err(dev, "%s: exynos-ufs-smu property not specified\n",
__func__);
goto err;
}
smu_pdev = exynos_smu_get_pdevice(node);
err:
return smu_pdev;
}
int exynos_ufs_smu_get_dev(struct exynos_ufs *ufs)
{
int ret;
struct device *dev;
if (!ufs || !ufs->dev) {
pr_err("%s: invalid ufs host, host->dev\n", __func__);
ret = -EINVAL;
goto err;
}
dev = ufs->dev;
ufs->smu.vops = exynos_ufs_smu_get_vops(dev);
ufs->smu.pdev = exynos_ufs_smu_get_pdevice(dev);
if (ufs->smu.pdev == ERR_PTR(-EPROBE_DEFER)) {
dev_err(dev, "%s: SMU device not probed yet\n", __func__);
ret = -EPROBE_DEFER;
goto err;
}
if (!ufs->smu.pdev || !ufs->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:
ufs->smu.pdev = NULL;
ufs->smu.vops = NULL;
return ret;
}
int exynos_ufs_smu_init(struct exynos_ufs *ufs)
{
struct smu_data_setting smu_set;
if (!ufs->smu.vops || !ufs->smu.pdev)
return 0;
smu_set.id = SMU_EMBEDDED;
smu_set.command = SMU_INIT;
return ufs->smu.vops->init(ufs->smu.pdev, &smu_set);
}
int exynos_ufs_smu_sec_cfg(struct exynos_ufs *ufs)
{
struct smu_data_setting smu_set;
if (!ufs->smu.vops || !ufs->smu.pdev)
return 0;
smu_set.id = SMU_EMBEDDED;
smu_set.desc_type = CFG_DESCTYPE;
return ufs->smu.vops->sec_config(ufs->smu.pdev, &smu_set);
}
int exynos_ufs_smu_resume(struct exynos_ufs *ufs)
{
struct smu_data_setting smu_set;
if (!ufs->smu.vops || !ufs->smu.pdev)
return 0;
smu_set.id = SMU_EMBEDDED;
return ufs->smu.vops->resume(ufs->smu.pdev, &smu_set);
}
int exynos_ufs_smu_abort(struct exynos_ufs *ufs)
{
struct smu_data_setting smu_set;
if (!ufs->smu.vops || !ufs->smu.pdev)
return 0;
smu_set.id = SMU_EMBEDDED;
smu_set.command = SMU_ABORT;
return ufs->smu.vops->abort(ufs->smu.pdev, &smu_set);
}