1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2013 - 2025 Intel Corporation 4 */ 5 6 #include <linux/auxiliary_bus.h> 7 #include <linux/device.h> 8 #include <linux/dma-mapping.h> 9 #include <linux/err.h> 10 #include <linux/list.h> 11 #include <linux/mutex.h> 12 #include <linux/pci.h> 13 #include <linux/pm_domain.h> 14 #include <linux/pm_runtime.h> 15 #include <linux/slab.h> 16 17 #include "ipu7.h" 18 #include "ipu7-bus.h" 19 #include "ipu7-boot.h" 20 #include "ipu7-dma.h" 21 22 static int bus_pm_runtime_suspend(struct device *dev) 23 { 24 struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); 25 int ret; 26 27 ret = pm_generic_runtime_suspend(dev); 28 if (ret) 29 return ret; 30 31 ret = ipu_buttress_powerdown(dev, adev->ctrl); 32 if (!ret) 33 return 0; 34 35 dev_err(dev, "power down failed!\n"); 36 37 /* Powering down failed, attempt to resume device now */ 38 ret = pm_generic_runtime_resume(dev); 39 if (!ret) 40 return -EBUSY; 41 42 return -EIO; 43 } 44 45 static int bus_pm_runtime_resume(struct device *dev) 46 { 47 struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); 48 int ret; 49 50 ret = ipu_buttress_powerup(dev, adev->ctrl); 51 if (ret) 52 return ret; 53 54 ret = pm_generic_runtime_resume(dev); 55 if (ret) 56 goto out_err; 57 58 return 0; 59 60 out_err: 61 ipu_buttress_powerdown(dev, adev->ctrl); 62 63 return -EBUSY; 64 } 65 66 static struct dev_pm_domain ipu7_bus_pm_domain = { 67 .ops = { 68 .runtime_suspend = bus_pm_runtime_suspend, 69 .runtime_resume = bus_pm_runtime_resume, 70 }, 71 }; 72 73 static DEFINE_MUTEX(ipu7_bus_mutex); 74 static void ipu7_bus_release(struct device *dev) 75 { 76 struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); 77 78 kfree(adev->pdata); 79 kfree(adev); 80 } 81 82 struct ipu7_bus_device * 83 ipu7_bus_initialize_device(struct pci_dev *pdev, struct device *parent, 84 void *pdata, const struct ipu_buttress_ctrl *ctrl, 85 const char *name) 86 { 87 struct auxiliary_device *auxdev; 88 struct ipu7_bus_device *adev; 89 struct ipu7_device *isp = pci_get_drvdata(pdev); 90 int ret; 91 92 adev = kzalloc(sizeof(*adev), GFP_KERNEL); 93 if (!adev) 94 return ERR_PTR(-ENOMEM); 95 96 adev->isp = isp; 97 adev->ctrl = ctrl; 98 adev->pdata = pdata; 99 auxdev = &adev->auxdev; 100 auxdev->name = name; 101 auxdev->id = (pci_domain_nr(pdev->bus) << 16) | 102 PCI_DEVID(pdev->bus->number, pdev->devfn); 103 104 auxdev->dev.parent = parent; 105 auxdev->dev.release = ipu7_bus_release; 106 107 ret = auxiliary_device_init(auxdev); 108 if (ret < 0) { 109 dev_err(&isp->pdev->dev, "auxiliary device init failed (%d)\n", 110 ret); 111 kfree(adev); 112 return ERR_PTR(ret); 113 } 114 115 dev_pm_domain_set(&auxdev->dev, &ipu7_bus_pm_domain); 116 117 pm_runtime_forbid(&adev->auxdev.dev); 118 pm_runtime_enable(&adev->auxdev.dev); 119 120 return adev; 121 } 122 123 int ipu7_bus_add_device(struct ipu7_bus_device *adev) 124 { 125 struct auxiliary_device *auxdev = &adev->auxdev; 126 int ret; 127 128 ret = auxiliary_device_add(auxdev); 129 if (ret) { 130 auxiliary_device_uninit(auxdev); 131 return ret; 132 } 133 134 mutex_lock(&ipu7_bus_mutex); 135 list_add(&adev->list, &adev->isp->devices); 136 mutex_unlock(&ipu7_bus_mutex); 137 138 pm_runtime_allow(&auxdev->dev); 139 140 return 0; 141 } 142 143 void ipu7_bus_del_devices(struct pci_dev *pdev) 144 { 145 struct ipu7_device *isp = pci_get_drvdata(pdev); 146 struct ipu7_bus_device *adev, *save; 147 148 mutex_lock(&ipu7_bus_mutex); 149 150 list_for_each_entry_safe(adev, save, &isp->devices, list) { 151 pm_runtime_disable(&adev->auxdev.dev); 152 list_del(&adev->list); 153 auxiliary_device_delete(&adev->auxdev); 154 auxiliary_device_uninit(&adev->auxdev); 155 } 156 157 mutex_unlock(&ipu7_bus_mutex); 158 } 159