FILE
mipi-i3c-hci-pci.c
File preview with syntax highlighting.
linux-6.19.14/drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * PCI glue code for MIPI I3C HCI driver 4 * 5 * Copyright (C) 2024 Intel Corporation 6 * 7 * Author: Jarkko Nikula <jarkko.nikula@linux.intel.com> 8 */ 9#include <linux/acpi.h> 10#include <linux/bitfield.h> 11#include <linux/debugfs.h> 12#include <linux/idr.h> 13#include <linux/iopoll.h> 14#include <linux/kernel.h> 15#include <linux/module.h> 16#include <linux/pci.h> 17#include <linux/platform_device.h> 18#include <linux/pm_qos.h> 19 20struct mipi_i3c_hci_pci { 21 struct pci_dev *pci; 22 struct platform_device *pdev; 23 const struct mipi_i3c_hci_pci_info *info; 24 void *private; 25}; 26 27struct mipi_i3c_hci_pci_info { 28 int (*init)(struct mipi_i3c_hci_pci *hci); 29 void (*exit)(struct mipi_i3c_hci_pci *hci); 30}; 31 32static DEFINE_IDA(mipi_i3c_hci_pci_ida); 33 34#define INTEL_PRIV_OFFSET 0x2b0 35#define INTEL_PRIV_SIZE 0x28 36#define INTEL_RESETS 0x04 37#define INTEL_RESETS_RESET BIT(0) 38#define INTEL_RESETS_RESET_DONE BIT(1) 39#define INTEL_RESETS_TIMEOUT_US (10 * USEC_PER_MSEC) 40 41#define INTEL_ACTIVELTR 0x0c 42#define INTEL_IDLELTR 0x10 43 44#define INTEL_LTR_REQ BIT(15) 45#define INTEL_LTR_SCALE_MASK GENMASK(11, 10) 46#define INTEL_LTR_SCALE_1US FIELD_PREP(INTEL_LTR_SCALE_MASK, 2) 47#define INTEL_LTR_SCALE_32US FIELD_PREP(INTEL_LTR_SCALE_MASK, 3) 48#define INTEL_LTR_VALUE_MASK GENMASK(9, 0) 49 50struct intel_host { 51 void __iomem *priv; 52 u32 active_ltr; 53 u32 idle_ltr; 54 struct dentry *debugfs_root; 55}; 56 57static void intel_cache_ltr(struct intel_host *host) 58{ 59 host->active_ltr = readl(host->priv + INTEL_ACTIVELTR); 60 host->idle_ltr = readl(host->priv + INTEL_IDLELTR); 61} 62 63static void intel_ltr_set(struct device *dev, s32 val) 64{ 65 struct mipi_i3c_hci_pci *hci = dev_get_drvdata(dev); 66 struct intel_host *host = hci->private; 67 u32 ltr; 68 69 /* 70 * Program latency tolerance (LTR) accordingly what has been asked 71 * by the PM QoS layer or disable it in case we were passed 72 * negative value or PM_QOS_LATENCY_ANY. 73 */ 74 ltr = readl(host->priv + INTEL_ACTIVELTR); 75 76 if (val == PM_QOS_LATENCY_ANY || val < 0) { 77 ltr &= ~INTEL_LTR_REQ; 78 } else { 79 ltr |= INTEL_LTR_REQ; 80 ltr &= ~INTEL_LTR_SCALE_MASK; 81 ltr &= ~INTEL_LTR_VALUE_MASK; 82 83 if (val > INTEL_LTR_VALUE_MASK) { 84 val >>= 5; 85 if (val > INTEL_LTR_VALUE_MASK) 86 val = INTEL_LTR_VALUE_MASK; 87 ltr |= INTEL_LTR_SCALE_32US | val; 88 } else { 89 ltr |= INTEL_LTR_SCALE_1US | val; 90 } 91 } 92 93 if (ltr == host->active_ltr) 94 return; 95 96 writel(ltr, host->priv + INTEL_ACTIVELTR); 97 writel(ltr, host->priv + INTEL_IDLELTR); 98 99 /* Cache the values into intel_host structure */ 100 intel_cache_ltr(host); 101} 102 103static void intel_ltr_expose(struct device *dev) 104{ 105 dev->power.set_latency_tolerance = intel_ltr_set; 106 dev_pm_qos_expose_latency_tolerance(dev); 107} 108 109static void intel_ltr_hide(struct device *dev) 110{ 111 dev_pm_qos_hide_latency_tolerance(dev); 112 dev->power.set_latency_tolerance = NULL; 113} 114 115static void intel_add_debugfs(struct mipi_i3c_hci_pci *hci) 116{ 117 struct dentry *dir = debugfs_create_dir(dev_name(&hci->pci->dev), NULL); 118 struct intel_host *host = hci->private; 119 120 intel_cache_ltr(host); 121 122 host->debugfs_root = dir; 123 debugfs_create_x32("active_ltr", 0444, dir, &host->active_ltr); 124 debugfs_create_x32("idle_ltr", 0444, dir, &host->idle_ltr); 125} 126 127static void intel_remove_debugfs(struct mipi_i3c_hci_pci *hci) 128{ 129 struct intel_host *host = hci->private; 130 131 debugfs_remove_recursive(host->debugfs_root); 132} 133 134static void intel_reset(void __iomem *priv) 135{ 136 u32 reg; 137 138 /* Assert reset, wait for completion and release reset */ 139 writel(0, priv + INTEL_RESETS); 140 readl_poll_timeout(priv + INTEL_RESETS, reg, 141 reg & INTEL_RESETS_RESET_DONE, 0, 142 INTEL_RESETS_TIMEOUT_US); 143 writel(INTEL_RESETS_RESET, priv + INTEL_RESETS); 144} 145 146static void __iomem *intel_priv(struct pci_dev *pci) 147{ 148 resource_size_t base = pci_resource_start(pci, 0); 149 150 return devm_ioremap(&pci->dev, base + INTEL_PRIV_OFFSET, INTEL_PRIV_SIZE); 151} 152 153static int intel_i3c_init(struct mipi_i3c_hci_pci *hci) 154{ 155 struct intel_host *host = devm_kzalloc(&hci->pci->dev, sizeof(*host), GFP_KERNEL); 156 void __iomem *priv = intel_priv(hci->pci); 157 158 if (!host || !priv) 159 return -ENOMEM; 160 161 dma_set_mask_and_coherent(&hci->pci->dev, DMA_BIT_MASK(64)); 162 163 hci->pci->d3cold_delay = 0; 164 165 hci->private = host; 166 host->priv = priv; 167 168 intel_reset(priv); 169 170 intel_ltr_expose(&hci->pci->dev); 171 intel_add_debugfs(hci); 172 173 return 0; 174} 175 176static void intel_i3c_exit(struct mipi_i3c_hci_pci *hci) 177{ 178 intel_remove_debugfs(hci); 179 intel_ltr_hide(&hci->pci->dev); 180} 181 182static const struct mipi_i3c_hci_pci_info intel_info = { 183 .init = intel_i3c_init, 184 .exit = intel_i3c_exit, 185}; 186 187static int mipi_i3c_hci_pci_probe(struct pci_dev *pci, 188 const struct pci_device_id *id) 189{ 190 struct mipi_i3c_hci_pci *hci; 191 struct resource res[2]; 192 int dev_id, ret; 193 194 hci = devm_kzalloc(&pci->dev, sizeof(*hci), GFP_KERNEL); 195 if (!hci) 196 return -ENOMEM; 197 198 hci->pci = pci; 199 200 ret = pcim_enable_device(pci); 201 if (ret) 202 return ret; 203 204 pci_set_master(pci); 205 206 memset(&res, 0, sizeof(res)); 207 208 res[0].flags = IORESOURCE_MEM; 209 res[0].start = pci_resource_start(pci, 0); 210 res[0].end = pci_resource_end(pci, 0); 211 212 res[1].flags = IORESOURCE_IRQ; 213 res[1].start = pci->irq; 214 res[1].end = pci->irq; 215 216 dev_id = ida_alloc(&mipi_i3c_hci_pci_ida, GFP_KERNEL); 217 if (dev_id < 0) 218 return dev_id; 219 220 hci->pdev = platform_device_alloc("mipi-i3c-hci", dev_id); 221 if (!hci->pdev) 222 return -ENOMEM; 223 224 hci->pdev->dev.parent = &pci->dev; 225 device_set_node(&hci->pdev->dev, dev_fwnode(&pci->dev)); 226 227 ret = platform_device_add_resources(hci->pdev, res, ARRAY_SIZE(res)); 228 if (ret) 229 goto err; 230 231 hci->info = (const struct mipi_i3c_hci_pci_info *)id->driver_data; 232 if (hci->info && hci->info->init) { 233 ret = hci->info->init(hci); 234 if (ret) 235 goto err; 236 } 237 238 ret = platform_device_add(hci->pdev); 239 if (ret) 240 goto err_exit; 241 242 pci_set_drvdata(pci, hci); 243 244 return 0; 245 246err_exit: 247 if (hci->info && hci->info->exit) 248 hci->info->exit(hci); 249err: 250 platform_device_put(hci->pdev); 251 ida_free(&mipi_i3c_hci_pci_ida, dev_id); 252 return ret; 253} 254 255static void mipi_i3c_hci_pci_remove(struct pci_dev *pci) 256{ 257 struct mipi_i3c_hci_pci *hci = pci_get_drvdata(pci); 258 struct platform_device *pdev = hci->pdev; 259 int dev_id = pdev->id; 260 261 if (hci->info && hci->info->exit) 262 hci->info->exit(hci); 263 264 platform_device_unregister(pdev); 265 ida_free(&mipi_i3c_hci_pci_ida, dev_id); 266} 267 268static const struct pci_device_id mipi_i3c_hci_pci_devices[] = { 269 /* Wildcat Lake-U */ 270 { PCI_VDEVICE(INTEL, 0x4d7c), (kernel_ulong_t)&intel_info}, 271 { PCI_VDEVICE(INTEL, 0x4d6f), (kernel_ulong_t)&intel_info}, 272 /* Panther Lake-H */ 273 { PCI_VDEVICE(INTEL, 0xe37c), (kernel_ulong_t)&intel_info}, 274 { PCI_VDEVICE(INTEL, 0xe36f), (kernel_ulong_t)&intel_info}, 275 /* Panther Lake-P */ 276 { PCI_VDEVICE(INTEL, 0xe47c), (kernel_ulong_t)&intel_info}, 277 { PCI_VDEVICE(INTEL, 0xe46f), (kernel_ulong_t)&intel_info}, 278 /* Nova Lake-S */ 279 { PCI_VDEVICE(INTEL, 0x6e2c), (kernel_ulong_t)&intel_info}, 280 { PCI_VDEVICE(INTEL, 0x6e2d), (kernel_ulong_t)&intel_info}, 281 { }, 282}; 283MODULE_DEVICE_TABLE(pci, mipi_i3c_hci_pci_devices); 284 285static struct pci_driver mipi_i3c_hci_pci_driver = { 286 .name = "mipi_i3c_hci_pci", 287 .id_table = mipi_i3c_hci_pci_devices, 288 .probe = mipi_i3c_hci_pci_probe, 289 .remove = mipi_i3c_hci_pci_remove, 290}; 291 292module_pci_driver(mipi_i3c_hci_pci_driver); 293 294MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@intel.com>"); 295MODULE_LICENSE("GPL"); 296MODULE_DESCRIPTION("MIPI I3C HCI driver on PCI bus");
~/
Read-only file preview.