160 lines
3.3 KiB
C
160 lines
3.3 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (C) 2013 - 2024 Intel Corporation
|
|
*/
|
|
|
|
#include <linux/auxiliary_bus.h>
|
|
#include <linux/device.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/err.h>
|
|
#include <linux/list.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/pm_domain.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/slab.h>
|
|
|
|
#include "ipu6.h"
|
|
#include "ipu6-bus.h"
|
|
#include "ipu6-buttress.h"
|
|
#include "ipu6-dma.h"
|
|
|
|
static int bus_pm_runtime_suspend(struct device *dev)
|
|
{
|
|
struct ipu6_bus_device *adev = to_ipu6_bus_device(dev);
|
|
int ret;
|
|
|
|
ret = pm_generic_runtime_suspend(dev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = ipu6_buttress_power(dev, adev->ctrl, false);
|
|
if (!ret)
|
|
return 0;
|
|
|
|
dev_err(dev, "power down failed!\n");
|
|
|
|
/* Powering down failed, attempt to resume device now */
|
|
ret = pm_generic_runtime_resume(dev);
|
|
if (!ret)
|
|
return -EBUSY;
|
|
|
|
return -EIO;
|
|
}
|
|
|
|
static int bus_pm_runtime_resume(struct device *dev)
|
|
{
|
|
struct ipu6_bus_device *adev = to_ipu6_bus_device(dev);
|
|
int ret;
|
|
|
|
ret = ipu6_buttress_power(dev, adev->ctrl, true);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = pm_generic_runtime_resume(dev);
|
|
if (ret)
|
|
goto out_err;
|
|
|
|
return 0;
|
|
|
|
out_err:
|
|
ipu6_buttress_power(dev, adev->ctrl, false);
|
|
|
|
return -EBUSY;
|
|
}
|
|
|
|
static struct dev_pm_domain ipu6_bus_pm_domain = {
|
|
.ops = {
|
|
.runtime_suspend = bus_pm_runtime_suspend,
|
|
.runtime_resume = bus_pm_runtime_resume,
|
|
},
|
|
};
|
|
|
|
static DEFINE_MUTEX(ipu6_bus_mutex);
|
|
|
|
static void ipu6_bus_release(struct device *dev)
|
|
{
|
|
struct ipu6_bus_device *adev = to_ipu6_bus_device(dev);
|
|
|
|
kfree(adev->pdata);
|
|
kfree(adev);
|
|
}
|
|
|
|
struct ipu6_bus_device *
|
|
ipu6_bus_initialize_device(struct pci_dev *pdev, struct device *parent,
|
|
void *pdata, struct ipu6_buttress_ctrl *ctrl,
|
|
char *name)
|
|
{
|
|
struct auxiliary_device *auxdev;
|
|
struct ipu6_bus_device *adev;
|
|
struct ipu6_device *isp = pci_get_drvdata(pdev);
|
|
int ret;
|
|
|
|
adev = kzalloc(sizeof(*adev), GFP_KERNEL);
|
|
if (!adev)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
adev->isp = isp;
|
|
adev->ctrl = ctrl;
|
|
adev->pdata = pdata;
|
|
auxdev = &adev->auxdev;
|
|
auxdev->name = name;
|
|
auxdev->id = (pci_domain_nr(pdev->bus) << 16) |
|
|
PCI_DEVID(pdev->bus->number, pdev->devfn);
|
|
|
|
auxdev->dev.parent = parent;
|
|
auxdev->dev.release = ipu6_bus_release;
|
|
|
|
ret = auxiliary_device_init(auxdev);
|
|
if (ret < 0) {
|
|
dev_err(&isp->pdev->dev, "auxiliary device init failed (%d)\n",
|
|
ret);
|
|
kfree(adev);
|
|
return ERR_PTR(ret);
|
|
}
|
|
|
|
dev_pm_domain_set(&auxdev->dev, &ipu6_bus_pm_domain);
|
|
|
|
pm_runtime_forbid(&adev->auxdev.dev);
|
|
pm_runtime_enable(&adev->auxdev.dev);
|
|
|
|
return adev;
|
|
}
|
|
|
|
int ipu6_bus_add_device(struct ipu6_bus_device *adev)
|
|
{
|
|
struct auxiliary_device *auxdev = &adev->auxdev;
|
|
int ret;
|
|
|
|
ret = auxiliary_device_add(auxdev);
|
|
if (ret) {
|
|
auxiliary_device_uninit(auxdev);
|
|
return ret;
|
|
}
|
|
|
|
mutex_lock(&ipu6_bus_mutex);
|
|
list_add(&adev->list, &adev->isp->devices);
|
|
mutex_unlock(&ipu6_bus_mutex);
|
|
|
|
pm_runtime_allow(&auxdev->dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void ipu6_bus_del_devices(struct pci_dev *pdev)
|
|
{
|
|
struct ipu6_device *isp = pci_get_drvdata(pdev);
|
|
struct ipu6_bus_device *adev, *save;
|
|
|
|
mutex_lock(&ipu6_bus_mutex);
|
|
|
|
list_for_each_entry_safe(adev, save, &isp->devices, list) {
|
|
pm_runtime_disable(&adev->auxdev.dev);
|
|
list_del(&adev->list);
|
|
auxiliary_device_delete(&adev->auxdev);
|
|
auxiliary_device_uninit(&adev->auxdev);
|
|
}
|
|
|
|
mutex_unlock(&ipu6_bus_mutex);
|
|
}
|