Changeset View
Standalone View
sys/compat/linuxkpi/common/src/linux_pci.c
Show First 20 Lines • Show All 64 Lines • ▼ Show 20 Lines | |||||
#include <linux/pci.h> | #include <linux/pci.h> | ||||
#include <linux/compat.h> | #include <linux/compat.h> | ||||
#include <linux/backlight.h> | #include <linux/backlight.h> | ||||
#include "backlight_if.h" | #include "backlight_if.h" | ||||
#include "pcib_if.h" | #include "pcib_if.h" | ||||
/* Undef the linux function macro defined in linux/pci.h */ | |||||
#undef pci_get_class | |||||
static device_probe_t linux_pci_probe; | static device_probe_t linux_pci_probe; | ||||
static device_attach_t linux_pci_attach; | static device_attach_t linux_pci_attach; | ||||
static device_detach_t linux_pci_detach; | static device_detach_t linux_pci_detach; | ||||
static device_suspend_t linux_pci_suspend; | static device_suspend_t linux_pci_suspend; | ||||
static device_resume_t linux_pci_resume; | static device_resume_t linux_pci_resume; | ||||
static device_shutdown_t linux_pci_shutdown; | static device_shutdown_t linux_pci_shutdown; | ||||
static pci_iov_init_t linux_pci_iov_init; | static pci_iov_init_t linux_pci_iov_init; | ||||
static pci_iov_uninit_t linux_pci_iov_uninit; | static pci_iov_uninit_t linux_pci_iov_uninit; | ||||
▲ Show 20 Lines • Show All 124 Lines • ▼ Show 20 Lines | for (id = pdrv->id_table; id->vendor != 0; id++) { | ||||
return (pdrv); | return (pdrv); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
spin_unlock(&pci_lock); | spin_unlock(&pci_lock); | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
static void | |||||
lkpifill_pci_dev(device_t dev, struct pci_dev *pdev) | |||||
{ | |||||
kib: I think you should use PCI_GET_ID() there instead of PCI_DEVFN. | |||||
pdev->devfn = PCI_DEVFN(pci_get_slot(dev), pci_get_function(dev)); | |||||
pdev->vendor = pci_get_vendor(dev); | |||||
pdev->device = pci_get_device(dev); | |||||
pdev->class = pci_get_class(dev); | |||||
pdev->revision = pci_get_revid(dev); | |||||
pdev->dev.bsddev = dev; | |||||
pdev->bus->self = pdev; | |||||
pdev->bus->number = pci_get_bus(dev); | |||||
pdev->bus->domain = pci_get_domain(dev); | |||||
} | |||||
static struct pci_dev * | |||||
lkpinew_pci_dev(device_t dev) | |||||
{ | |||||
struct pci_dev *pdev; | |||||
struct pci_bus *pbus; | |||||
Not Done Inline ActionsStyle: space around binary op (' | '). kib: Style: space around binary op (' | '). | |||||
pdev = malloc(sizeof(*pdev), M_DEVBUF, M_WAITOK|M_ZERO); | |||||
pbus = malloc(sizeof(*pbus), M_DEVBUF, M_WAITOK|M_ZERO); | |||||
pdev->bus = pbus; | |||||
lkpifill_pci_dev(dev, pdev); | |||||
return (pdev); | |||||
} | |||||
struct pci_dev * | |||||
lkpi_pci_get_class(unsigned int class, struct pci_dev *from) | |||||
{ | |||||
device_t dev; | |||||
device_t devfrom = NULL; | |||||
struct pci_dev *pdev; | |||||
if (from != NULL) | |||||
devfrom = from->dev.bsddev; | |||||
dev = pci_find_class_from(class >> 16, (class >> 8) & 0xFF, devfrom); | |||||
if (dev == NULL) | |||||
return (NULL); | |||||
pdev = lkpinew_pci_dev(dev); | |||||
return (pdev); | |||||
} | |||||
struct pci_dev * | |||||
lkpi_pci_get_domain_bus_and_slot(int domain, unsigned int bus, | |||||
unsigned int devfn) | |||||
{ | |||||
Done Inline ActionsGiven the two blocks from malloc() down are the same apart from "devfn", can they be one internal function or two as the lower half is another copy from linux_pci_attach_device() if I am not mistaken? I was looking at that for linux_pci_attach_device() yesterday as for the dev the release function doesn't exist and it's highly confusing how the pci or the dev get allocated and the naming of things. So I am happy the less duplicated "alloc" code we have. Not need to do, if you don't want to, just a suggestion in this case. bz: Given the two blocks from malloc() down are the same apart from "devfn", can they be one… | |||||
Done Inline ActionsYeah I can do a generic alloc function. manu: Yeah I can do a generic alloc function. | |||||
Done Inline ActionsTHANK YOU! bz: THANK YOU! | |||||
device_t dev; | |||||
struct pci_dev *pdev; | |||||
dev = pci_find_dbsf(domain, bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); | |||||
if (dev == NULL) | |||||
return (NULL); | |||||
Not Done Inline ActionsI suggest to add assert that returned device indeed has slot/function number as requested. This should simplify further debugging for some cases. kib: I suggest to add assert that returned device indeed has slot/function number as requested. | |||||
pdev = lkpinew_pci_dev(dev); | |||||
return (pdev); | |||||
} | |||||
static int | static int | ||||
linux_pci_probe(device_t dev) | linux_pci_probe(device_t dev) | ||||
{ | { | ||||
const struct pci_device_id *id; | const struct pci_device_id *id; | ||||
struct pci_driver *pdrv; | struct pci_driver *pdrv; | ||||
if ((pdrv = linux_pci_find(dev, &id)) == NULL) | if ((pdrv = linux_pci_find(dev, &id)) == NULL) | ||||
return (ENXIO); | return (ENXIO); | ||||
Show All 19 Lines | linux_pci_attach(device_t dev) | ||||
return (linux_pci_attach_device(dev, pdrv, id, pdev)); | return (linux_pci_attach_device(dev, pdrv, id, pdev)); | ||||
} | } | ||||
int | int | ||||
linux_pci_attach_device(device_t dev, struct pci_driver *pdrv, | linux_pci_attach_device(device_t dev, struct pci_driver *pdrv, | ||||
const struct pci_device_id *id, struct pci_dev *pdev) | const struct pci_device_id *id, struct pci_dev *pdev) | ||||
{ | { | ||||
struct resource_list_entry *rle; | struct resource_list_entry *rle; | ||||
struct pci_bus *pbus; | |||||
struct pci_devinfo *dinfo; | struct pci_devinfo *dinfo; | ||||
device_t parent; | device_t parent; | ||||
uintptr_t rid; | uintptr_t rid; | ||||
int error; | int error; | ||||
bool isdrm; | bool isdrm; | ||||
linux_set_current(curthread); | linux_set_current(curthread); | ||||
parent = device_get_parent(dev); | parent = device_get_parent(dev); | ||||
isdrm = pdrv != NULL && pdrv->isdrm; | isdrm = pdrv != NULL && pdrv->isdrm; | ||||
if (isdrm) { | if (isdrm) { | ||||
dinfo = device_get_ivars(parent); | dinfo = device_get_ivars(parent); | ||||
device_set_ivars(dev, dinfo); | device_set_ivars(dev, dinfo); | ||||
} else { | } else { | ||||
dinfo = device_get_ivars(dev); | dinfo = device_get_ivars(dev); | ||||
} | } | ||||
pdev->bus = malloc(sizeof(*pdev->bus), M_DEVBUF, M_WAITOK | M_ZERO); | |||||
lkpifill_pci_dev(dev, pdev); | |||||
pdev->dev.parent = &linux_root_device; | pdev->dev.parent = &linux_root_device; | ||||
pdev->dev.bsddev = dev; | |||||
INIT_LIST_HEAD(&pdev->dev.irqents); | INIT_LIST_HEAD(&pdev->dev.irqents); | ||||
if (isdrm) | if (isdrm) | ||||
PCI_GET_ID(device_get_parent(parent), parent, PCI_ID_RID, &rid); | PCI_GET_ID(device_get_parent(parent), parent, PCI_ID_RID, &rid); | ||||
else | else | ||||
PCI_GET_ID(parent, dev, PCI_ID_RID, &rid); | PCI_GET_ID(parent, dev, PCI_ID_RID, &rid); | ||||
pdev->devfn = rid; | pdev->devfn = rid; | ||||
pdev->device = dinfo->cfg.device; | pdev->device = dinfo->cfg.device; | ||||
pdev->vendor = dinfo->cfg.vendor; | pdev->vendor = dinfo->cfg.vendor; | ||||
pdev->subsystem_vendor = dinfo->cfg.subvendor; | pdev->subsystem_vendor = dinfo->cfg.subvendor; | ||||
pdev->subsystem_device = dinfo->cfg.subdevice; | pdev->subsystem_device = dinfo->cfg.subdevice; | ||||
pdev->class = pci_get_class(dev); | |||||
pdev->revision = pci_get_revid(dev); | |||||
pdev->pdrv = pdrv; | pdev->pdrv = pdrv; | ||||
kobject_init(&pdev->dev.kobj, &linux_dev_ktype); | kobject_init(&pdev->dev.kobj, &linux_dev_ktype); | ||||
kobject_set_name(&pdev->dev.kobj, device_get_nameunit(dev)); | kobject_set_name(&pdev->dev.kobj, device_get_nameunit(dev)); | ||||
kobject_add(&pdev->dev.kobj, &linux_root_device.kobj, | kobject_add(&pdev->dev.kobj, &linux_root_device.kobj, | ||||
kobject_name(&pdev->dev.kobj)); | kobject_name(&pdev->dev.kobj)); | ||||
rle = linux_pci_get_rle(pdev, SYS_RES_IRQ, 0); | rle = linux_pci_get_rle(pdev, SYS_RES_IRQ, 0); | ||||
if (rle != NULL) | if (rle != NULL) | ||||
pdev->dev.irq = rle->start; | pdev->dev.irq = rle->start; | ||||
else | else | ||||
pdev->dev.irq = LINUX_IRQ_INVALID; | pdev->dev.irq = LINUX_IRQ_INVALID; | ||||
pdev->irq = pdev->dev.irq; | pdev->irq = pdev->dev.irq; | ||||
error = linux_pdev_dma_init(pdev); | error = linux_pdev_dma_init(pdev); | ||||
if (error) | if (error) | ||||
goto out_dma_init; | goto out_dma_init; | ||||
TAILQ_INIT(&pdev->mmio); | TAILQ_INIT(&pdev->mmio); | ||||
pbus = malloc(sizeof(*pbus), M_DEVBUF, M_WAITOK | M_ZERO); | |||||
pbus->self = pdev; | |||||
pbus->number = pci_get_bus(dev); | |||||
pbus->domain = pci_get_domain(dev); | |||||
pdev->bus = pbus; | |||||
spin_lock(&pci_lock); | spin_lock(&pci_lock); | ||||
list_add(&pdev->links, &pci_devices); | list_add(&pdev->links, &pci_devices); | ||||
spin_unlock(&pci_lock); | spin_unlock(&pci_lock); | ||||
if (pdrv != NULL) { | if (pdrv != NULL) { | ||||
error = pdrv->probe(pdev, id); | error = pdrv->probe(pdev, id); | ||||
if (error) | if (error) | ||||
▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | |||||
linux_pci_suspend(device_t dev) | linux_pci_suspend(device_t dev) | ||||
{ | { | ||||
const struct dev_pm_ops *pmops; | const struct dev_pm_ops *pmops; | ||||
struct pm_message pm = { }; | struct pm_message pm = { }; | ||||
struct pci_dev *pdev; | struct pci_dev *pdev; | ||||
int error; | int error; | ||||
error = 0; | error = 0; | ||||
linux_set_current(curthread); | linux_set_current(curthread); | ||||
Done Inline ActionsI am still pondering if this will work in all cases; I'll go and try to try/check tonight. bz: I am still pondering if this will work in all cases; I'll go and try to try/check tonight. | |||||
Done Inline ActionsWhy will it not work in some case ? manu: Why will it not work in some case ? | |||||
Done Inline ActionsBecause from what I can see on network drivers pci_dev_get() and pci_dev_put() are reference operations, I'd assume around the device and not direct operations. I also see pdev accesses after pci_dev_put() so it cannot be freed at that point: iwlwifi/pcie/trans.c::iwl_trans_pcie_removal_wk() pci_dev_put(pdev); pci_stop_and_remove_bus_device(pdev); It's the only call I have there so I had ignored it so far. I assume the above just works for you in your special case. I further assume the normal implementation would be similar to device_get/put(): static __inline void /* XXX-BZ returns something? */ pci_dev_get(struct pci_dev *pdev) { if (pdev != NULL) device_get(&pdev->dev); } static __inline void pci_dev_put(struct pci_dev *pdev) { if (pdev != NULL) device_put(&pdev->dev); } I believe to achieve what you seem to want/need you'd want to do the following: Rename this function to static void linuxkpi_pci_release_direct(struct device *dev) ( ... fix/cast dev to pdev } and move it up before your two new linuxkpi_pci_get_* functions or a new alloc function; there for the struct pci_dev *dev you want to set: pdev->dev.release = linuxkpi_pci_release_direct; But that means that you probably also have to set the parent and init the kobj in your two get_* or alloc functions above as does linux_pci_attach_device(). That means embracing the struct device which embedded in pci_dev as first argument rather than working around it. And you don't have to use it much more than that I'd assume. I haven't tried this; only put it down from the top of my head. PS: yes, linux_pci_attach_device() also lacks a release function for devres; this is how I got to figure that out yesterday. bz: Because from what I can see on network drivers pci_dev_get() and pci_dev_put() are reference… | |||||
Done Inline ActionsYeah I had a look at what linux is doing and you're right, it's just device_put. manu: Yeah I had a look at what linux is doing and you're right, it's just device_put.
I'll see at… | |||||
pdev = device_get_softc(dev); | pdev = device_get_softc(dev); | ||||
pmops = pdev->pdrv->driver.pm; | pmops = pdev->pdrv->driver.pm; | ||||
if (pdev->pdrv->suspend != NULL) | if (pdev->pdrv->suspend != NULL) | ||||
error = -pdev->pdrv->suspend(pdev, pm); | error = -pdev->pdrv->suspend(pdev, pm); | ||||
else if (pmops != NULL && pmops->suspend != NULL) { | else if (pmops != NULL && pmops->suspend != NULL) { | ||||
error = -pmops->suspend(&pdev->dev); | error = -pmops->suspend(&pdev->dev); | ||||
if (error == 0 && pmops->suspend_late != NULL) | if (error == 0 && pmops->suspend_late != NULL) | ||||
▲ Show 20 Lines • Show All 668 Lines • Show Last 20 Lines |
I think you should use PCI_GET_ID() there instead of PCI_DEVFN.