1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2013 - 2024 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 "ipu6.h" 18 #include "ipu6-bus.h" 19 #include "ipu6-buttress.h" 20 #include "ipu6-dma.h" 21 22 static int bus_pm_runtime_suspend(struct device *dev) 23 { 24 struct ipu6_bus_device *adev = to_ipu6_bus_device(dev); 25 int ret; 26 27 ret = pm_generic_runtime_suspend(dev); 28 if (ret) 29 return ret; 30 31 ret = ipu6_buttress_power(dev, adev->ctrl, false); 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 ipu6_bus_device *adev = to_ipu6_bus_device(dev); 48 int ret; 49 50 ret = ipu6_buttress_power(dev, adev->ctrl, true); 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 ipu6_buttress_power(dev, adev->ctrl, false); 62 63 return -EBUSY; 64 } 65 66 static struct dev_pm_domain ipu6_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(ipu6_bus_mutex); 74 75 static void ipu6_bus_release(struct device *dev) 76 { 77 struct ipu6_bus_device *adev = to_ipu6_bus_device(dev); 78 79 kfree(adev->pdata); 80 kfree(adev); 81 } 82 83 struct ipu6_bus_device * 84 ipu6_bus_initialize_device(struct pci_dev *pdev, struct device *parent, 85 void *pdata, struct ipu6_buttress_ctrl *ctrl, 86 char *name) 87 { 88 struct auxiliary_device *auxdev; 89 struct ipu6_bus_device *adev; 90 struct ipu6_device *isp = pci_get_drvdata(pdev); 91 int ret; 92 93 adev = kzalloc(sizeof(*adev), GFP_KERNEL); 94 if (!adev) 95 return ERR_PTR(-ENOMEM); 96 97 adev->dma_mask = DMA_BIT_MASK(isp->secure_mode ? IPU6_MMU_ADDR_BITS : 98 IPU6_MMU_ADDR_BITS_NON_SECURE); 99 adev->isp = isp; 100 adev->ctrl = ctrl; 101 adev->pdata = pdata; 102 auxdev = &adev->auxdev; 103 auxdev->name = name; 104 auxdev->id = (pci_domain_nr(pdev->bus) << 16) | 105 PCI_DEVID(pdev->bus->number, pdev->devfn); 106 107 auxdev->dev.parent = parent; 108 auxdev->dev.release = ipu6_bus_release; 109 auxdev->dev.dma_ops = &ipu6_dma_ops; 110 auxdev->dev.dma_mask = &adev->dma_mask; 111 auxdev->dev.dma_parms = pdev->dev.dma_parms; 112 auxdev->dev.coherent_dma_mask = adev->dma_mask; 113 114 ret = auxiliary_device_init(auxdev); 115 if (ret < 0) { 116 dev_err(&isp->pdev->dev, "auxiliary device init failed (%d)\n", 117 ret); 118 kfree(adev); 119 return ERR_PTR(ret); 120 } 121 122 dev_pm_domain_set(&auxdev->dev, &ipu6_bus_pm_domain); 123 124 pm_runtime_forbid(&adev->auxdev.dev); 125 pm_runtime_enable(&adev->auxdev.dev); 126 127 return adev; 128 } 129 130 int ipu6_bus_add_device(struct ipu6_bus_device *adev) 131 { 132 struct auxiliary_device *auxdev = &adev->auxdev; 133 int ret; 134 135 ret = auxiliary_device_add(auxdev); 136 if (ret) { 137 auxiliary_device_uninit(auxdev); 138 return ret; 139 } 140 141 mutex_lock(&ipu6_bus_mutex); 142 list_add(&adev->list, &adev->isp->devices); 143 mutex_unlock(&ipu6_bus_mutex); 144 145 pm_runtime_allow(&auxdev->dev); 146 147 return 0; 148 } 149 150 void ipu6_bus_del_devices(struct pci_dev *pdev) 151 { 152 struct ipu6_device *isp = pci_get_drvdata(pdev); 153 struct ipu6_bus_device *adev, *save; 154 155 mutex_lock(&ipu6_bus_mutex); 156 157 list_for_each_entry_safe(adev, save, &isp->devices, list) { 158 pm_runtime_disable(&adev->auxdev.dev); 159 list_del(&adev->list); 160 auxiliary_device_delete(&adev->auxdev); 161 auxiliary_device_uninit(&adev->auxdev); 162 } 163 164 mutex_unlock(&ipu6_bus_mutex); 165 } 166