1*30bb1ce7SJarkko Nikula // SPDX-License-Identifier: GPL-2.0 2*30bb1ce7SJarkko Nikula /* 3*30bb1ce7SJarkko Nikula * PCI glue code for MIPI I3C HCI driver 4*30bb1ce7SJarkko Nikula * 5*30bb1ce7SJarkko Nikula * Copyright (C) 2024 Intel Corporation 6*30bb1ce7SJarkko Nikula * 7*30bb1ce7SJarkko Nikula * Author: Jarkko Nikula <jarkko.nikula@linux.intel.com> 8*30bb1ce7SJarkko Nikula */ 9*30bb1ce7SJarkko Nikula #include <linux/acpi.h> 10*30bb1ce7SJarkko Nikula #include <linux/idr.h> 11*30bb1ce7SJarkko Nikula #include <linux/kernel.h> 12*30bb1ce7SJarkko Nikula #include <linux/module.h> 13*30bb1ce7SJarkko Nikula #include <linux/pci.h> 14*30bb1ce7SJarkko Nikula #include <linux/platform_device.h> 15*30bb1ce7SJarkko Nikula 16*30bb1ce7SJarkko Nikula struct mipi_i3c_hci_pci_info { 17*30bb1ce7SJarkko Nikula int (*init)(struct pci_dev *pci); 18*30bb1ce7SJarkko Nikula }; 19*30bb1ce7SJarkko Nikula 20*30bb1ce7SJarkko Nikula #define INTEL_PRIV_OFFSET 0x2b0 21*30bb1ce7SJarkko Nikula #define INTEL_PRIV_SIZE 0x28 22*30bb1ce7SJarkko Nikula #define INTEL_PRIV_RESETS 0x04 23*30bb1ce7SJarkko Nikula #define INTEL_PRIV_RESETS_RESET BIT(0) 24*30bb1ce7SJarkko Nikula #define INTEL_PRIV_RESETS_RESET_DONE BIT(1) 25*30bb1ce7SJarkko Nikula 26*30bb1ce7SJarkko Nikula static DEFINE_IDA(mipi_i3c_hci_pci_ida); 27*30bb1ce7SJarkko Nikula 28*30bb1ce7SJarkko Nikula static int mipi_i3c_hci_pci_intel_init(struct pci_dev *pci) 29*30bb1ce7SJarkko Nikula { 30*30bb1ce7SJarkko Nikula unsigned long timeout; 31*30bb1ce7SJarkko Nikula void __iomem *priv; 32*30bb1ce7SJarkko Nikula 33*30bb1ce7SJarkko Nikula priv = devm_ioremap(&pci->dev, 34*30bb1ce7SJarkko Nikula pci_resource_start(pci, 0) + INTEL_PRIV_OFFSET, 35*30bb1ce7SJarkko Nikula INTEL_PRIV_SIZE); 36*30bb1ce7SJarkko Nikula if (!priv) 37*30bb1ce7SJarkko Nikula return -ENOMEM; 38*30bb1ce7SJarkko Nikula 39*30bb1ce7SJarkko Nikula /* Assert reset, wait for completion and release reset */ 40*30bb1ce7SJarkko Nikula writel(0, priv + INTEL_PRIV_RESETS); 41*30bb1ce7SJarkko Nikula timeout = jiffies + msecs_to_jiffies(10); 42*30bb1ce7SJarkko Nikula while (!(readl(priv + INTEL_PRIV_RESETS) & 43*30bb1ce7SJarkko Nikula INTEL_PRIV_RESETS_RESET_DONE)) { 44*30bb1ce7SJarkko Nikula if (time_after(jiffies, timeout)) 45*30bb1ce7SJarkko Nikula break; 46*30bb1ce7SJarkko Nikula cpu_relax(); 47*30bb1ce7SJarkko Nikula } 48*30bb1ce7SJarkko Nikula writel(INTEL_PRIV_RESETS_RESET, priv + INTEL_PRIV_RESETS); 49*30bb1ce7SJarkko Nikula 50*30bb1ce7SJarkko Nikula return 0; 51*30bb1ce7SJarkko Nikula } 52*30bb1ce7SJarkko Nikula 53*30bb1ce7SJarkko Nikula static struct mipi_i3c_hci_pci_info intel_info = { 54*30bb1ce7SJarkko Nikula .init = mipi_i3c_hci_pci_intel_init, 55*30bb1ce7SJarkko Nikula }; 56*30bb1ce7SJarkko Nikula 57*30bb1ce7SJarkko Nikula static int mipi_i3c_hci_pci_probe(struct pci_dev *pci, 58*30bb1ce7SJarkko Nikula const struct pci_device_id *id) 59*30bb1ce7SJarkko Nikula { 60*30bb1ce7SJarkko Nikula struct mipi_i3c_hci_pci_info *info; 61*30bb1ce7SJarkko Nikula struct platform_device *pdev; 62*30bb1ce7SJarkko Nikula struct resource res[2]; 63*30bb1ce7SJarkko Nikula int dev_id, ret; 64*30bb1ce7SJarkko Nikula 65*30bb1ce7SJarkko Nikula ret = pcim_enable_device(pci); 66*30bb1ce7SJarkko Nikula if (ret) 67*30bb1ce7SJarkko Nikula return ret; 68*30bb1ce7SJarkko Nikula 69*30bb1ce7SJarkko Nikula pci_set_master(pci); 70*30bb1ce7SJarkko Nikula 71*30bb1ce7SJarkko Nikula memset(&res, 0, sizeof(res)); 72*30bb1ce7SJarkko Nikula 73*30bb1ce7SJarkko Nikula res[0].flags = IORESOURCE_MEM; 74*30bb1ce7SJarkko Nikula res[0].start = pci_resource_start(pci, 0); 75*30bb1ce7SJarkko Nikula res[0].end = pci_resource_end(pci, 0); 76*30bb1ce7SJarkko Nikula 77*30bb1ce7SJarkko Nikula res[1].flags = IORESOURCE_IRQ; 78*30bb1ce7SJarkko Nikula res[1].start = pci->irq; 79*30bb1ce7SJarkko Nikula res[1].end = pci->irq; 80*30bb1ce7SJarkko Nikula 81*30bb1ce7SJarkko Nikula dev_id = ida_alloc(&mipi_i3c_hci_pci_ida, GFP_KERNEL); 82*30bb1ce7SJarkko Nikula if (dev_id < 0) 83*30bb1ce7SJarkko Nikula return dev_id; 84*30bb1ce7SJarkko Nikula 85*30bb1ce7SJarkko Nikula pdev = platform_device_alloc("mipi-i3c-hci", dev_id); 86*30bb1ce7SJarkko Nikula if (!pdev) 87*30bb1ce7SJarkko Nikula return -ENOMEM; 88*30bb1ce7SJarkko Nikula 89*30bb1ce7SJarkko Nikula pdev->dev.parent = &pci->dev; 90*30bb1ce7SJarkko Nikula device_set_node(&pdev->dev, dev_fwnode(&pci->dev)); 91*30bb1ce7SJarkko Nikula 92*30bb1ce7SJarkko Nikula ret = platform_device_add_resources(pdev, res, ARRAY_SIZE(res)); 93*30bb1ce7SJarkko Nikula if (ret) 94*30bb1ce7SJarkko Nikula goto err; 95*30bb1ce7SJarkko Nikula 96*30bb1ce7SJarkko Nikula info = (struct mipi_i3c_hci_pci_info *)id->driver_data; 97*30bb1ce7SJarkko Nikula if (info && info->init) { 98*30bb1ce7SJarkko Nikula ret = info->init(pci); 99*30bb1ce7SJarkko Nikula if (ret) 100*30bb1ce7SJarkko Nikula goto err; 101*30bb1ce7SJarkko Nikula } 102*30bb1ce7SJarkko Nikula 103*30bb1ce7SJarkko Nikula ret = platform_device_add(pdev); 104*30bb1ce7SJarkko Nikula if (ret) 105*30bb1ce7SJarkko Nikula goto err; 106*30bb1ce7SJarkko Nikula 107*30bb1ce7SJarkko Nikula pci_set_drvdata(pci, pdev); 108*30bb1ce7SJarkko Nikula 109*30bb1ce7SJarkko Nikula return 0; 110*30bb1ce7SJarkko Nikula 111*30bb1ce7SJarkko Nikula err: 112*30bb1ce7SJarkko Nikula platform_device_put(pdev); 113*30bb1ce7SJarkko Nikula ida_free(&mipi_i3c_hci_pci_ida, dev_id); 114*30bb1ce7SJarkko Nikula return ret; 115*30bb1ce7SJarkko Nikula } 116*30bb1ce7SJarkko Nikula 117*30bb1ce7SJarkko Nikula static void mipi_i3c_hci_pci_remove(struct pci_dev *pci) 118*30bb1ce7SJarkko Nikula { 119*30bb1ce7SJarkko Nikula struct platform_device *pdev = pci_get_drvdata(pci); 120*30bb1ce7SJarkko Nikula int dev_id = pdev->id; 121*30bb1ce7SJarkko Nikula 122*30bb1ce7SJarkko Nikula platform_device_unregister(pdev); 123*30bb1ce7SJarkko Nikula ida_free(&mipi_i3c_hci_pci_ida, dev_id); 124*30bb1ce7SJarkko Nikula } 125*30bb1ce7SJarkko Nikula 126*30bb1ce7SJarkko Nikula static const struct pci_device_id mipi_i3c_hci_pci_devices[] = { 127*30bb1ce7SJarkko Nikula /* Panther Lake-H */ 128*30bb1ce7SJarkko Nikula { PCI_VDEVICE(INTEL, 0xe37c), (kernel_ulong_t)&intel_info}, 129*30bb1ce7SJarkko Nikula { PCI_VDEVICE(INTEL, 0xe36f), (kernel_ulong_t)&intel_info}, 130*30bb1ce7SJarkko Nikula /* Panther Lake-P */ 131*30bb1ce7SJarkko Nikula { PCI_VDEVICE(INTEL, 0xe47c), (kernel_ulong_t)&intel_info}, 132*30bb1ce7SJarkko Nikula { PCI_VDEVICE(INTEL, 0xe46f), (kernel_ulong_t)&intel_info}, 133*30bb1ce7SJarkko Nikula { }, 134*30bb1ce7SJarkko Nikula }; 135*30bb1ce7SJarkko Nikula MODULE_DEVICE_TABLE(pci, mipi_i3c_hci_pci_devices); 136*30bb1ce7SJarkko Nikula 137*30bb1ce7SJarkko Nikula static struct pci_driver mipi_i3c_hci_pci_driver = { 138*30bb1ce7SJarkko Nikula .name = "mipi_i3c_hci_pci", 139*30bb1ce7SJarkko Nikula .id_table = mipi_i3c_hci_pci_devices, 140*30bb1ce7SJarkko Nikula .probe = mipi_i3c_hci_pci_probe, 141*30bb1ce7SJarkko Nikula .remove = mipi_i3c_hci_pci_remove, 142*30bb1ce7SJarkko Nikula }; 143*30bb1ce7SJarkko Nikula 144*30bb1ce7SJarkko Nikula module_pci_driver(mipi_i3c_hci_pci_driver); 145*30bb1ce7SJarkko Nikula 146*30bb1ce7SJarkko Nikula MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@intel.com>"); 147*30bb1ce7SJarkko Nikula MODULE_LICENSE("GPL"); 148*30bb1ce7SJarkko Nikula MODULE_DESCRIPTION("MIPI I3C HCI driver on PCI bus"); 149