321 lines
8.6 KiB
C
321 lines
8.6 KiB
C
/*
|
|
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 and
|
|
* only version 2 as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
|
|
#include "sdhci-msm-ice.h"
|
|
|
|
static void sdhci_msm_ice_error_cb(void *host_ctrl, u32 error)
|
|
{
|
|
struct sdhci_msm_host *msm_host = (struct sdhci_msm_host *)host_ctrl;
|
|
|
|
dev_err(&msm_host->pdev->dev, "%s: Error in ice operation 0x%x",
|
|
__func__, error);
|
|
|
|
if (msm_host->ice.state == SDHCI_MSM_ICE_STATE_ACTIVE)
|
|
msm_host->ice.state = SDHCI_MSM_ICE_STATE_DISABLED;
|
|
}
|
|
|
|
static struct platform_device *sdhci_msm_ice_get_pdevice(struct device *dev)
|
|
{
|
|
struct device_node *node;
|
|
struct platform_device *ice_pdev = NULL;
|
|
|
|
node = of_parse_phandle(dev->of_node, SDHC_MSM_CRYPTO_LABEL, 0);
|
|
if (!node) {
|
|
dev_dbg(dev, "%s: sdhc-msm-crypto property not specified\n",
|
|
__func__);
|
|
goto out;
|
|
}
|
|
ice_pdev = qcom_ice_get_pdevice(node);
|
|
out:
|
|
return ice_pdev;
|
|
}
|
|
|
|
static
|
|
struct qcom_ice_variant_ops *sdhci_msm_ice_get_vops(struct device *dev)
|
|
{
|
|
struct qcom_ice_variant_ops *ice_vops = NULL;
|
|
struct device_node *node;
|
|
|
|
node = of_parse_phandle(dev->of_node, SDHC_MSM_CRYPTO_LABEL, 0);
|
|
if (!node) {
|
|
dev_dbg(dev, "%s: sdhc-msm-crypto property not specified\n",
|
|
__func__);
|
|
goto out;
|
|
}
|
|
ice_vops = qcom_ice_get_variant_ops(node);
|
|
of_node_put(node);
|
|
out:
|
|
return ice_vops;
|
|
}
|
|
|
|
int sdhci_msm_ice_get_dev(struct sdhci_host *host)
|
|
{
|
|
struct device *sdhc_dev;
|
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
|
struct sdhci_msm_host *msm_host = pltfm_host->priv;
|
|
|
|
if (!msm_host || !msm_host->pdev) {
|
|
pr_err("%s: invalid msm_host %p or msm_host->pdev\n",
|
|
__func__, msm_host);
|
|
return -EINVAL;
|
|
}
|
|
|
|
sdhc_dev = &msm_host->pdev->dev;
|
|
msm_host->ice.vops = sdhci_msm_ice_get_vops(sdhc_dev);
|
|
msm_host->ice.pdev = sdhci_msm_ice_get_pdevice(sdhc_dev);
|
|
|
|
if (msm_host->ice.pdev == ERR_PTR(-EPROBE_DEFER)) {
|
|
dev_err(sdhc_dev, "%s: ICE device not probed yet\n",
|
|
__func__);
|
|
msm_host->ice.pdev = NULL;
|
|
msm_host->ice.vops = NULL;
|
|
return -EPROBE_DEFER;
|
|
}
|
|
|
|
if (!msm_host->ice.pdev) {
|
|
dev_dbg(sdhc_dev, "%s: invalid platform device\n", __func__);
|
|
msm_host->ice.vops = NULL;
|
|
return -ENODEV;
|
|
}
|
|
if (!msm_host->ice.vops) {
|
|
dev_dbg(sdhc_dev, "%s: invalid ice vops\n", __func__);
|
|
msm_host->ice.pdev = NULL;
|
|
return -ENODEV;
|
|
}
|
|
msm_host->ice.state = SDHCI_MSM_ICE_STATE_DISABLED;
|
|
return 0;
|
|
}
|
|
|
|
int sdhci_msm_ice_init(struct sdhci_host *host)
|
|
{
|
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
|
struct sdhci_msm_host *msm_host = pltfm_host->priv;
|
|
int err = 0;
|
|
|
|
if (msm_host->ice.vops->config) {
|
|
err = msm_host->ice.vops->init(msm_host->ice.pdev,
|
|
msm_host,
|
|
sdhci_msm_ice_error_cb);
|
|
if (err) {
|
|
pr_err("%s: ice init err %d\n",
|
|
mmc_hostname(host->mmc), err);
|
|
sdhci_msm_ice_print_regs(host);
|
|
goto out;
|
|
}
|
|
msm_host->ice.state = SDHCI_MSM_ICE_STATE_ACTIVE;
|
|
}
|
|
|
|
out:
|
|
return err;
|
|
}
|
|
|
|
void sdhci_msm_ice_cfg_reset(struct sdhci_host *host, u32 slot)
|
|
{
|
|
writel_relaxed(SDHCI_MSM_ICE_ENABLE_BYPASS,
|
|
host->ioaddr + CORE_VENDOR_SPEC_ICE_CTRL_INFO_3_n + 16 * slot);
|
|
}
|
|
|
|
int sdhci_msm_ice_cfg(struct sdhci_host *host, struct mmc_request *mrq,
|
|
u32 slot)
|
|
{
|
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
|
struct sdhci_msm_host *msm_host = pltfm_host->priv;
|
|
int err = 0;
|
|
struct ice_data_setting ice_set;
|
|
sector_t lba = 0;
|
|
unsigned int ctrl_info_val = 0;
|
|
unsigned int bypass = SDHCI_MSM_ICE_ENABLE_BYPASS;
|
|
struct request *req;
|
|
|
|
if (msm_host->ice.state != SDHCI_MSM_ICE_STATE_ACTIVE) {
|
|
pr_err("%s: ice is in invalid state %d\n",
|
|
mmc_hostname(host->mmc), msm_host->ice.state);
|
|
return -EINVAL;
|
|
}
|
|
|
|
BUG_ON(!mrq);
|
|
memset(&ice_set, 0, sizeof(struct ice_data_setting));
|
|
req = mrq->req;
|
|
if (req) {
|
|
lba = req->__sector;
|
|
if (msm_host->ice.vops->config) {
|
|
err = msm_host->ice.vops->config(msm_host->ice.pdev,
|
|
req, &ice_set);
|
|
if (err) {
|
|
pr_err("%s: ice config failed %d\n",
|
|
mmc_hostname(host->mmc), err);
|
|
return err;
|
|
}
|
|
}
|
|
/* if writing data command */
|
|
if (rq_data_dir(req) == WRITE)
|
|
bypass = ice_set.encr_bypass ?
|
|
SDHCI_MSM_ICE_ENABLE_BYPASS :
|
|
SDHCI_MSM_ICE_DISABLE_BYPASS;
|
|
/* if reading data command */
|
|
else if (rq_data_dir(req) == READ)
|
|
bypass = ice_set.decr_bypass ?
|
|
SDHCI_MSM_ICE_ENABLE_BYPASS :
|
|
SDHCI_MSM_ICE_DISABLE_BYPASS;
|
|
pr_debug("%s: %s: slot %d encr_bypass %d bypass %d decr_bypass %d key_index %d\n",
|
|
mmc_hostname(host->mmc),
|
|
(rq_data_dir(req) == WRITE) ? "WRITE" : "READ",
|
|
slot, ice_set.encr_bypass, bypass,
|
|
ice_set.decr_bypass,
|
|
ice_set.crypto_data.key_index);
|
|
}
|
|
|
|
/* Configure ICE index */
|
|
ctrl_info_val =
|
|
(ice_set.crypto_data.key_index &
|
|
MASK_SDHCI_MSM_ICE_CTRL_INFO_KEY_INDEX)
|
|
<< OFFSET_SDHCI_MSM_ICE_CTRL_INFO_KEY_INDEX;
|
|
|
|
/* Configure data unit size of transfer request */
|
|
ctrl_info_val |=
|
|
(SDHCI_MSM_ICE_TR_DATA_UNIT_512_B &
|
|
MASK_SDHCI_MSM_ICE_CTRL_INFO_CDU)
|
|
<< OFFSET_SDHCI_MSM_ICE_CTRL_INFO_CDU;
|
|
|
|
/* Configure ICE bypass mode */
|
|
ctrl_info_val |=
|
|
(bypass & MASK_SDHCI_MSM_ICE_CTRL_INFO_BYPASS)
|
|
<< OFFSET_SDHCI_MSM_ICE_CTRL_INFO_BYPASS;
|
|
|
|
writel_relaxed((lba & 0xFFFFFFFF),
|
|
host->ioaddr + CORE_VENDOR_SPEC_ICE_CTRL_INFO_1_n + 16 * slot);
|
|
writel_relaxed(((lba >> 32) & 0xFFFFFFFF),
|
|
host->ioaddr + CORE_VENDOR_SPEC_ICE_CTRL_INFO_2_n + 16 * slot);
|
|
writel_relaxed(ctrl_info_val,
|
|
host->ioaddr + CORE_VENDOR_SPEC_ICE_CTRL_INFO_3_n + 16 * slot);
|
|
|
|
/* Ensure ICE registers are configured before issuing SDHCI request */
|
|
mb();
|
|
return 0;
|
|
}
|
|
|
|
int sdhci_msm_ice_reset(struct sdhci_host *host)
|
|
{
|
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
|
struct sdhci_msm_host *msm_host = pltfm_host->priv;
|
|
int err = 0;
|
|
|
|
if (msm_host->ice.state != SDHCI_MSM_ICE_STATE_ACTIVE) {
|
|
pr_err("%s: ice is in invalid state before reset %d\n",
|
|
mmc_hostname(host->mmc), msm_host->ice.state);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (msm_host->ice.vops->reset) {
|
|
err = msm_host->ice.vops->reset(msm_host->ice.pdev);
|
|
if (err) {
|
|
pr_err("%s: ice reset failed %d\n",
|
|
mmc_hostname(host->mmc), err);
|
|
sdhci_msm_ice_print_regs(host);
|
|
return err;
|
|
}
|
|
}
|
|
|
|
if (msm_host->ice.state != SDHCI_MSM_ICE_STATE_ACTIVE) {
|
|
pr_err("%s: ice is in invalid state after reset %d\n",
|
|
mmc_hostname(host->mmc), msm_host->ice.state);
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int sdhci_msm_ice_resume(struct sdhci_host *host)
|
|
{
|
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
|
struct sdhci_msm_host *msm_host = pltfm_host->priv;
|
|
int err = 0;
|
|
|
|
if (msm_host->ice.state !=
|
|
SDHCI_MSM_ICE_STATE_SUSPENDED) {
|
|
pr_err("%s: ice is in invalid state before resume %d\n",
|
|
mmc_hostname(host->mmc), msm_host->ice.state);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (msm_host->ice.vops->resume) {
|
|
err = msm_host->ice.vops->resume(msm_host->ice.pdev);
|
|
if (err) {
|
|
pr_err("%s: ice resume failed %d\n",
|
|
mmc_hostname(host->mmc), err);
|
|
return err;
|
|
}
|
|
}
|
|
|
|
msm_host->ice.state = SDHCI_MSM_ICE_STATE_ACTIVE;
|
|
return 0;
|
|
}
|
|
|
|
int sdhci_msm_ice_suspend(struct sdhci_host *host)
|
|
{
|
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
|
struct sdhci_msm_host *msm_host = pltfm_host->priv;
|
|
int err = 0;
|
|
|
|
if (msm_host->ice.state !=
|
|
SDHCI_MSM_ICE_STATE_ACTIVE) {
|
|
pr_err("%s: ice is in invalid state before resume %d\n",
|
|
mmc_hostname(host->mmc), msm_host->ice.state);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (msm_host->ice.vops->suspend) {
|
|
err = msm_host->ice.vops->suspend(msm_host->ice.pdev);
|
|
if (err) {
|
|
pr_err("%s: ice suspend failed %d\n",
|
|
mmc_hostname(host->mmc), err);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
msm_host->ice.state = SDHCI_MSM_ICE_STATE_SUSPENDED;
|
|
return 0;
|
|
}
|
|
|
|
int sdhci_msm_ice_get_status(struct sdhci_host *host, int *ice_status)
|
|
{
|
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
|
struct sdhci_msm_host *msm_host = pltfm_host->priv;
|
|
int stat = -EINVAL;
|
|
|
|
if (msm_host->ice.state != SDHCI_MSM_ICE_STATE_ACTIVE) {
|
|
pr_err("%s: ice is in invalid state %d\n",
|
|
mmc_hostname(host->mmc), msm_host->ice.state);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (msm_host->ice.vops->status) {
|
|
*ice_status = 0;
|
|
stat = msm_host->ice.vops->status(msm_host->ice.pdev);
|
|
if (stat < 0) {
|
|
pr_err("%s: ice get sts failed %d\n",
|
|
mmc_hostname(host->mmc), stat);
|
|
return -EINVAL;
|
|
}
|
|
*ice_status = stat;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void sdhci_msm_ice_print_regs(struct sdhci_host *host)
|
|
{
|
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
|
struct sdhci_msm_host *msm_host = pltfm_host->priv;
|
|
|
|
if (msm_host->ice.vops->debug)
|
|
msm_host->ice.vops->debug(msm_host->ice.pdev);
|
|
}
|