.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * Universal Flash Storage Host controller PCI glue driver |
---|
3 | 4 | * |
---|
.. | .. |
---|
7 | 8 | * Authors: |
---|
8 | 9 | * Santosh Yaraganavi <santosh.sy@samsung.com> |
---|
9 | 10 | * Vinayak Holikatti <h.vinayak@samsung.com> |
---|
10 | | - * |
---|
11 | | - * This program is free software; you can redistribute it and/or |
---|
12 | | - * modify it under the terms of the GNU General Public License |
---|
13 | | - * as published by the Free Software Foundation; either version 2 |
---|
14 | | - * of the License, or (at your option) any later version. |
---|
15 | | - * See the COPYING file in the top-level directory or visit |
---|
16 | | - * <http://www.gnu.org/licenses/gpl-2.0.html> |
---|
17 | | - * |
---|
18 | | - * This program is distributed in the hope that it will be useful, |
---|
19 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
20 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
21 | | - * GNU General Public License for more details. |
---|
22 | | - * |
---|
23 | | - * This program is provided "AS IS" and "WITH ALL FAULTS" and |
---|
24 | | - * without warranty of any kind. You are solely responsible for |
---|
25 | | - * determining the appropriateness of using and distributing |
---|
26 | | - * the program and assume all risks associated with your exercise |
---|
27 | | - * of rights with respect to the program, including but not limited |
---|
28 | | - * to infringement of third party rights, the risks and costs of |
---|
29 | | - * program errors, damage to or loss of data, programs or equipment, |
---|
30 | | - * and unavailability or interruption of operations. Under no |
---|
31 | | - * circumstances will the contributor of this Program be liable for |
---|
32 | | - * any damages of any kind arising from your use or distribution of |
---|
33 | | - * this program. |
---|
34 | 11 | */ |
---|
35 | 12 | |
---|
36 | 13 | #include "ufshcd.h" |
---|
37 | 14 | #include <linux/pci.h> |
---|
38 | 15 | #include <linux/pm_runtime.h> |
---|
| 16 | +#include <linux/pm_qos.h> |
---|
| 17 | +#include <linux/debugfs.h> |
---|
| 18 | + |
---|
| 19 | +struct intel_host { |
---|
| 20 | + u32 active_ltr; |
---|
| 21 | + u32 idle_ltr; |
---|
| 22 | + struct dentry *debugfs_root; |
---|
| 23 | +}; |
---|
39 | 24 | |
---|
40 | 25 | static int ufs_intel_disable_lcc(struct ufs_hba *hba) |
---|
41 | 26 | { |
---|
.. | .. |
---|
44 | 29 | |
---|
45 | 30 | ufshcd_dme_get(hba, attr, &lcc_enable); |
---|
46 | 31 | if (lcc_enable) |
---|
47 | | - ufshcd_dme_set(hba, attr, 0); |
---|
| 32 | + ufshcd_disable_host_tx_lcc(hba); |
---|
48 | 33 | |
---|
49 | 34 | return 0; |
---|
50 | 35 | } |
---|
.. | .. |
---|
67 | 52 | return err; |
---|
68 | 53 | } |
---|
69 | 54 | |
---|
| 55 | +#define INTEL_ACTIVELTR 0x804 |
---|
| 56 | +#define INTEL_IDLELTR 0x808 |
---|
| 57 | + |
---|
| 58 | +#define INTEL_LTR_REQ BIT(15) |
---|
| 59 | +#define INTEL_LTR_SCALE_MASK GENMASK(11, 10) |
---|
| 60 | +#define INTEL_LTR_SCALE_1US (2 << 10) |
---|
| 61 | +#define INTEL_LTR_SCALE_32US (3 << 10) |
---|
| 62 | +#define INTEL_LTR_VALUE_MASK GENMASK(9, 0) |
---|
| 63 | + |
---|
| 64 | +static void intel_cache_ltr(struct ufs_hba *hba) |
---|
| 65 | +{ |
---|
| 66 | + struct intel_host *host = ufshcd_get_variant(hba); |
---|
| 67 | + |
---|
| 68 | + host->active_ltr = readl(hba->mmio_base + INTEL_ACTIVELTR); |
---|
| 69 | + host->idle_ltr = readl(hba->mmio_base + INTEL_IDLELTR); |
---|
| 70 | +} |
---|
| 71 | + |
---|
| 72 | +static void intel_ltr_set(struct device *dev, s32 val) |
---|
| 73 | +{ |
---|
| 74 | + struct ufs_hba *hba = dev_get_drvdata(dev); |
---|
| 75 | + struct intel_host *host = ufshcd_get_variant(hba); |
---|
| 76 | + u32 ltr; |
---|
| 77 | + |
---|
| 78 | + pm_runtime_get_sync(dev); |
---|
| 79 | + |
---|
| 80 | + /* |
---|
| 81 | + * Program latency tolerance (LTR) accordingly what has been asked |
---|
| 82 | + * by the PM QoS layer or disable it in case we were passed |
---|
| 83 | + * negative value or PM_QOS_LATENCY_ANY. |
---|
| 84 | + */ |
---|
| 85 | + ltr = readl(hba->mmio_base + INTEL_ACTIVELTR); |
---|
| 86 | + |
---|
| 87 | + if (val == PM_QOS_LATENCY_ANY || val < 0) { |
---|
| 88 | + ltr &= ~INTEL_LTR_REQ; |
---|
| 89 | + } else { |
---|
| 90 | + ltr |= INTEL_LTR_REQ; |
---|
| 91 | + ltr &= ~INTEL_LTR_SCALE_MASK; |
---|
| 92 | + ltr &= ~INTEL_LTR_VALUE_MASK; |
---|
| 93 | + |
---|
| 94 | + if (val > INTEL_LTR_VALUE_MASK) { |
---|
| 95 | + val >>= 5; |
---|
| 96 | + if (val > INTEL_LTR_VALUE_MASK) |
---|
| 97 | + val = INTEL_LTR_VALUE_MASK; |
---|
| 98 | + ltr |= INTEL_LTR_SCALE_32US | val; |
---|
| 99 | + } else { |
---|
| 100 | + ltr |= INTEL_LTR_SCALE_1US | val; |
---|
| 101 | + } |
---|
| 102 | + } |
---|
| 103 | + |
---|
| 104 | + if (ltr == host->active_ltr) |
---|
| 105 | + goto out; |
---|
| 106 | + |
---|
| 107 | + writel(ltr, hba->mmio_base + INTEL_ACTIVELTR); |
---|
| 108 | + writel(ltr, hba->mmio_base + INTEL_IDLELTR); |
---|
| 109 | + |
---|
| 110 | + /* Cache the values into intel_host structure */ |
---|
| 111 | + intel_cache_ltr(hba); |
---|
| 112 | +out: |
---|
| 113 | + pm_runtime_put(dev); |
---|
| 114 | +} |
---|
| 115 | + |
---|
| 116 | +static void intel_ltr_expose(struct device *dev) |
---|
| 117 | +{ |
---|
| 118 | + dev->power.set_latency_tolerance = intel_ltr_set; |
---|
| 119 | + dev_pm_qos_expose_latency_tolerance(dev); |
---|
| 120 | +} |
---|
| 121 | + |
---|
| 122 | +static void intel_ltr_hide(struct device *dev) |
---|
| 123 | +{ |
---|
| 124 | + dev_pm_qos_hide_latency_tolerance(dev); |
---|
| 125 | + dev->power.set_latency_tolerance = NULL; |
---|
| 126 | +} |
---|
| 127 | + |
---|
| 128 | +static void intel_add_debugfs(struct ufs_hba *hba) |
---|
| 129 | +{ |
---|
| 130 | + struct dentry *dir = debugfs_create_dir(dev_name(hba->dev), NULL); |
---|
| 131 | + struct intel_host *host = ufshcd_get_variant(hba); |
---|
| 132 | + |
---|
| 133 | + intel_cache_ltr(hba); |
---|
| 134 | + |
---|
| 135 | + host->debugfs_root = dir; |
---|
| 136 | + debugfs_create_x32("active_ltr", 0444, dir, &host->active_ltr); |
---|
| 137 | + debugfs_create_x32("idle_ltr", 0444, dir, &host->idle_ltr); |
---|
| 138 | +} |
---|
| 139 | + |
---|
| 140 | +static void intel_remove_debugfs(struct ufs_hba *hba) |
---|
| 141 | +{ |
---|
| 142 | + struct intel_host *host = ufshcd_get_variant(hba); |
---|
| 143 | + |
---|
| 144 | + debugfs_remove_recursive(host->debugfs_root); |
---|
| 145 | +} |
---|
| 146 | + |
---|
| 147 | +static int ufs_intel_common_init(struct ufs_hba *hba) |
---|
| 148 | +{ |
---|
| 149 | + struct intel_host *host; |
---|
| 150 | + |
---|
| 151 | + hba->caps |= UFSHCD_CAP_RPM_AUTOSUSPEND; |
---|
| 152 | + |
---|
| 153 | + host = devm_kzalloc(hba->dev, sizeof(*host), GFP_KERNEL); |
---|
| 154 | + if (!host) |
---|
| 155 | + return -ENOMEM; |
---|
| 156 | + ufshcd_set_variant(hba, host); |
---|
| 157 | + intel_ltr_expose(hba->dev); |
---|
| 158 | + intel_add_debugfs(hba); |
---|
| 159 | + return 0; |
---|
| 160 | +} |
---|
| 161 | + |
---|
| 162 | +static void ufs_intel_common_exit(struct ufs_hba *hba) |
---|
| 163 | +{ |
---|
| 164 | + intel_remove_debugfs(hba); |
---|
| 165 | + intel_ltr_hide(hba->dev); |
---|
| 166 | +} |
---|
| 167 | + |
---|
| 168 | +static int ufs_intel_resume(struct ufs_hba *hba, enum ufs_pm_op op) |
---|
| 169 | +{ |
---|
| 170 | + /* |
---|
| 171 | + * To support S4 (suspend-to-disk) with spm_lvl other than 5, the base |
---|
| 172 | + * address registers must be restored because the restore kernel can |
---|
| 173 | + * have used different addresses. |
---|
| 174 | + */ |
---|
| 175 | + ufshcd_writel(hba, lower_32_bits(hba->utrdl_dma_addr), |
---|
| 176 | + REG_UTP_TRANSFER_REQ_LIST_BASE_L); |
---|
| 177 | + ufshcd_writel(hba, upper_32_bits(hba->utrdl_dma_addr), |
---|
| 178 | + REG_UTP_TRANSFER_REQ_LIST_BASE_H); |
---|
| 179 | + ufshcd_writel(hba, lower_32_bits(hba->utmrdl_dma_addr), |
---|
| 180 | + REG_UTP_TASK_REQ_LIST_BASE_L); |
---|
| 181 | + ufshcd_writel(hba, upper_32_bits(hba->utmrdl_dma_addr), |
---|
| 182 | + REG_UTP_TASK_REQ_LIST_BASE_H); |
---|
| 183 | + |
---|
| 184 | + if (ufshcd_is_link_hibern8(hba)) { |
---|
| 185 | + int ret = ufshcd_uic_hibern8_exit(hba); |
---|
| 186 | + |
---|
| 187 | + if (!ret) { |
---|
| 188 | + ufshcd_set_link_active(hba); |
---|
| 189 | + } else { |
---|
| 190 | + dev_err(hba->dev, "%s: hibern8 exit failed %d\n", |
---|
| 191 | + __func__, ret); |
---|
| 192 | + /* |
---|
| 193 | + * Force reset and restore. Any other actions can lead |
---|
| 194 | + * to an unrecoverable state. |
---|
| 195 | + */ |
---|
| 196 | + ufshcd_set_link_off(hba); |
---|
| 197 | + } |
---|
| 198 | + } |
---|
| 199 | + |
---|
| 200 | + return 0; |
---|
| 201 | +} |
---|
| 202 | + |
---|
| 203 | +static int ufs_intel_ehl_init(struct ufs_hba *hba) |
---|
| 204 | +{ |
---|
| 205 | + hba->quirks |= UFSHCD_QUIRK_BROKEN_AUTO_HIBERN8; |
---|
| 206 | + return ufs_intel_common_init(hba); |
---|
| 207 | +} |
---|
| 208 | + |
---|
70 | 209 | static struct ufs_hba_variant_ops ufs_intel_cnl_hba_vops = { |
---|
71 | 210 | .name = "intel-pci", |
---|
| 211 | + .init = ufs_intel_common_init, |
---|
| 212 | + .exit = ufs_intel_common_exit, |
---|
72 | 213 | .link_startup_notify = ufs_intel_link_startup_notify, |
---|
| 214 | + .resume = ufs_intel_resume, |
---|
| 215 | +}; |
---|
| 216 | + |
---|
| 217 | +static struct ufs_hba_variant_ops ufs_intel_ehl_hba_vops = { |
---|
| 218 | + .name = "intel-pci", |
---|
| 219 | + .init = ufs_intel_ehl_init, |
---|
| 220 | + .exit = ufs_intel_common_exit, |
---|
| 221 | + .link_startup_notify = ufs_intel_link_startup_notify, |
---|
| 222 | + .resume = ufs_intel_resume, |
---|
73 | 223 | }; |
---|
74 | 224 | |
---|
75 | 225 | #ifdef CONFIG_PM_SLEEP |
---|
.. | .. |
---|
206 | 356 | return err; |
---|
207 | 357 | } |
---|
208 | 358 | |
---|
209 | | - pci_set_drvdata(pdev, hba); |
---|
210 | 359 | pm_runtime_put_noidle(&pdev->dev); |
---|
211 | 360 | pm_runtime_allow(&pdev->dev); |
---|
212 | 361 | |
---|
.. | .. |
---|
230 | 379 | static const struct pci_device_id ufshcd_pci_tbl[] = { |
---|
231 | 380 | { PCI_VENDOR_ID_SAMSUNG, 0xC00C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, |
---|
232 | 381 | { PCI_VDEVICE(INTEL, 0x9DFA), (kernel_ulong_t)&ufs_intel_cnl_hba_vops }, |
---|
| 382 | + { PCI_VDEVICE(INTEL, 0x4B41), (kernel_ulong_t)&ufs_intel_ehl_hba_vops }, |
---|
| 383 | + { PCI_VDEVICE(INTEL, 0x4B43), (kernel_ulong_t)&ufs_intel_ehl_hba_vops }, |
---|
233 | 384 | { } /* terminate list */ |
---|
234 | 385 | }; |
---|
235 | 386 | |
---|