Skip to content

Commit

Permalink
scsi: ufs: ufs-pci: Add support for Intel LKF
Browse files Browse the repository at this point in the history
Add PCI ID and callbacks to support Intel LKF.

This includes the ability to use an ACPI device-specific method (DSM) to
perform a UFS device reset.

Link: https://lore.kernel.org/r/20210312080620.13311-1-adrian.hunter@intel.com
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
  • Loading branch information
Adrian Hunter authored and Martin K. Petersen committed Mar 17, 2021
1 parent 4f5e51c commit b2c5792
Showing 1 changed file with 169 additions and 0 deletions.
169 changes: 169 additions & 0 deletions drivers/scsi/ufs/ufshcd-pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,89 @@
#include <linux/pm_runtime.h>
#include <linux/pm_qos.h>
#include <linux/debugfs.h>
#include <linux/uuid.h>
#include <linux/acpi.h>
#include <linux/gpio/consumer.h>

struct ufs_host {
void (*late_init)(struct ufs_hba *hba);
};

enum {
INTEL_DSM_FNS = 0,
INTEL_DSM_RESET = 1,
};

struct intel_host {
struct ufs_host ufs_host;
u32 dsm_fns;
u32 active_ltr;
u32 idle_ltr;
struct dentry *debugfs_root;
struct gpio_desc *reset_gpio;
};

static const guid_t intel_dsm_guid =
GUID_INIT(0x1A4832A0, 0x7D03, 0x43CA,
0xB0, 0x20, 0xF6, 0xDC, 0xD1, 0x2A, 0x19, 0x50);

static int __intel_dsm(struct intel_host *intel_host, struct device *dev,
unsigned int fn, u32 *result)
{
union acpi_object *obj;
int err = 0;
size_t len;

obj = acpi_evaluate_dsm(ACPI_HANDLE(dev), &intel_dsm_guid, 0, fn, NULL);
if (!obj)
return -EOPNOTSUPP;

if (obj->type != ACPI_TYPE_BUFFER || obj->buffer.length < 1) {
err = -EINVAL;
goto out;
}

len = min_t(size_t, obj->buffer.length, 4);

*result = 0;
memcpy(result, obj->buffer.pointer, len);
out:
ACPI_FREE(obj);

return err;
}

static int intel_dsm(struct intel_host *intel_host, struct device *dev,
unsigned int fn, u32 *result)
{
if (fn > 31 || !(intel_host->dsm_fns & (1 << fn)))
return -EOPNOTSUPP;

return __intel_dsm(intel_host, dev, fn, result);
}

static void intel_dsm_init(struct intel_host *intel_host, struct device *dev)
{
int err;

err = __intel_dsm(intel_host, dev, INTEL_DSM_FNS, &intel_host->dsm_fns);
dev_dbg(dev, "DSM fns %#x, error %d\n", intel_host->dsm_fns, err);
}

static int ufs_intel_hce_enable_notify(struct ufs_hba *hba,
enum ufs_notify_change_status status)
{
/* Cannot enable ICE until after HC enable */
if (status == POST_CHANGE && hba->caps & UFSHCD_CAP_CRYPTO) {
u32 hce = ufshcd_readl(hba, REG_CONTROLLER_ENABLE);

hce |= CRYPTO_GENERAL_ENABLE;
ufshcd_writel(hba, hce, REG_CONTROLLER_ENABLE);
}

return 0;
}

static int ufs_intel_disable_lcc(struct ufs_hba *hba)
{
u32 attr = UIC_ARG_MIB(PA_LOCAL_TX_LCC_ENABLE);
Expand Down Expand Up @@ -144,6 +220,41 @@ static void intel_remove_debugfs(struct ufs_hba *hba)
debugfs_remove_recursive(host->debugfs_root);
}

static int ufs_intel_device_reset(struct ufs_hba *hba)
{
struct intel_host *host = ufshcd_get_variant(hba);

if (host->dsm_fns & INTEL_DSM_RESET) {
u32 result = 0;
int err;

err = intel_dsm(host, hba->dev, INTEL_DSM_RESET, &result);
if (!err && !result)
err = -EIO;
if (err)
dev_err(hba->dev, "%s: DSM error %d result %u\n",
__func__, err, result);
return err;
}

if (!host->reset_gpio)
return -EOPNOTSUPP;

gpiod_set_value_cansleep(host->reset_gpio, 1);
usleep_range(10, 15);

gpiod_set_value_cansleep(host->reset_gpio, 0);
usleep_range(10, 15);

return 0;
}

static struct gpio_desc *ufs_intel_get_reset_gpio(struct device *dev)
{
/* GPIO in _DSD has active low setting */
return devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
}

static int ufs_intel_common_init(struct ufs_hba *hba)
{
struct intel_host *host;
Expand All @@ -154,6 +265,23 @@ static int ufs_intel_common_init(struct ufs_hba *hba)
if (!host)
return -ENOMEM;
ufshcd_set_variant(hba, host);
intel_dsm_init(host, hba->dev);
if (host->dsm_fns & INTEL_DSM_RESET) {
if (hba->vops->device_reset)
hba->caps |= UFSHCD_CAP_DEEPSLEEP;
} else {
if (hba->vops->device_reset)
host->reset_gpio = ufs_intel_get_reset_gpio(hba->dev);
if (IS_ERR(host->reset_gpio)) {
dev_err(hba->dev, "%s: failed to get reset GPIO, error %ld\n",
__func__, PTR_ERR(host->reset_gpio));
host->reset_gpio = NULL;
}
if (host->reset_gpio) {
gpiod_set_value_cansleep(host->reset_gpio, 0);
hba->caps |= UFSHCD_CAP_DEEPSLEEP;
}
}
intel_ltr_expose(hba->dev);
intel_add_debugfs(hba);
return 0;
Expand Down Expand Up @@ -206,6 +334,31 @@ static int ufs_intel_ehl_init(struct ufs_hba *hba)
return ufs_intel_common_init(hba);
}

static void ufs_intel_lkf_late_init(struct ufs_hba *hba)
{
/* LKF always needs a full reset, so set PM accordingly */
if (hba->caps & UFSHCD_CAP_DEEPSLEEP) {
hba->spm_lvl = UFS_PM_LVL_6;
hba->rpm_lvl = UFS_PM_LVL_6;
} else {
hba->spm_lvl = UFS_PM_LVL_5;
hba->rpm_lvl = UFS_PM_LVL_5;
}
}

static int ufs_intel_lkf_init(struct ufs_hba *hba)
{
struct ufs_host *ufs_host;
int err;

hba->quirks |= UFSHCD_QUIRK_BROKEN_AUTO_HIBERN8;
hba->caps |= UFSHCD_CAP_CRYPTO;
err = ufs_intel_common_init(hba);
ufs_host = ufshcd_get_variant(hba);
ufs_host->late_init = ufs_intel_lkf_late_init;
return err;
}

static struct ufs_hba_variant_ops ufs_intel_cnl_hba_vops = {
.name = "intel-pci",
.init = ufs_intel_common_init,
Expand All @@ -222,6 +375,16 @@ static struct ufs_hba_variant_ops ufs_intel_ehl_hba_vops = {
.resume = ufs_intel_resume,
};

static struct ufs_hba_variant_ops ufs_intel_lkf_hba_vops = {
.name = "intel-pci",
.init = ufs_intel_lkf_init,
.exit = ufs_intel_common_exit,
.hce_enable_notify = ufs_intel_hce_enable_notify,
.link_startup_notify = ufs_intel_link_startup_notify,
.resume = ufs_intel_resume,
.device_reset = ufs_intel_device_reset,
};

#ifdef CONFIG_PM_SLEEP
/**
* ufshcd_pci_suspend - suspend power management function
Expand Down Expand Up @@ -321,6 +484,7 @@ static void ufshcd_pci_remove(struct pci_dev *pdev)
static int
ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct ufs_host *ufs_host;
struct ufs_hba *hba;
void __iomem *mmio_base;
int err;
Expand Down Expand Up @@ -358,6 +522,10 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return err;
}

ufs_host = ufshcd_get_variant(hba);
if (ufs_host && ufs_host->late_init)
ufs_host->late_init(hba);

pm_runtime_put_noidle(&pdev->dev);
pm_runtime_allow(&pdev->dev);

Expand All @@ -383,6 +551,7 @@ static const struct pci_device_id ufshcd_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, 0x9DFA), (kernel_ulong_t)&ufs_intel_cnl_hba_vops },
{ PCI_VDEVICE(INTEL, 0x4B41), (kernel_ulong_t)&ufs_intel_ehl_hba_vops },
{ PCI_VDEVICE(INTEL, 0x4B43), (kernel_ulong_t)&ufs_intel_ehl_hba_vops },
{ PCI_VDEVICE(INTEL, 0x98FA), (kernel_ulong_t)&ufs_intel_lkf_hba_vops },
{ } /* terminate list */
};

Expand Down

0 comments on commit b2c5792

Please sign in to comment.