Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

mei: csc: support controller with separate PCI device

Intel PCI driver for chassis controller embedded in Intel graphics
devices.

An MEI device here called CSC can be embedded in discrete
Intel graphics devices having separate PCI device, to support a range
of chassis tasks such as graphics card firmware update and security tasks.

Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
Link: https://patch.msgid.link/20260201094358.1440593-7-alexander.usyskin@intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Alexander Usyskin and committed by
Greg Kroah-Hartman
72fdf0bb 60ca1597

+311 -1
+11
drivers/misc/mei/Kconfig
··· 58 58 tasks such as graphics card firmware update and security 59 59 tasks. 60 60 61 + config INTEL_MEI_CSC 62 + tristate "Intel MEI CSC embedded device" 63 + depends on INTEL_MEI_ME 64 + help 65 + Intel PCI driver for the chassis controller embedded in Intel graphics devices. 66 + 67 + An MEI device here called CSC can be embedded in discrete 68 + Intel graphics devices, to support a range of chassis 69 + tasks such as graphics card firmware update and security 70 + tasks. 71 + 61 72 config INTEL_MEI_VSC_HW 62 73 tristate "Intel visual sensing controller device transport driver" 63 74 depends on ACPI && SPI
+3
drivers/misc/mei/Makefile
··· 21 21 obj-$(CONFIG_INTEL_MEI_GSC) += mei-gsc.o 22 22 mei-gsc-objs := gsc-me.o 23 23 24 + obj-$(CONFIG_INTEL_MEI_CSC) += mei-csc.o 25 + mei-csc-objs := pci-csc.o 26 + 24 27 obj-$(CONFIG_INTEL_MEI_TXE) += mei-txe.o 25 28 mei-txe-objs := pci-txe.o 26 29 mei-txe-objs += hw-txe.o
+3
drivers/misc/mei/hw-me-regs.h
··· 124 124 125 125 #define PCI_DEVICE_ID_INTEL_MEI_NVL_S 0x6E68 /* Nova Lake Point S */ 126 126 127 + #define PCI_DEVICE_ID_INTEL_MEI_CRI 0x6766 /* Crescent Island */ 128 + 127 129 /* 128 130 * MEI HW Section 129 131 */ ··· 136 134 # define PCI_CFG_HFS_1_OPMODE_MSK 0xf0000 /* OP MODE Mask: SPS <= 4.0 */ 137 135 # define PCI_CFG_HFS_1_OPMODE_SPS 0xf0000 /* SPS SKU : SPS <= 4.0 */ 138 136 #define PCI_CFG_HFS_2 0x48 137 + # define PCI_CFG_HFS_2_D3_BLOCK BIT(7) 139 138 # define PCI_CFG_HFS_2_PM_CMOFF_TO_CMX_ERROR 0x1000000 /* CMoff->CMx wake after an error */ 140 139 # define PCI_CFG_HFS_2_PM_CM_RESET_ERROR 0x5000000 /* CME reset due to exception */ 141 140 # define PCI_CFG_HFS_2_PM_EVENT_MASK 0xf000000
+27
drivers/misc/mei/hw-me.c
··· 224 224 return 0; 225 225 } 226 226 227 + static bool mei_csc_pg_blocked(struct mei_device *dev) 228 + { 229 + struct mei_me_hw *hw = to_me_hw(dev); 230 + u32 reg = 0; 231 + 232 + hw->read_fws(dev, PCI_CFG_HFS_2, "PCI_CFG_HFS_2", &reg); 233 + return (reg & PCI_CFG_HFS_2_D3_BLOCK) == PCI_CFG_HFS_2_D3_BLOCK; 234 + } 235 + 227 236 /** 228 237 * mei_me_hw_config - configure hw dependent settings 229 238 * ··· 1215 1206 return ret; 1216 1207 } else { 1217 1208 hw->pg_state = MEI_PG_OFF; 1209 + dev->pg_blocked = mei_csc_pg_blocked(dev); 1218 1210 } 1219 1211 } 1220 1212 ··· 1304 1294 { 1305 1295 struct mei_device *dev = (struct mei_device *) dev_id; 1306 1296 struct list_head cmpl_list; 1297 + bool pg_blocked; 1307 1298 s32 slots; 1308 1299 u32 hcsr; 1309 1300 int rets = 0; ··· 1362 1351 } 1363 1352 goto end; 1364 1353 } 1354 + 1355 + pg_blocked = mei_csc_pg_blocked(dev); 1356 + if (pg_blocked && !dev->pg_blocked) /* PG block requested */ 1357 + pm_request_resume(&dev->dev); 1358 + else if (!pg_blocked && dev->pg_blocked) /* PG block lifted */ 1359 + pm_request_autosuspend(&dev->dev); 1360 + dev->pg_blocked = pg_blocked; 1361 + 1365 1362 /* check slots available for reading */ 1366 1363 slots = mei_count_full_read_slots(dev); 1367 1364 while (slots > 0) { ··· 1745 1726 MEI_CFG_FW_VER_SUPP, 1746 1727 }; 1747 1728 1729 + /* Chassis System Controller Firmware Interface */ 1730 + static const struct mei_cfg mei_me_csc_cfg = { 1731 + MEI_CFG_TYPE_GSCFI, 1732 + MEI_CFG_PCH8_HFS, 1733 + MEI_CFG_FW_VER_SUPP, 1734 + }; 1735 + 1748 1736 /* 1749 1737 * mei_cfg_list - A list of platform platform specific configurations. 1750 1738 * Note: has to be synchronized with enum mei_cfg_idx. ··· 1774 1748 [MEI_ME_PCH15_SPS_CFG] = &mei_me_pch15_sps_cfg, 1775 1749 [MEI_ME_GSC_CFG] = &mei_me_gsc_cfg, 1776 1750 [MEI_ME_GSCFI_CFG] = &mei_me_gscfi_cfg, 1751 + [MEI_ME_CSC_CFG] = &mei_me_csc_cfg, 1777 1752 }; 1778 1753 1779 1754 const struct mei_cfg *mei_me_get_cfg(kernel_ulong_t idx)
+2
drivers/misc/mei/hw-me.h
··· 104 104 * SPS firmware exclusion. 105 105 * @MEI_ME_GSC_CFG: Graphics System Controller 106 106 * @MEI_ME_GSCFI_CFG: Graphics System Controller Firmware Interface 107 + * @MEI_ME_CSC_CFG: Chassis System Controller Firmware Interface 107 108 * @MEI_ME_NUM_CFG: Upper Sentinel. 108 109 */ 109 110 enum mei_cfg_idx { ··· 125 124 MEI_ME_PCH15_SPS_CFG, 126 125 MEI_ME_GSC_CFG, 127 126 MEI_ME_GSCFI_CFG, 127 + MEI_ME_CSC_CFG, 128 128 MEI_ME_NUM_CFG, 129 129 }; 130 130
+3 -1
drivers/misc/mei/init.c
··· 344 344 bool mei_write_is_idle(struct mei_device *dev) 345 345 { 346 346 bool idle = (dev->dev_state == MEI_DEV_ENABLED && 347 + !dev->pg_blocked && 347 348 list_empty(&dev->ctrl_wr_list) && 348 349 list_empty(&dev->write_list) && 349 350 list_empty(&dev->write_waiting_list)); 350 351 351 - dev_dbg(&dev->dev, "write pg: is idle[%d] state=%s ctrl=%d write=%d wwait=%d\n", 352 + dev_dbg(&dev->dev, "write pg: is idle[%d] state=%s blocked=%d ctrl=%d write=%d wwait=%d\n", 352 353 idle, 353 354 mei_dev_state_str(dev->dev_state), 355 + dev->pg_blocked, 354 356 list_empty(&dev->ctrl_wr_list), 355 357 list_empty(&dev->write_list), 356 358 list_empty(&dev->write_waiting_list));
+3
drivers/misc/mei/mei_dev.h
··· 490 490 * @timer_work : MEI timer delayed work (timeouts) 491 491 * 492 492 * @recvd_hw_ready : hw ready message received flag 493 + * @pg_blocked : low power mode is not allowed 493 494 * 494 495 * @wait_hw_ready : wait queue for receive HW ready message form FW 495 496 * @wait_pg : wait queue for receive PG message from FW ··· 576 575 struct delayed_work timer_work; 577 576 578 577 bool recvd_hw_ready; 578 + bool pg_blocked; 579 + 579 580 /* 580 581 * waiting queue for receive message from FW 581 582 */
+259
drivers/misc/mei/pci-csc.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2025-2026, Intel Corporation. All rights reserved. 4 + * Intel Management Engine Interface (Intel MEI) Linux driver 5 + * for CSC platforms. 6 + */ 7 + 8 + #include <linux/cleanup.h> 9 + #include <linux/device.h> 10 + #include <linux/dma-mapping.h> 11 + #include <linux/err.h> 12 + #include <linux/errno.h> 13 + #include <linux/interrupt.h> 14 + #include <linux/module.h> 15 + #include <linux/mutex.h> 16 + #include <linux/pci.h> 17 + #include <linux/pm_runtime.h> 18 + #include <linux/sched.h> 19 + #include <linux/types.h> 20 + 21 + #include "client.h" 22 + #include "hw-me-regs.h" 23 + #include "hw-me.h" 24 + #include "mei_dev.h" 25 + #include "mei-trace.h" 26 + 27 + #define MEI_CSC_HECI2_OFFSET 0x1000 28 + 29 + static int mei_csc_read_fws(const struct mei_device *mdev, int where, const char *name, u32 *val) 30 + { 31 + struct mei_me_hw *hw = to_me_hw(mdev); 32 + 33 + *val = ioread32(hw->mem_addr + where + 0xC00); 34 + trace_mei_reg_read(&mdev->dev, name, where, *val); 35 + return 0; 36 + } 37 + 38 + static int mei_csc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 39 + { 40 + struct device *dev = &pdev->dev; 41 + const struct mei_cfg *cfg; 42 + char __iomem *registers; 43 + struct mei_device *mdev; 44 + struct mei_me_hw *hw; 45 + int err; 46 + 47 + cfg = mei_me_get_cfg(ent->driver_data); 48 + if (!cfg) 49 + return -ENODEV; 50 + 51 + err = pcim_enable_device(pdev); 52 + if (err) 53 + return dev_err_probe(dev, err, "Failed to enable PCI device.\n"); 54 + 55 + pci_set_master(pdev); 56 + 57 + registers = pcim_iomap_region(pdev, 0, KBUILD_MODNAME); 58 + if (IS_ERR(registers)) 59 + return dev_err_probe(dev, PTR_ERR(registers), "Failed to get PCI region.\n"); 60 + 61 + err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); 62 + if (err) 63 + return dev_err_probe(dev, err, "No usable DMA configuration.\n"); 64 + 65 + /* allocates and initializes the mei dev structure */ 66 + mdev = mei_me_dev_init(dev, cfg, false); 67 + if (!mdev) 68 + return -ENOMEM; 69 + 70 + hw = to_me_hw(mdev); 71 + 72 + /* 73 + * Both HECI1 and HECI2 are on this device, but only HECI2 is supported. 74 + */ 75 + hw->mem_addr = registers + MEI_CSC_HECI2_OFFSET; 76 + hw->read_fws = mei_csc_read_fws; 77 + 78 + /* 79 + * mei_register() assumes ownership of mdev. 80 + * No need to release it explicitly in error path. 81 + */ 82 + err = mei_register(mdev, dev); 83 + if (err) 84 + return err; 85 + 86 + pci_set_drvdata(pdev, mdev); 87 + 88 + err = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_INTX | PCI_IRQ_MSI); 89 + if (err < 0) { 90 + dev_err_probe(dev, err, "Failed to allocate IRQ.\n"); 91 + goto err_mei_unreg; 92 + } 93 + 94 + hw->irq = pci_irq_vector(pdev, 0); 95 + 96 + /* request and enable interrupt */ 97 + err = request_threaded_irq(hw->irq, 98 + mei_me_irq_quick_handler, mei_me_irq_thread_handler, 99 + IRQF_SHARED | IRQF_ONESHOT, KBUILD_MODNAME, mdev); 100 + if (err) 101 + goto err_free_irq_vectors; 102 + 103 + /* 104 + * Continue to char device setup in spite of firmware handshake failure. 105 + * In order to provide access to the firmware status registers to the user 106 + * space via sysfs. The firmware status registers required to understand 107 + * firmware error state and possible recovery flow. 108 + */ 109 + if (mei_start(mdev)) 110 + dev_warn(dev, "Failed to initialize HECI hardware.\n"); 111 + 112 + pm_runtime_set_autosuspend_delay(dev, MEI_ME_RPM_TIMEOUT); 113 + pm_runtime_use_autosuspend(dev); 114 + 115 + /* 116 + * MEI requires to resume from runtime suspend mode 117 + * in order to perform link reset flow upon system suspend. 118 + */ 119 + dev_pm_set_driver_flags(dev, DPM_FLAG_NO_DIRECT_COMPLETE); 120 + 121 + pm_runtime_allow(dev); 122 + pm_runtime_put_noidle(dev); 123 + 124 + return 0; 125 + 126 + err_free_irq_vectors: 127 + pci_free_irq_vectors(pdev); 128 + err_mei_unreg: 129 + mei_deregister(mdev); 130 + return err; 131 + } 132 + 133 + static void mei_csc_shutdown(struct pci_dev *pdev) 134 + { 135 + struct mei_device *mdev = pci_get_drvdata(pdev); 136 + struct mei_me_hw *hw = to_me_hw(mdev); 137 + 138 + pm_runtime_get_noresume(&pdev->dev); 139 + 140 + mei_stop(mdev); 141 + 142 + mei_disable_interrupts(mdev); 143 + free_irq(hw->irq, mdev); 144 + pci_free_irq_vectors(pdev); 145 + } 146 + 147 + static void mei_csc_remove(struct pci_dev *pdev) 148 + { 149 + struct mei_device *mdev = pci_get_drvdata(pdev); 150 + 151 + mei_csc_shutdown(pdev); 152 + 153 + mei_deregister(mdev); 154 + } 155 + 156 + static int mei_csc_pci_prepare(struct device *dev) 157 + { 158 + pm_runtime_resume(dev); 159 + return 0; 160 + } 161 + 162 + static int mei_csc_pci_suspend(struct device *dev) 163 + { 164 + struct mei_device *mdev = dev_get_drvdata(dev); 165 + 166 + mei_stop(mdev); 167 + 168 + mei_disable_interrupts(mdev); 169 + 170 + return 0; 171 + } 172 + 173 + static int mei_csc_pci_resume(struct device *dev) 174 + { 175 + struct mei_device *mdev = dev_get_drvdata(dev); 176 + int err; 177 + 178 + err = mei_restart(mdev); 179 + if (err) 180 + return err; 181 + 182 + /* Start timer if stopped in suspend */ 183 + schedule_delayed_work(&mdev->timer_work, HZ); 184 + 185 + return 0; 186 + } 187 + 188 + static void mei_csc_pci_complete(struct device *dev) 189 + { 190 + pm_runtime_suspend(dev); 191 + } 192 + 193 + static int mei_csc_pm_runtime_idle(struct device *dev) 194 + { 195 + struct mei_device *mdev = dev_get_drvdata(dev); 196 + 197 + return mei_write_is_idle(mdev) ? 0 : -EBUSY; 198 + } 199 + 200 + static int mei_csc_pm_runtime_suspend(struct device *dev) 201 + { 202 + struct mei_device *mdev = dev_get_drvdata(dev); 203 + struct mei_me_hw *hw = to_me_hw(mdev); 204 + 205 + guard(mutex)(&mdev->device_lock); 206 + 207 + if (!mei_write_is_idle(mdev)) 208 + return -EAGAIN; 209 + 210 + hw->pg_state = MEI_PG_ON; 211 + return 0; 212 + } 213 + 214 + static int mei_csc_pm_runtime_resume(struct device *dev) 215 + { 216 + struct mei_device *mdev = dev_get_drvdata(dev); 217 + struct mei_me_hw *hw = to_me_hw(mdev); 218 + irqreturn_t irq_ret; 219 + 220 + scoped_guard(mutex, &mdev->device_lock) 221 + hw->pg_state = MEI_PG_OFF; 222 + 223 + /* Process all queues that wait for resume */ 224 + irq_ret = mei_me_irq_thread_handler(1, mdev); 225 + if (irq_ret != IRQ_HANDLED) 226 + dev_err(dev, "thread handler fail %d\n", irq_ret); 227 + 228 + return 0; 229 + } 230 + 231 + static const struct dev_pm_ops mei_csc_pm_ops = { 232 + .prepare = pm_sleep_ptr(mei_csc_pci_prepare), 233 + .complete = pm_sleep_ptr(mei_csc_pci_complete), 234 + SYSTEM_SLEEP_PM_OPS(mei_csc_pci_suspend, mei_csc_pci_resume) 235 + RUNTIME_PM_OPS(mei_csc_pm_runtime_suspend, 236 + mei_csc_pm_runtime_resume, mei_csc_pm_runtime_idle) 237 + }; 238 + 239 + static const struct pci_device_id mei_csc_pci_tbl[] = { 240 + { PCI_DEVICE_DATA(INTEL, MEI_CRI, MEI_ME_CSC_CFG) }, 241 + {} 242 + }; 243 + MODULE_DEVICE_TABLE(pci, mei_csc_pci_tbl); 244 + 245 + static struct pci_driver mei_csc_driver = { 246 + .name = KBUILD_MODNAME, 247 + .id_table = mei_csc_pci_tbl, 248 + .probe = mei_csc_probe, 249 + .remove = mei_csc_remove, 250 + .shutdown = mei_csc_shutdown, 251 + .driver = { 252 + .pm = &mei_csc_pm_ops, 253 + .probe_type = PROBE_PREFER_ASYNCHRONOUS, 254 + } 255 + }; 256 + module_pci_driver(mei_csc_driver); 257 + 258 + MODULE_DESCRIPTION("Intel(R) Management Engine Interface for discrete graphics (CSC)"); 259 + MODULE_LICENSE("GPL");