File Index
Files and directories
Sign in
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 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.