hc
2023-12-11 d2ccde1c8e90d38cee87a1b0309ad2827f3fd30d
kernel/drivers/scsi/ufs/ufshcd-pci.c
....@@ -1,3 +1,4 @@
1
+// SPDX-License-Identifier: GPL-2.0-or-later
12 /*
23 * Universal Flash Storage Host controller PCI glue driver
34 *
....@@ -7,35 +8,19 @@
78 * Authors:
89 * Santosh Yaraganavi <santosh.sy@samsung.com>
910 * 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.
3411 */
3512
3613 #include "ufshcd.h"
3714 #include <linux/pci.h>
3815 #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
+};
3924
4025 static int ufs_intel_disable_lcc(struct ufs_hba *hba)
4126 {
....@@ -44,7 +29,7 @@
4429
4530 ufshcd_dme_get(hba, attr, &lcc_enable);
4631 if (lcc_enable)
47
- ufshcd_dme_set(hba, attr, 0);
32
+ ufshcd_disable_host_tx_lcc(hba);
4833
4934 return 0;
5035 }
....@@ -67,9 +52,174 @@
6752 return err;
6853 }
6954
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
+
70209 static struct ufs_hba_variant_ops ufs_intel_cnl_hba_vops = {
71210 .name = "intel-pci",
211
+ .init = ufs_intel_common_init,
212
+ .exit = ufs_intel_common_exit,
72213 .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,
73223 };
74224
75225 #ifdef CONFIG_PM_SLEEP
....@@ -206,7 +356,6 @@
206356 return err;
207357 }
208358
209
- pci_set_drvdata(pdev, hba);
210359 pm_runtime_put_noidle(&pdev->dev);
211360 pm_runtime_allow(&pdev->dev);
212361
....@@ -230,6 +379,8 @@
230379 static const struct pci_device_id ufshcd_pci_tbl[] = {
231380 { PCI_VENDOR_ID_SAMSUNG, 0xC00C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
232381 { 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 },
233384 { } /* terminate list */
234385 };
235386