xref: /linux/drivers/misc/mei/pci-csc.c (revision cb4eb6771c0f8fd1c52a8f6fdec7762fb087380a)
172fdf0bbSAlexander Usyskin // SPDX-License-Identifier: GPL-2.0
272fdf0bbSAlexander Usyskin /*
372fdf0bbSAlexander Usyskin  * Copyright (C) 2025-2026, Intel Corporation. All rights reserved.
472fdf0bbSAlexander Usyskin  * Intel Management Engine Interface (Intel MEI) Linux driver
572fdf0bbSAlexander Usyskin  * for CSC platforms.
672fdf0bbSAlexander Usyskin  */
772fdf0bbSAlexander Usyskin 
872fdf0bbSAlexander Usyskin #include <linux/cleanup.h>
972fdf0bbSAlexander Usyskin #include <linux/device.h>
1072fdf0bbSAlexander Usyskin #include <linux/dma-mapping.h>
1172fdf0bbSAlexander Usyskin #include <linux/err.h>
1272fdf0bbSAlexander Usyskin #include <linux/errno.h>
1372fdf0bbSAlexander Usyskin #include <linux/interrupt.h>
1472fdf0bbSAlexander Usyskin #include <linux/module.h>
1572fdf0bbSAlexander Usyskin #include <linux/mutex.h>
1672fdf0bbSAlexander Usyskin #include <linux/pci.h>
1772fdf0bbSAlexander Usyskin #include <linux/pm_runtime.h>
1872fdf0bbSAlexander Usyskin #include <linux/sched.h>
1972fdf0bbSAlexander Usyskin #include <linux/types.h>
2072fdf0bbSAlexander Usyskin 
2172fdf0bbSAlexander Usyskin #include "client.h"
2272fdf0bbSAlexander Usyskin #include "hw-me-regs.h"
2372fdf0bbSAlexander Usyskin #include "hw-me.h"
2472fdf0bbSAlexander Usyskin #include "mei_dev.h"
2572fdf0bbSAlexander Usyskin #include "mei-trace.h"
2672fdf0bbSAlexander Usyskin 
2772fdf0bbSAlexander Usyskin #define MEI_CSC_HECI2_OFFSET 0x1000
2872fdf0bbSAlexander Usyskin 
mei_csc_read_fws(const struct mei_device * mdev,int where,const char * name,u32 * val)2972fdf0bbSAlexander Usyskin static int mei_csc_read_fws(const struct mei_device *mdev, int where, const char *name, u32 *val)
3072fdf0bbSAlexander Usyskin {
3172fdf0bbSAlexander Usyskin 	struct mei_me_hw *hw = to_me_hw(mdev);
3272fdf0bbSAlexander Usyskin 
3372fdf0bbSAlexander Usyskin 	*val = ioread32(hw->mem_addr + where + 0xC00);
3472fdf0bbSAlexander Usyskin 	trace_mei_reg_read(&mdev->dev, name, where, *val);
3572fdf0bbSAlexander Usyskin 	return 0;
3672fdf0bbSAlexander Usyskin }
3772fdf0bbSAlexander Usyskin 
mei_csc_probe(struct pci_dev * pdev,const struct pci_device_id * ent)3872fdf0bbSAlexander Usyskin static int mei_csc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
3972fdf0bbSAlexander Usyskin {
4072fdf0bbSAlexander Usyskin 	struct device *dev = &pdev->dev;
4172fdf0bbSAlexander Usyskin 	const struct mei_cfg *cfg;
4272fdf0bbSAlexander Usyskin 	char __iomem *registers;
4372fdf0bbSAlexander Usyskin 	struct mei_device *mdev;
4472fdf0bbSAlexander Usyskin 	struct mei_me_hw *hw;
4572fdf0bbSAlexander Usyskin 	int err;
4672fdf0bbSAlexander Usyskin 
4772fdf0bbSAlexander Usyskin 	cfg = mei_me_get_cfg(ent->driver_data);
4872fdf0bbSAlexander Usyskin 	if (!cfg)
4972fdf0bbSAlexander Usyskin 		return -ENODEV;
5072fdf0bbSAlexander Usyskin 
5172fdf0bbSAlexander Usyskin 	err = pcim_enable_device(pdev);
5272fdf0bbSAlexander Usyskin 	if (err)
5372fdf0bbSAlexander Usyskin 		return dev_err_probe(dev, err, "Failed to enable PCI device.\n");
5472fdf0bbSAlexander Usyskin 
5572fdf0bbSAlexander Usyskin 	pci_set_master(pdev);
5672fdf0bbSAlexander Usyskin 
5772fdf0bbSAlexander Usyskin 	registers = pcim_iomap_region(pdev, 0, KBUILD_MODNAME);
5872fdf0bbSAlexander Usyskin 	if (IS_ERR(registers))
5972fdf0bbSAlexander Usyskin 		return dev_err_probe(dev, PTR_ERR(registers), "Failed to get PCI region.\n");
6072fdf0bbSAlexander Usyskin 
6172fdf0bbSAlexander Usyskin 	err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
6272fdf0bbSAlexander Usyskin 	if (err)
6372fdf0bbSAlexander Usyskin 		return dev_err_probe(dev, err, "No usable DMA configuration.\n");
6472fdf0bbSAlexander Usyskin 
6572fdf0bbSAlexander Usyskin 	/* allocates and initializes the mei dev structure */
6672fdf0bbSAlexander Usyskin 	mdev = mei_me_dev_init(dev, cfg, false);
6772fdf0bbSAlexander Usyskin 	if (!mdev)
6872fdf0bbSAlexander Usyskin 		return -ENOMEM;
6972fdf0bbSAlexander Usyskin 
70*0a18c3bcSAlexander Usyskin 	mdev->read_fws_need_resume = true;
71*0a18c3bcSAlexander Usyskin 
7272fdf0bbSAlexander Usyskin 	hw = to_me_hw(mdev);
7372fdf0bbSAlexander Usyskin 
7472fdf0bbSAlexander Usyskin 	/*
7572fdf0bbSAlexander Usyskin 	 * Both HECI1 and HECI2 are on this device, but only HECI2 is supported.
7672fdf0bbSAlexander Usyskin 	 */
7772fdf0bbSAlexander Usyskin 	hw->mem_addr = registers + MEI_CSC_HECI2_OFFSET;
7872fdf0bbSAlexander Usyskin 	hw->read_fws = mei_csc_read_fws;
7972fdf0bbSAlexander Usyskin 
8072fdf0bbSAlexander Usyskin 	/*
8172fdf0bbSAlexander Usyskin 	 * mei_register() assumes ownership of mdev.
8272fdf0bbSAlexander Usyskin 	 * No need to release it explicitly in error path.
8372fdf0bbSAlexander Usyskin 	 */
8472fdf0bbSAlexander Usyskin 	err = mei_register(mdev, dev);
8572fdf0bbSAlexander Usyskin 	if (err)
8672fdf0bbSAlexander Usyskin 		return err;
8772fdf0bbSAlexander Usyskin 
8872fdf0bbSAlexander Usyskin 	pci_set_drvdata(pdev, mdev);
8972fdf0bbSAlexander Usyskin 
9072fdf0bbSAlexander Usyskin 	err = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_INTX | PCI_IRQ_MSI);
9172fdf0bbSAlexander Usyskin 	if (err < 0) {
9272fdf0bbSAlexander Usyskin 		dev_err_probe(dev, err, "Failed to allocate IRQ.\n");
9372fdf0bbSAlexander Usyskin 		goto err_mei_unreg;
9472fdf0bbSAlexander Usyskin 	}
9572fdf0bbSAlexander Usyskin 
9672fdf0bbSAlexander Usyskin 	hw->irq = pci_irq_vector(pdev, 0);
9772fdf0bbSAlexander Usyskin 
9872fdf0bbSAlexander Usyskin 	/* request and enable interrupt */
9972fdf0bbSAlexander Usyskin 	err = request_threaded_irq(hw->irq,
10072fdf0bbSAlexander Usyskin 				   mei_me_irq_quick_handler, mei_me_irq_thread_handler,
10172fdf0bbSAlexander Usyskin 				   IRQF_SHARED | IRQF_ONESHOT, KBUILD_MODNAME, mdev);
10272fdf0bbSAlexander Usyskin 	if (err)
10372fdf0bbSAlexander Usyskin 		goto err_free_irq_vectors;
10472fdf0bbSAlexander Usyskin 
10572fdf0bbSAlexander Usyskin 	/*
10672fdf0bbSAlexander Usyskin 	 * Continue to char device setup in spite of firmware handshake failure.
10772fdf0bbSAlexander Usyskin 	 * In order to provide access to the firmware status registers to the user
10872fdf0bbSAlexander Usyskin 	 * space via sysfs. The firmware status registers required to understand
10972fdf0bbSAlexander Usyskin 	 * firmware error state and possible recovery flow.
11072fdf0bbSAlexander Usyskin 	 */
11172fdf0bbSAlexander Usyskin 	if (mei_start(mdev))
11272fdf0bbSAlexander Usyskin 		dev_warn(dev, "Failed to initialize HECI hardware.\n");
11372fdf0bbSAlexander Usyskin 
11472fdf0bbSAlexander Usyskin 	pm_runtime_set_autosuspend_delay(dev, MEI_ME_RPM_TIMEOUT);
11572fdf0bbSAlexander Usyskin 	pm_runtime_use_autosuspend(dev);
11672fdf0bbSAlexander Usyskin 
11772fdf0bbSAlexander Usyskin 	/*
11872fdf0bbSAlexander Usyskin 	 * MEI requires to resume from runtime suspend mode
11972fdf0bbSAlexander Usyskin 	 * in order to perform link reset flow upon system suspend.
12072fdf0bbSAlexander Usyskin 	 */
12172fdf0bbSAlexander Usyskin 	dev_pm_set_driver_flags(dev, DPM_FLAG_NO_DIRECT_COMPLETE);
12272fdf0bbSAlexander Usyskin 
12372fdf0bbSAlexander Usyskin 	pm_runtime_allow(dev);
12472fdf0bbSAlexander Usyskin 	pm_runtime_put_noidle(dev);
12572fdf0bbSAlexander Usyskin 
12672fdf0bbSAlexander Usyskin 	return 0;
12772fdf0bbSAlexander Usyskin 
12872fdf0bbSAlexander Usyskin err_free_irq_vectors:
12972fdf0bbSAlexander Usyskin 	pci_free_irq_vectors(pdev);
13072fdf0bbSAlexander Usyskin err_mei_unreg:
13172fdf0bbSAlexander Usyskin 	mei_deregister(mdev);
13272fdf0bbSAlexander Usyskin 	return err;
13372fdf0bbSAlexander Usyskin }
13472fdf0bbSAlexander Usyskin 
mei_csc_shutdown(struct pci_dev * pdev)13572fdf0bbSAlexander Usyskin static void mei_csc_shutdown(struct pci_dev *pdev)
13672fdf0bbSAlexander Usyskin {
13772fdf0bbSAlexander Usyskin 	struct mei_device *mdev = pci_get_drvdata(pdev);
13872fdf0bbSAlexander Usyskin 	struct mei_me_hw *hw = to_me_hw(mdev);
13972fdf0bbSAlexander Usyskin 
14072fdf0bbSAlexander Usyskin 	pm_runtime_get_noresume(&pdev->dev);
14172fdf0bbSAlexander Usyskin 
14272fdf0bbSAlexander Usyskin 	mei_stop(mdev);
14372fdf0bbSAlexander Usyskin 
14472fdf0bbSAlexander Usyskin 	mei_disable_interrupts(mdev);
14572fdf0bbSAlexander Usyskin 	free_irq(hw->irq, mdev);
14672fdf0bbSAlexander Usyskin 	pci_free_irq_vectors(pdev);
14772fdf0bbSAlexander Usyskin }
14872fdf0bbSAlexander Usyskin 
mei_csc_remove(struct pci_dev * pdev)14972fdf0bbSAlexander Usyskin static void mei_csc_remove(struct pci_dev *pdev)
15072fdf0bbSAlexander Usyskin {
15172fdf0bbSAlexander Usyskin 	struct mei_device *mdev = pci_get_drvdata(pdev);
15272fdf0bbSAlexander Usyskin 
15372fdf0bbSAlexander Usyskin 	mei_csc_shutdown(pdev);
15472fdf0bbSAlexander Usyskin 
15572fdf0bbSAlexander Usyskin 	mei_deregister(mdev);
15672fdf0bbSAlexander Usyskin }
15772fdf0bbSAlexander Usyskin 
mei_csc_pci_prepare(struct device * dev)15872fdf0bbSAlexander Usyskin static int mei_csc_pci_prepare(struct device *dev)
15972fdf0bbSAlexander Usyskin {
16072fdf0bbSAlexander Usyskin 	pm_runtime_resume(dev);
16172fdf0bbSAlexander Usyskin 	return 0;
16272fdf0bbSAlexander Usyskin }
16372fdf0bbSAlexander Usyskin 
mei_csc_pci_suspend(struct device * dev)16472fdf0bbSAlexander Usyskin static int mei_csc_pci_suspend(struct device *dev)
16572fdf0bbSAlexander Usyskin {
16672fdf0bbSAlexander Usyskin 	struct mei_device *mdev = dev_get_drvdata(dev);
16772fdf0bbSAlexander Usyskin 
16872fdf0bbSAlexander Usyskin 	mei_stop(mdev);
16972fdf0bbSAlexander Usyskin 
17072fdf0bbSAlexander Usyskin 	mei_disable_interrupts(mdev);
17172fdf0bbSAlexander Usyskin 
17272fdf0bbSAlexander Usyskin 	return 0;
17372fdf0bbSAlexander Usyskin }
17472fdf0bbSAlexander Usyskin 
mei_csc_pci_resume(struct device * dev)17572fdf0bbSAlexander Usyskin static int mei_csc_pci_resume(struct device *dev)
17672fdf0bbSAlexander Usyskin {
17772fdf0bbSAlexander Usyskin 	struct mei_device *mdev = dev_get_drvdata(dev);
17872fdf0bbSAlexander Usyskin 	int err;
17972fdf0bbSAlexander Usyskin 
18072fdf0bbSAlexander Usyskin 	err = mei_restart(mdev);
18172fdf0bbSAlexander Usyskin 	if (err)
18272fdf0bbSAlexander Usyskin 		return err;
18372fdf0bbSAlexander Usyskin 
18472fdf0bbSAlexander Usyskin 	/* Start timer if stopped in suspend */
18572fdf0bbSAlexander Usyskin 	schedule_delayed_work(&mdev->timer_work, HZ);
18672fdf0bbSAlexander Usyskin 
18772fdf0bbSAlexander Usyskin 	return 0;
18872fdf0bbSAlexander Usyskin }
18972fdf0bbSAlexander Usyskin 
mei_csc_pci_complete(struct device * dev)19072fdf0bbSAlexander Usyskin static void mei_csc_pci_complete(struct device *dev)
19172fdf0bbSAlexander Usyskin {
19272fdf0bbSAlexander Usyskin 	pm_runtime_suspend(dev);
19372fdf0bbSAlexander Usyskin }
19472fdf0bbSAlexander Usyskin 
mei_csc_pm_runtime_idle(struct device * dev)19572fdf0bbSAlexander Usyskin static int mei_csc_pm_runtime_idle(struct device *dev)
19672fdf0bbSAlexander Usyskin {
19772fdf0bbSAlexander Usyskin 	struct mei_device *mdev = dev_get_drvdata(dev);
19872fdf0bbSAlexander Usyskin 
19972fdf0bbSAlexander Usyskin 	return mei_write_is_idle(mdev) ? 0 : -EBUSY;
20072fdf0bbSAlexander Usyskin }
20172fdf0bbSAlexander Usyskin 
mei_csc_pm_runtime_suspend(struct device * dev)20272fdf0bbSAlexander Usyskin static int mei_csc_pm_runtime_suspend(struct device *dev)
20372fdf0bbSAlexander Usyskin {
20472fdf0bbSAlexander Usyskin 	struct mei_device *mdev = dev_get_drvdata(dev);
20572fdf0bbSAlexander Usyskin 	struct mei_me_hw *hw = to_me_hw(mdev);
20672fdf0bbSAlexander Usyskin 
20772fdf0bbSAlexander Usyskin 	guard(mutex)(&mdev->device_lock);
20872fdf0bbSAlexander Usyskin 
20972fdf0bbSAlexander Usyskin 	if (!mei_write_is_idle(mdev))
21072fdf0bbSAlexander Usyskin 		return -EAGAIN;
21172fdf0bbSAlexander Usyskin 
21272fdf0bbSAlexander Usyskin 	hw->pg_state = MEI_PG_ON;
21372fdf0bbSAlexander Usyskin 	return 0;
21472fdf0bbSAlexander Usyskin }
21572fdf0bbSAlexander Usyskin 
mei_csc_pm_runtime_resume(struct device * dev)21672fdf0bbSAlexander Usyskin static int mei_csc_pm_runtime_resume(struct device *dev)
21772fdf0bbSAlexander Usyskin {
21872fdf0bbSAlexander Usyskin 	struct mei_device *mdev = dev_get_drvdata(dev);
21972fdf0bbSAlexander Usyskin 	struct mei_me_hw *hw = to_me_hw(mdev);
22072fdf0bbSAlexander Usyskin 	irqreturn_t irq_ret;
22172fdf0bbSAlexander Usyskin 
22272fdf0bbSAlexander Usyskin 	scoped_guard(mutex, &mdev->device_lock)
22372fdf0bbSAlexander Usyskin 		hw->pg_state = MEI_PG_OFF;
22472fdf0bbSAlexander Usyskin 
22572fdf0bbSAlexander Usyskin 	/* Process all queues that wait for resume */
22672fdf0bbSAlexander Usyskin 	irq_ret = mei_me_irq_thread_handler(1, mdev);
22772fdf0bbSAlexander Usyskin 	if (irq_ret != IRQ_HANDLED)
22872fdf0bbSAlexander Usyskin 		dev_err(dev, "thread handler fail %d\n", irq_ret);
22972fdf0bbSAlexander Usyskin 
23072fdf0bbSAlexander Usyskin 	return 0;
23172fdf0bbSAlexander Usyskin }
23272fdf0bbSAlexander Usyskin 
23372fdf0bbSAlexander Usyskin static const struct dev_pm_ops mei_csc_pm_ops = {
23472fdf0bbSAlexander Usyskin 	.prepare = pm_sleep_ptr(mei_csc_pci_prepare),
23572fdf0bbSAlexander Usyskin 	.complete = pm_sleep_ptr(mei_csc_pci_complete),
23672fdf0bbSAlexander Usyskin 	SYSTEM_SLEEP_PM_OPS(mei_csc_pci_suspend, mei_csc_pci_resume)
23772fdf0bbSAlexander Usyskin 	RUNTIME_PM_OPS(mei_csc_pm_runtime_suspend,
23872fdf0bbSAlexander Usyskin 		       mei_csc_pm_runtime_resume, mei_csc_pm_runtime_idle)
23972fdf0bbSAlexander Usyskin };
24072fdf0bbSAlexander Usyskin 
24172fdf0bbSAlexander Usyskin static const struct pci_device_id mei_csc_pci_tbl[] = {
24272fdf0bbSAlexander Usyskin 	{ PCI_DEVICE_DATA(INTEL, MEI_CRI, MEI_ME_CSC_CFG) },
24372fdf0bbSAlexander Usyskin 	{}
24472fdf0bbSAlexander Usyskin };
24572fdf0bbSAlexander Usyskin MODULE_DEVICE_TABLE(pci, mei_csc_pci_tbl);
24672fdf0bbSAlexander Usyskin 
24772fdf0bbSAlexander Usyskin static struct pci_driver mei_csc_driver = {
24872fdf0bbSAlexander Usyskin 	.name = KBUILD_MODNAME,
24972fdf0bbSAlexander Usyskin 	.id_table = mei_csc_pci_tbl,
25072fdf0bbSAlexander Usyskin 	.probe = mei_csc_probe,
25172fdf0bbSAlexander Usyskin 	.remove = mei_csc_remove,
25272fdf0bbSAlexander Usyskin 	.shutdown = mei_csc_shutdown,
25372fdf0bbSAlexander Usyskin 	.driver = {
25472fdf0bbSAlexander Usyskin 		.pm = &mei_csc_pm_ops,
25572fdf0bbSAlexander Usyskin 		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
25672fdf0bbSAlexander Usyskin 	}
25772fdf0bbSAlexander Usyskin };
25872fdf0bbSAlexander Usyskin module_pci_driver(mei_csc_driver);
25972fdf0bbSAlexander Usyskin 
26072fdf0bbSAlexander Usyskin MODULE_DESCRIPTION("Intel(R) Management Engine Interface for discrete graphics (CSC)");
26172fdf0bbSAlexander Usyskin MODULE_LICENSE("GPL");
262