| .. | .. |
|---|
| 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 | |
|---|