162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Universal Flash Storage Host controller PCI glue driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2011-2013 Samsung India Software Operations
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Authors:
862306a36Sopenharmony_ci *	Santosh Yaraganavi <santosh.sy@samsung.com>
962306a36Sopenharmony_ci *	Vinayak Holikatti <h.vinayak@samsung.com>
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <ufs/ufshcd.h>
1362306a36Sopenharmony_ci#include <linux/delay.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/pci.h>
1662306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1762306a36Sopenharmony_ci#include <linux/pm_qos.h>
1862306a36Sopenharmony_ci#include <linux/debugfs.h>
1962306a36Sopenharmony_ci#include <linux/uuid.h>
2062306a36Sopenharmony_ci#include <linux/acpi.h>
2162306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistruct ufs_host {
2462306a36Sopenharmony_ci	void (*late_init)(struct ufs_hba *hba);
2562306a36Sopenharmony_ci};
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cienum intel_ufs_dsm_func_id {
2862306a36Sopenharmony_ci	INTEL_DSM_FNS		=  0,
2962306a36Sopenharmony_ci	INTEL_DSM_RESET		=  1,
3062306a36Sopenharmony_ci};
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistruct intel_host {
3362306a36Sopenharmony_ci	struct ufs_host ufs_host;
3462306a36Sopenharmony_ci	u32		dsm_fns;
3562306a36Sopenharmony_ci	u32		active_ltr;
3662306a36Sopenharmony_ci	u32		idle_ltr;
3762306a36Sopenharmony_ci	struct dentry	*debugfs_root;
3862306a36Sopenharmony_ci	struct gpio_desc *reset_gpio;
3962306a36Sopenharmony_ci};
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic const guid_t intel_dsm_guid =
4262306a36Sopenharmony_ci	GUID_INIT(0x1A4832A0, 0x7D03, 0x43CA,
4362306a36Sopenharmony_ci		  0xB0, 0x20, 0xF6, 0xDC, 0xD1, 0x2A, 0x19, 0x50);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic bool __intel_dsm_supported(struct intel_host *host,
4662306a36Sopenharmony_ci				  enum intel_ufs_dsm_func_id fn)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	return fn < 32 && fn >= 0 && (host->dsm_fns & (1u << fn));
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci#define INTEL_DSM_SUPPORTED(host, name) \
5262306a36Sopenharmony_ci	__intel_dsm_supported(host, INTEL_DSM_##name)
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic int __intel_dsm(struct intel_host *intel_host, struct device *dev,
5562306a36Sopenharmony_ci		       unsigned int fn, u32 *result)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	union acpi_object *obj;
5862306a36Sopenharmony_ci	int err = 0;
5962306a36Sopenharmony_ci	size_t len;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	obj = acpi_evaluate_dsm(ACPI_HANDLE(dev), &intel_dsm_guid, 0, fn, NULL);
6262306a36Sopenharmony_ci	if (!obj)
6362306a36Sopenharmony_ci		return -EOPNOTSUPP;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	if (obj->type != ACPI_TYPE_BUFFER || obj->buffer.length < 1) {
6662306a36Sopenharmony_ci		err = -EINVAL;
6762306a36Sopenharmony_ci		goto out;
6862306a36Sopenharmony_ci	}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	len = min_t(size_t, obj->buffer.length, 4);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	*result = 0;
7362306a36Sopenharmony_ci	memcpy(result, obj->buffer.pointer, len);
7462306a36Sopenharmony_ciout:
7562306a36Sopenharmony_ci	ACPI_FREE(obj);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	return err;
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic int intel_dsm(struct intel_host *intel_host, struct device *dev,
8162306a36Sopenharmony_ci		     unsigned int fn, u32 *result)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	if (!__intel_dsm_supported(intel_host, fn))
8462306a36Sopenharmony_ci		return -EOPNOTSUPP;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	return __intel_dsm(intel_host, dev, fn, result);
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic void intel_dsm_init(struct intel_host *intel_host, struct device *dev)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	int err;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	err = __intel_dsm(intel_host, dev, INTEL_DSM_FNS, &intel_host->dsm_fns);
9462306a36Sopenharmony_ci	dev_dbg(dev, "DSM fns %#x, error %d\n", intel_host->dsm_fns, err);
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic int ufs_intel_hce_enable_notify(struct ufs_hba *hba,
9862306a36Sopenharmony_ci				       enum ufs_notify_change_status status)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	/* Cannot enable ICE until after HC enable */
10162306a36Sopenharmony_ci	if (status == POST_CHANGE && hba->caps & UFSHCD_CAP_CRYPTO) {
10262306a36Sopenharmony_ci		u32 hce = ufshcd_readl(hba, REG_CONTROLLER_ENABLE);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci		hce |= CRYPTO_GENERAL_ENABLE;
10562306a36Sopenharmony_ci		ufshcd_writel(hba, hce, REG_CONTROLLER_ENABLE);
10662306a36Sopenharmony_ci	}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	return 0;
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic int ufs_intel_disable_lcc(struct ufs_hba *hba)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	u32 attr = UIC_ARG_MIB(PA_LOCAL_TX_LCC_ENABLE);
11462306a36Sopenharmony_ci	u32 lcc_enable = 0;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	ufshcd_dme_get(hba, attr, &lcc_enable);
11762306a36Sopenharmony_ci	if (lcc_enable)
11862306a36Sopenharmony_ci		ufshcd_disable_host_tx_lcc(hba);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	return 0;
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic int ufs_intel_link_startup_notify(struct ufs_hba *hba,
12462306a36Sopenharmony_ci					 enum ufs_notify_change_status status)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	int err = 0;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	switch (status) {
12962306a36Sopenharmony_ci	case PRE_CHANGE:
13062306a36Sopenharmony_ci		err = ufs_intel_disable_lcc(hba);
13162306a36Sopenharmony_ci		break;
13262306a36Sopenharmony_ci	case POST_CHANGE:
13362306a36Sopenharmony_ci		break;
13462306a36Sopenharmony_ci	default:
13562306a36Sopenharmony_ci		break;
13662306a36Sopenharmony_ci	}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	return err;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic int ufs_intel_set_lanes(struct ufs_hba *hba, u32 lanes)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	struct ufs_pa_layer_attr pwr_info = hba->pwr_info;
14462306a36Sopenharmony_ci	int ret;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	pwr_info.lane_rx = lanes;
14762306a36Sopenharmony_ci	pwr_info.lane_tx = lanes;
14862306a36Sopenharmony_ci	ret = ufshcd_config_pwr_mode(hba, &pwr_info);
14962306a36Sopenharmony_ci	if (ret)
15062306a36Sopenharmony_ci		dev_err(hba->dev, "%s: Setting %u lanes, err = %d\n",
15162306a36Sopenharmony_ci			__func__, lanes, ret);
15262306a36Sopenharmony_ci	return ret;
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic int ufs_intel_lkf_pwr_change_notify(struct ufs_hba *hba,
15662306a36Sopenharmony_ci				enum ufs_notify_change_status status,
15762306a36Sopenharmony_ci				struct ufs_pa_layer_attr *dev_max_params,
15862306a36Sopenharmony_ci				struct ufs_pa_layer_attr *dev_req_params)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	int err = 0;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	switch (status) {
16362306a36Sopenharmony_ci	case PRE_CHANGE:
16462306a36Sopenharmony_ci		if (ufshcd_is_hs_mode(dev_max_params) &&
16562306a36Sopenharmony_ci		    (hba->pwr_info.lane_rx != 2 || hba->pwr_info.lane_tx != 2))
16662306a36Sopenharmony_ci			ufs_intel_set_lanes(hba, 2);
16762306a36Sopenharmony_ci		memcpy(dev_req_params, dev_max_params, sizeof(*dev_req_params));
16862306a36Sopenharmony_ci		break;
16962306a36Sopenharmony_ci	case POST_CHANGE:
17062306a36Sopenharmony_ci		if (ufshcd_is_hs_mode(dev_req_params)) {
17162306a36Sopenharmony_ci			u32 peer_granularity;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci			usleep_range(1000, 1250);
17462306a36Sopenharmony_ci			err = ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_GRANULARITY),
17562306a36Sopenharmony_ci						  &peer_granularity);
17662306a36Sopenharmony_ci		}
17762306a36Sopenharmony_ci		break;
17862306a36Sopenharmony_ci	default:
17962306a36Sopenharmony_ci		break;
18062306a36Sopenharmony_ci	}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	return err;
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic int ufs_intel_lkf_apply_dev_quirks(struct ufs_hba *hba)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	u32 granularity, peer_granularity;
18862306a36Sopenharmony_ci	u32 pa_tactivate, peer_pa_tactivate;
18962306a36Sopenharmony_ci	int ret;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	ret = ufshcd_dme_get(hba, UIC_ARG_MIB(PA_GRANULARITY), &granularity);
19262306a36Sopenharmony_ci	if (ret)
19362306a36Sopenharmony_ci		goto out;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	ret = ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_GRANULARITY), &peer_granularity);
19662306a36Sopenharmony_ci	if (ret)
19762306a36Sopenharmony_ci		goto out;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	ret = ufshcd_dme_get(hba, UIC_ARG_MIB(PA_TACTIVATE), &pa_tactivate);
20062306a36Sopenharmony_ci	if (ret)
20162306a36Sopenharmony_ci		goto out;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	ret = ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_TACTIVATE), &peer_pa_tactivate);
20462306a36Sopenharmony_ci	if (ret)
20562306a36Sopenharmony_ci		goto out;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	if (granularity == peer_granularity) {
20862306a36Sopenharmony_ci		u32 new_peer_pa_tactivate = pa_tactivate + 2;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci		ret = ufshcd_dme_peer_set(hba, UIC_ARG_MIB(PA_TACTIVATE), new_peer_pa_tactivate);
21162306a36Sopenharmony_ci	}
21262306a36Sopenharmony_ciout:
21362306a36Sopenharmony_ci	return ret;
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci#define INTEL_ACTIVELTR		0x804
21762306a36Sopenharmony_ci#define INTEL_IDLELTR		0x808
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci#define INTEL_LTR_REQ		BIT(15)
22062306a36Sopenharmony_ci#define INTEL_LTR_SCALE_MASK	GENMASK(11, 10)
22162306a36Sopenharmony_ci#define INTEL_LTR_SCALE_1US	(2 << 10)
22262306a36Sopenharmony_ci#define INTEL_LTR_SCALE_32US	(3 << 10)
22362306a36Sopenharmony_ci#define INTEL_LTR_VALUE_MASK	GENMASK(9, 0)
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_cistatic void intel_cache_ltr(struct ufs_hba *hba)
22662306a36Sopenharmony_ci{
22762306a36Sopenharmony_ci	struct intel_host *host = ufshcd_get_variant(hba);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	host->active_ltr = readl(hba->mmio_base + INTEL_ACTIVELTR);
23062306a36Sopenharmony_ci	host->idle_ltr = readl(hba->mmio_base + INTEL_IDLELTR);
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic void intel_ltr_set(struct device *dev, s32 val)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	struct ufs_hba *hba = dev_get_drvdata(dev);
23662306a36Sopenharmony_ci	struct intel_host *host = ufshcd_get_variant(hba);
23762306a36Sopenharmony_ci	u32 ltr;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	pm_runtime_get_sync(dev);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	/*
24262306a36Sopenharmony_ci	 * Program latency tolerance (LTR) accordingly what has been asked
24362306a36Sopenharmony_ci	 * by the PM QoS layer or disable it in case we were passed
24462306a36Sopenharmony_ci	 * negative value or PM_QOS_LATENCY_ANY.
24562306a36Sopenharmony_ci	 */
24662306a36Sopenharmony_ci	ltr = readl(hba->mmio_base + INTEL_ACTIVELTR);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	if (val == PM_QOS_LATENCY_ANY || val < 0) {
24962306a36Sopenharmony_ci		ltr &= ~INTEL_LTR_REQ;
25062306a36Sopenharmony_ci	} else {
25162306a36Sopenharmony_ci		ltr |= INTEL_LTR_REQ;
25262306a36Sopenharmony_ci		ltr &= ~INTEL_LTR_SCALE_MASK;
25362306a36Sopenharmony_ci		ltr &= ~INTEL_LTR_VALUE_MASK;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci		if (val > INTEL_LTR_VALUE_MASK) {
25662306a36Sopenharmony_ci			val >>= 5;
25762306a36Sopenharmony_ci			if (val > INTEL_LTR_VALUE_MASK)
25862306a36Sopenharmony_ci				val = INTEL_LTR_VALUE_MASK;
25962306a36Sopenharmony_ci			ltr |= INTEL_LTR_SCALE_32US | val;
26062306a36Sopenharmony_ci		} else {
26162306a36Sopenharmony_ci			ltr |= INTEL_LTR_SCALE_1US | val;
26262306a36Sopenharmony_ci		}
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	if (ltr == host->active_ltr)
26662306a36Sopenharmony_ci		goto out;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	writel(ltr, hba->mmio_base + INTEL_ACTIVELTR);
26962306a36Sopenharmony_ci	writel(ltr, hba->mmio_base + INTEL_IDLELTR);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	/* Cache the values into intel_host structure */
27262306a36Sopenharmony_ci	intel_cache_ltr(hba);
27362306a36Sopenharmony_ciout:
27462306a36Sopenharmony_ci	pm_runtime_put(dev);
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_cistatic void intel_ltr_expose(struct device *dev)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	dev->power.set_latency_tolerance = intel_ltr_set;
28062306a36Sopenharmony_ci	dev_pm_qos_expose_latency_tolerance(dev);
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_cistatic void intel_ltr_hide(struct device *dev)
28462306a36Sopenharmony_ci{
28562306a36Sopenharmony_ci	dev_pm_qos_hide_latency_tolerance(dev);
28662306a36Sopenharmony_ci	dev->power.set_latency_tolerance = NULL;
28762306a36Sopenharmony_ci}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_cistatic void intel_add_debugfs(struct ufs_hba *hba)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci	struct dentry *dir = debugfs_create_dir(dev_name(hba->dev), NULL);
29262306a36Sopenharmony_ci	struct intel_host *host = ufshcd_get_variant(hba);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	intel_cache_ltr(hba);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	host->debugfs_root = dir;
29762306a36Sopenharmony_ci	debugfs_create_x32("active_ltr", 0444, dir, &host->active_ltr);
29862306a36Sopenharmony_ci	debugfs_create_x32("idle_ltr", 0444, dir, &host->idle_ltr);
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_cistatic void intel_remove_debugfs(struct ufs_hba *hba)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	struct intel_host *host = ufshcd_get_variant(hba);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	debugfs_remove_recursive(host->debugfs_root);
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_cistatic int ufs_intel_device_reset(struct ufs_hba *hba)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	struct intel_host *host = ufshcd_get_variant(hba);
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	if (INTEL_DSM_SUPPORTED(host, RESET)) {
31362306a36Sopenharmony_ci		u32 result = 0;
31462306a36Sopenharmony_ci		int err;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci		err = intel_dsm(host, hba->dev, INTEL_DSM_RESET, &result);
31762306a36Sopenharmony_ci		if (!err && !result)
31862306a36Sopenharmony_ci			err = -EIO;
31962306a36Sopenharmony_ci		if (err)
32062306a36Sopenharmony_ci			dev_err(hba->dev, "%s: DSM error %d result %u\n",
32162306a36Sopenharmony_ci				__func__, err, result);
32262306a36Sopenharmony_ci		return err;
32362306a36Sopenharmony_ci	}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	if (!host->reset_gpio)
32662306a36Sopenharmony_ci		return -EOPNOTSUPP;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	gpiod_set_value_cansleep(host->reset_gpio, 1);
32962306a36Sopenharmony_ci	usleep_range(10, 15);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	gpiod_set_value_cansleep(host->reset_gpio, 0);
33262306a36Sopenharmony_ci	usleep_range(10, 15);
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	return 0;
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic struct gpio_desc *ufs_intel_get_reset_gpio(struct device *dev)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	/* GPIO in _DSD has active low setting */
34062306a36Sopenharmony_ci	return devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
34162306a36Sopenharmony_ci}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_cistatic int ufs_intel_common_init(struct ufs_hba *hba)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	struct intel_host *host;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	hba->caps |= UFSHCD_CAP_RPM_AUTOSUSPEND;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	host = devm_kzalloc(hba->dev, sizeof(*host), GFP_KERNEL);
35062306a36Sopenharmony_ci	if (!host)
35162306a36Sopenharmony_ci		return -ENOMEM;
35262306a36Sopenharmony_ci	ufshcd_set_variant(hba, host);
35362306a36Sopenharmony_ci	intel_dsm_init(host, hba->dev);
35462306a36Sopenharmony_ci	if (INTEL_DSM_SUPPORTED(host, RESET)) {
35562306a36Sopenharmony_ci		if (hba->vops->device_reset)
35662306a36Sopenharmony_ci			hba->caps |= UFSHCD_CAP_DEEPSLEEP;
35762306a36Sopenharmony_ci	} else {
35862306a36Sopenharmony_ci		if (hba->vops->device_reset)
35962306a36Sopenharmony_ci			host->reset_gpio = ufs_intel_get_reset_gpio(hba->dev);
36062306a36Sopenharmony_ci		if (IS_ERR(host->reset_gpio)) {
36162306a36Sopenharmony_ci			dev_err(hba->dev, "%s: failed to get reset GPIO, error %ld\n",
36262306a36Sopenharmony_ci				__func__, PTR_ERR(host->reset_gpio));
36362306a36Sopenharmony_ci			host->reset_gpio = NULL;
36462306a36Sopenharmony_ci		}
36562306a36Sopenharmony_ci		if (host->reset_gpio) {
36662306a36Sopenharmony_ci			gpiod_set_value_cansleep(host->reset_gpio, 0);
36762306a36Sopenharmony_ci			hba->caps |= UFSHCD_CAP_DEEPSLEEP;
36862306a36Sopenharmony_ci		}
36962306a36Sopenharmony_ci	}
37062306a36Sopenharmony_ci	intel_ltr_expose(hba->dev);
37162306a36Sopenharmony_ci	intel_add_debugfs(hba);
37262306a36Sopenharmony_ci	return 0;
37362306a36Sopenharmony_ci}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_cistatic void ufs_intel_common_exit(struct ufs_hba *hba)
37662306a36Sopenharmony_ci{
37762306a36Sopenharmony_ci	intel_remove_debugfs(hba);
37862306a36Sopenharmony_ci	intel_ltr_hide(hba->dev);
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_cistatic int ufs_intel_resume(struct ufs_hba *hba, enum ufs_pm_op op)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	if (ufshcd_is_link_hibern8(hba)) {
38462306a36Sopenharmony_ci		int ret = ufshcd_uic_hibern8_exit(hba);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci		if (!ret) {
38762306a36Sopenharmony_ci			ufshcd_set_link_active(hba);
38862306a36Sopenharmony_ci		} else {
38962306a36Sopenharmony_ci			dev_err(hba->dev, "%s: hibern8 exit failed %d\n",
39062306a36Sopenharmony_ci				__func__, ret);
39162306a36Sopenharmony_ci			/*
39262306a36Sopenharmony_ci			 * Force reset and restore. Any other actions can lead
39362306a36Sopenharmony_ci			 * to an unrecoverable state.
39462306a36Sopenharmony_ci			 */
39562306a36Sopenharmony_ci			ufshcd_set_link_off(hba);
39662306a36Sopenharmony_ci		}
39762306a36Sopenharmony_ci	}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	return 0;
40062306a36Sopenharmony_ci}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_cistatic int ufs_intel_ehl_init(struct ufs_hba *hba)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	hba->quirks |= UFSHCD_QUIRK_BROKEN_AUTO_HIBERN8;
40562306a36Sopenharmony_ci	return ufs_intel_common_init(hba);
40662306a36Sopenharmony_ci}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_cistatic void ufs_intel_lkf_late_init(struct ufs_hba *hba)
40962306a36Sopenharmony_ci{
41062306a36Sopenharmony_ci	/* LKF always needs a full reset, so set PM accordingly */
41162306a36Sopenharmony_ci	if (hba->caps & UFSHCD_CAP_DEEPSLEEP) {
41262306a36Sopenharmony_ci		hba->spm_lvl = UFS_PM_LVL_6;
41362306a36Sopenharmony_ci		hba->rpm_lvl = UFS_PM_LVL_6;
41462306a36Sopenharmony_ci	} else {
41562306a36Sopenharmony_ci		hba->spm_lvl = UFS_PM_LVL_5;
41662306a36Sopenharmony_ci		hba->rpm_lvl = UFS_PM_LVL_5;
41762306a36Sopenharmony_ci	}
41862306a36Sopenharmony_ci}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_cistatic int ufs_intel_lkf_init(struct ufs_hba *hba)
42162306a36Sopenharmony_ci{
42262306a36Sopenharmony_ci	struct ufs_host *ufs_host;
42362306a36Sopenharmony_ci	int err;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	hba->nop_out_timeout = 200;
42662306a36Sopenharmony_ci	hba->quirks |= UFSHCD_QUIRK_BROKEN_AUTO_HIBERN8;
42762306a36Sopenharmony_ci	hba->caps |= UFSHCD_CAP_CRYPTO;
42862306a36Sopenharmony_ci	err = ufs_intel_common_init(hba);
42962306a36Sopenharmony_ci	ufs_host = ufshcd_get_variant(hba);
43062306a36Sopenharmony_ci	ufs_host->late_init = ufs_intel_lkf_late_init;
43162306a36Sopenharmony_ci	return err;
43262306a36Sopenharmony_ci}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_cistatic int ufs_intel_adl_init(struct ufs_hba *hba)
43562306a36Sopenharmony_ci{
43662306a36Sopenharmony_ci	hba->nop_out_timeout = 200;
43762306a36Sopenharmony_ci	hba->quirks |= UFSHCD_QUIRK_BROKEN_AUTO_HIBERN8;
43862306a36Sopenharmony_ci	hba->caps |= UFSHCD_CAP_WB_EN;
43962306a36Sopenharmony_ci	return ufs_intel_common_init(hba);
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cistatic int ufs_intel_mtl_init(struct ufs_hba *hba)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	hba->caps |= UFSHCD_CAP_CRYPTO | UFSHCD_CAP_WB_EN;
44562306a36Sopenharmony_ci	return ufs_intel_common_init(hba);
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_cistatic struct ufs_hba_variant_ops ufs_intel_cnl_hba_vops = {
44962306a36Sopenharmony_ci	.name                   = "intel-pci",
45062306a36Sopenharmony_ci	.init			= ufs_intel_common_init,
45162306a36Sopenharmony_ci	.exit			= ufs_intel_common_exit,
45262306a36Sopenharmony_ci	.link_startup_notify	= ufs_intel_link_startup_notify,
45362306a36Sopenharmony_ci	.resume			= ufs_intel_resume,
45462306a36Sopenharmony_ci};
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_cistatic struct ufs_hba_variant_ops ufs_intel_ehl_hba_vops = {
45762306a36Sopenharmony_ci	.name                   = "intel-pci",
45862306a36Sopenharmony_ci	.init			= ufs_intel_ehl_init,
45962306a36Sopenharmony_ci	.exit			= ufs_intel_common_exit,
46062306a36Sopenharmony_ci	.link_startup_notify	= ufs_intel_link_startup_notify,
46162306a36Sopenharmony_ci	.resume			= ufs_intel_resume,
46262306a36Sopenharmony_ci};
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_cistatic struct ufs_hba_variant_ops ufs_intel_lkf_hba_vops = {
46562306a36Sopenharmony_ci	.name                   = "intel-pci",
46662306a36Sopenharmony_ci	.init			= ufs_intel_lkf_init,
46762306a36Sopenharmony_ci	.exit			= ufs_intel_common_exit,
46862306a36Sopenharmony_ci	.hce_enable_notify	= ufs_intel_hce_enable_notify,
46962306a36Sopenharmony_ci	.link_startup_notify	= ufs_intel_link_startup_notify,
47062306a36Sopenharmony_ci	.pwr_change_notify	= ufs_intel_lkf_pwr_change_notify,
47162306a36Sopenharmony_ci	.apply_dev_quirks	= ufs_intel_lkf_apply_dev_quirks,
47262306a36Sopenharmony_ci	.resume			= ufs_intel_resume,
47362306a36Sopenharmony_ci	.device_reset		= ufs_intel_device_reset,
47462306a36Sopenharmony_ci};
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_cistatic struct ufs_hba_variant_ops ufs_intel_adl_hba_vops = {
47762306a36Sopenharmony_ci	.name			= "intel-pci",
47862306a36Sopenharmony_ci	.init			= ufs_intel_adl_init,
47962306a36Sopenharmony_ci	.exit			= ufs_intel_common_exit,
48062306a36Sopenharmony_ci	.link_startup_notify	= ufs_intel_link_startup_notify,
48162306a36Sopenharmony_ci	.resume			= ufs_intel_resume,
48262306a36Sopenharmony_ci	.device_reset		= ufs_intel_device_reset,
48362306a36Sopenharmony_ci};
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_cistatic struct ufs_hba_variant_ops ufs_intel_mtl_hba_vops = {
48662306a36Sopenharmony_ci	.name                   = "intel-pci",
48762306a36Sopenharmony_ci	.init			= ufs_intel_mtl_init,
48862306a36Sopenharmony_ci	.exit			= ufs_intel_common_exit,
48962306a36Sopenharmony_ci	.hce_enable_notify	= ufs_intel_hce_enable_notify,
49062306a36Sopenharmony_ci	.link_startup_notify	= ufs_intel_link_startup_notify,
49162306a36Sopenharmony_ci	.resume			= ufs_intel_resume,
49262306a36Sopenharmony_ci	.device_reset		= ufs_intel_device_reset,
49362306a36Sopenharmony_ci};
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
49662306a36Sopenharmony_cistatic int ufshcd_pci_restore(struct device *dev)
49762306a36Sopenharmony_ci{
49862306a36Sopenharmony_ci	struct ufs_hba *hba = dev_get_drvdata(dev);
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	/* Force a full reset and restore */
50162306a36Sopenharmony_ci	ufshcd_set_link_off(hba);
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	return ufshcd_system_resume(dev);
50462306a36Sopenharmony_ci}
50562306a36Sopenharmony_ci#endif
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci/**
50862306a36Sopenharmony_ci * ufshcd_pci_remove - de-allocate PCI/SCSI host and host memory space
50962306a36Sopenharmony_ci *		data structure memory
51062306a36Sopenharmony_ci * @pdev: pointer to PCI handle
51162306a36Sopenharmony_ci */
51262306a36Sopenharmony_cistatic void ufshcd_pci_remove(struct pci_dev *pdev)
51362306a36Sopenharmony_ci{
51462306a36Sopenharmony_ci	struct ufs_hba *hba = pci_get_drvdata(pdev);
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	pm_runtime_forbid(&pdev->dev);
51762306a36Sopenharmony_ci	pm_runtime_get_noresume(&pdev->dev);
51862306a36Sopenharmony_ci	ufshcd_remove(hba);
51962306a36Sopenharmony_ci	ufshcd_dealloc_host(hba);
52062306a36Sopenharmony_ci}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci/**
52362306a36Sopenharmony_ci * ufshcd_pci_probe - probe routine of the driver
52462306a36Sopenharmony_ci * @pdev: pointer to PCI device handle
52562306a36Sopenharmony_ci * @id: PCI device id
52662306a36Sopenharmony_ci *
52762306a36Sopenharmony_ci * Return: 0 on success, non-zero value on failure.
52862306a36Sopenharmony_ci */
52962306a36Sopenharmony_cistatic int
53062306a36Sopenharmony_ciufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
53162306a36Sopenharmony_ci{
53262306a36Sopenharmony_ci	struct ufs_host *ufs_host;
53362306a36Sopenharmony_ci	struct ufs_hba *hba;
53462306a36Sopenharmony_ci	void __iomem *mmio_base;
53562306a36Sopenharmony_ci	int err;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	err = pcim_enable_device(pdev);
53862306a36Sopenharmony_ci	if (err) {
53962306a36Sopenharmony_ci		dev_err(&pdev->dev, "pcim_enable_device failed\n");
54062306a36Sopenharmony_ci		return err;
54162306a36Sopenharmony_ci	}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	pci_set_master(pdev);
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	err = pcim_iomap_regions(pdev, 1 << 0, UFSHCD);
54662306a36Sopenharmony_ci	if (err < 0) {
54762306a36Sopenharmony_ci		dev_err(&pdev->dev, "request and iomap failed\n");
54862306a36Sopenharmony_ci		return err;
54962306a36Sopenharmony_ci	}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	mmio_base = pcim_iomap_table(pdev)[0];
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	err = ufshcd_alloc_host(&pdev->dev, &hba);
55462306a36Sopenharmony_ci	if (err) {
55562306a36Sopenharmony_ci		dev_err(&pdev->dev, "Allocation failed\n");
55662306a36Sopenharmony_ci		return err;
55762306a36Sopenharmony_ci	}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	hba->vops = (struct ufs_hba_variant_ops *)id->driver_data;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	err = ufshcd_init(hba, mmio_base, pdev->irq);
56262306a36Sopenharmony_ci	if (err) {
56362306a36Sopenharmony_ci		dev_err(&pdev->dev, "Initialization failed\n");
56462306a36Sopenharmony_ci		ufshcd_dealloc_host(hba);
56562306a36Sopenharmony_ci		return err;
56662306a36Sopenharmony_ci	}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	ufs_host = ufshcd_get_variant(hba);
56962306a36Sopenharmony_ci	if (ufs_host && ufs_host->late_init)
57062306a36Sopenharmony_ci		ufs_host->late_init(hba);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	pm_runtime_put_noidle(&pdev->dev);
57362306a36Sopenharmony_ci	pm_runtime_allow(&pdev->dev);
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	return 0;
57662306a36Sopenharmony_ci}
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_cistatic const struct dev_pm_ops ufshcd_pci_pm_ops = {
57962306a36Sopenharmony_ci	SET_RUNTIME_PM_OPS(ufshcd_runtime_suspend, ufshcd_runtime_resume, NULL)
58062306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
58162306a36Sopenharmony_ci	.suspend	= ufshcd_system_suspend,
58262306a36Sopenharmony_ci	.resume		= ufshcd_system_resume,
58362306a36Sopenharmony_ci	.freeze		= ufshcd_system_suspend,
58462306a36Sopenharmony_ci	.thaw		= ufshcd_system_resume,
58562306a36Sopenharmony_ci	.poweroff	= ufshcd_system_suspend,
58662306a36Sopenharmony_ci	.restore	= ufshcd_pci_restore,
58762306a36Sopenharmony_ci	.prepare	= ufshcd_suspend_prepare,
58862306a36Sopenharmony_ci	.complete	= ufshcd_resume_complete,
58962306a36Sopenharmony_ci#endif
59062306a36Sopenharmony_ci};
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_cistatic const struct pci_device_id ufshcd_pci_tbl[] = {
59362306a36Sopenharmony_ci	{ PCI_VENDOR_ID_REDHAT, 0x0013, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
59462306a36Sopenharmony_ci	{ PCI_VENDOR_ID_SAMSUNG, 0xC00C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
59562306a36Sopenharmony_ci	{ PCI_VDEVICE(INTEL, 0x9DFA), (kernel_ulong_t)&ufs_intel_cnl_hba_vops },
59662306a36Sopenharmony_ci	{ PCI_VDEVICE(INTEL, 0x4B41), (kernel_ulong_t)&ufs_intel_ehl_hba_vops },
59762306a36Sopenharmony_ci	{ PCI_VDEVICE(INTEL, 0x4B43), (kernel_ulong_t)&ufs_intel_ehl_hba_vops },
59862306a36Sopenharmony_ci	{ PCI_VDEVICE(INTEL, 0x98FA), (kernel_ulong_t)&ufs_intel_lkf_hba_vops },
59962306a36Sopenharmony_ci	{ PCI_VDEVICE(INTEL, 0x51FF), (kernel_ulong_t)&ufs_intel_adl_hba_vops },
60062306a36Sopenharmony_ci	{ PCI_VDEVICE(INTEL, 0x54FF), (kernel_ulong_t)&ufs_intel_adl_hba_vops },
60162306a36Sopenharmony_ci	{ PCI_VDEVICE(INTEL, 0x7E47), (kernel_ulong_t)&ufs_intel_mtl_hba_vops },
60262306a36Sopenharmony_ci	{ PCI_VDEVICE(INTEL, 0xA847), (kernel_ulong_t)&ufs_intel_mtl_hba_vops },
60362306a36Sopenharmony_ci	{ PCI_VDEVICE(INTEL, 0x7747), (kernel_ulong_t)&ufs_intel_mtl_hba_vops },
60462306a36Sopenharmony_ci	{ }	/* terminate list */
60562306a36Sopenharmony_ci};
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, ufshcd_pci_tbl);
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_cistatic struct pci_driver ufshcd_pci_driver = {
61062306a36Sopenharmony_ci	.name = UFSHCD,
61162306a36Sopenharmony_ci	.id_table = ufshcd_pci_tbl,
61262306a36Sopenharmony_ci	.probe = ufshcd_pci_probe,
61362306a36Sopenharmony_ci	.remove = ufshcd_pci_remove,
61462306a36Sopenharmony_ci	.driver = {
61562306a36Sopenharmony_ci		.pm = &ufshcd_pci_pm_ops
61662306a36Sopenharmony_ci	},
61762306a36Sopenharmony_ci};
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_cimodule_pci_driver(ufshcd_pci_driver);
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ciMODULE_AUTHOR("Santosh Yaragnavi <santosh.sy@samsung.com>");
62262306a36Sopenharmony_ciMODULE_AUTHOR("Vinayak Holikatti <h.vinayak@samsung.com>");
62362306a36Sopenharmony_ciMODULE_DESCRIPTION("UFS host controller PCI glue driver");
62462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
625