mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-20 11:11:24 +00:00
Stage one of multipass suspend/resume
Summary: Add the beginnings of multipass suspend/resume, by introducing BUS_SUSPEND_CHILD/BUS_RESUME_CHILD, and move the PCI driver to this. Reviewers: jhb Reviewed By: jhb Differential Revision: https://reviews.freebsd.org/D590
This commit is contained in:
parent
5ed6ab5baa
commit
a1c1634858
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=272013
@ -131,7 +131,7 @@ static device_method_t pci_methods[] = {
|
|||||||
DEVMETHOD(device_detach, bus_generic_detach),
|
DEVMETHOD(device_detach, bus_generic_detach),
|
||||||
#endif
|
#endif
|
||||||
DEVMETHOD(device_shutdown, bus_generic_shutdown),
|
DEVMETHOD(device_shutdown, bus_generic_shutdown),
|
||||||
DEVMETHOD(device_suspend, pci_suspend),
|
DEVMETHOD(device_suspend, bus_generic_suspend),
|
||||||
DEVMETHOD(device_resume, pci_resume),
|
DEVMETHOD(device_resume, pci_resume),
|
||||||
|
|
||||||
/* Bus interface */
|
/* Bus interface */
|
||||||
@ -157,6 +157,8 @@ static device_method_t pci_methods[] = {
|
|||||||
DEVMETHOD(bus_child_pnpinfo_str, pci_child_pnpinfo_str_method),
|
DEVMETHOD(bus_child_pnpinfo_str, pci_child_pnpinfo_str_method),
|
||||||
DEVMETHOD(bus_child_location_str, pci_child_location_str_method),
|
DEVMETHOD(bus_child_location_str, pci_child_location_str_method),
|
||||||
DEVMETHOD(bus_remap_intr, pci_remap_intr_method),
|
DEVMETHOD(bus_remap_intr, pci_remap_intr_method),
|
||||||
|
DEVMETHOD(bus_suspend_child, pci_suspend_child),
|
||||||
|
DEVMETHOD(bus_resume_child, pci_resume_child),
|
||||||
|
|
||||||
/* PCI interface */
|
/* PCI interface */
|
||||||
DEVMETHOD(pci_read_config, pci_read_config_method),
|
DEVMETHOD(pci_read_config, pci_read_config_method),
|
||||||
@ -3622,12 +3624,11 @@ pci_detach(device_t dev)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void
|
static void
|
||||||
pci_set_power_children(device_t dev, device_t *devlist, int numdevs,
|
pci_set_power_child(device_t dev, device_t child, int state)
|
||||||
int state)
|
|
||||||
{
|
{
|
||||||
device_t child, pcib;
|
|
||||||
struct pci_devinfo *dinfo;
|
struct pci_devinfo *dinfo;
|
||||||
int dstate, i;
|
device_t pcib;
|
||||||
|
int dstate;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set the device to the given state. If the firmware suggests
|
* Set the device to the given state. If the firmware suggests
|
||||||
@ -3637,45 +3638,54 @@ pci_set_power_children(device_t dev, device_t *devlist, int numdevs,
|
|||||||
* are handled separately.
|
* are handled separately.
|
||||||
*/
|
*/
|
||||||
pcib = device_get_parent(dev);
|
pcib = device_get_parent(dev);
|
||||||
for (i = 0; i < numdevs; i++) {
|
|
||||||
child = devlist[i];
|
|
||||||
dinfo = device_get_ivars(child);
|
dinfo = device_get_ivars(child);
|
||||||
dstate = state;
|
dstate = state;
|
||||||
if (device_is_attached(child) &&
|
if (device_is_attached(child) &&
|
||||||
PCIB_POWER_FOR_SLEEP(pcib, dev, &dstate) == 0)
|
PCIB_POWER_FOR_SLEEP(pcib, dev, &dstate) == 0)
|
||||||
pci_set_powerstate(child, dstate);
|
pci_set_powerstate(child, dstate);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
pci_suspend(device_t dev)
|
pci_suspend_child(device_t dev, device_t child)
|
||||||
{
|
{
|
||||||
device_t child, *devlist;
|
|
||||||
struct pci_devinfo *dinfo;
|
struct pci_devinfo *dinfo;
|
||||||
int error, i, numdevs;
|
int error;
|
||||||
|
|
||||||
|
dinfo = device_get_ivars(child);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Save the PCI configuration space for each child and set the
|
* Save the PCI configuration space for the child and set the
|
||||||
* device in the appropriate power state for this sleep state.
|
* device in the appropriate power state for this sleep state.
|
||||||
*/
|
*/
|
||||||
if ((error = device_get_children(dev, &devlist, &numdevs)) != 0)
|
|
||||||
return (error);
|
|
||||||
for (i = 0; i < numdevs; i++) {
|
|
||||||
child = devlist[i];
|
|
||||||
dinfo = device_get_ivars(child);
|
|
||||||
pci_cfg_save(child, dinfo, 0);
|
pci_cfg_save(child, dinfo, 0);
|
||||||
}
|
|
||||||
|
|
||||||
/* Suspend devices before potentially powering them down. */
|
/* Suspend devices before potentially powering them down. */
|
||||||
error = bus_generic_suspend(dev);
|
error = bus_generic_suspend_child(dev, child);
|
||||||
if (error) {
|
|
||||||
free(devlist, M_TEMP);
|
if (error)
|
||||||
return (error);
|
return (error);
|
||||||
}
|
|
||||||
if (pci_do_power_suspend)
|
if (pci_do_power_suspend)
|
||||||
pci_set_power_children(dev, devlist, numdevs,
|
pci_set_power_child(dev, child, PCI_POWERSTATE_D3);
|
||||||
PCI_POWERSTATE_D3);
|
|
||||||
free(devlist, M_TEMP);
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
pci_resume_child(device_t dev, device_t child)
|
||||||
|
{
|
||||||
|
struct pci_devinfo *dinfo;
|
||||||
|
|
||||||
|
if (pci_do_power_resume)
|
||||||
|
pci_set_power_child(dev, child, PCI_POWERSTATE_D0);
|
||||||
|
|
||||||
|
dinfo = device_get_ivars(child);
|
||||||
|
pci_cfg_restore(child, dinfo);
|
||||||
|
if (!device_is_attached(child))
|
||||||
|
pci_cfg_save(child, dinfo, 1);
|
||||||
|
|
||||||
|
bus_generic_resume_child(dev, child);
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3683,27 +3693,10 @@ int
|
|||||||
pci_resume(device_t dev)
|
pci_resume(device_t dev)
|
||||||
{
|
{
|
||||||
device_t child, *devlist;
|
device_t child, *devlist;
|
||||||
struct pci_devinfo *dinfo;
|
|
||||||
int error, i, numdevs;
|
int error, i, numdevs;
|
||||||
|
|
||||||
/*
|
|
||||||
* Set each child to D0 and restore its PCI configuration space.
|
|
||||||
*/
|
|
||||||
if ((error = device_get_children(dev, &devlist, &numdevs)) != 0)
|
if ((error = device_get_children(dev, &devlist, &numdevs)) != 0)
|
||||||
return (error);
|
return (error);
|
||||||
if (pci_do_power_resume)
|
|
||||||
pci_set_power_children(dev, devlist, numdevs,
|
|
||||||
PCI_POWERSTATE_D0);
|
|
||||||
|
|
||||||
/* Now the device is powered up, restore its config space. */
|
|
||||||
for (i = 0; i < numdevs; i++) {
|
|
||||||
child = devlist[i];
|
|
||||||
dinfo = device_get_ivars(child);
|
|
||||||
|
|
||||||
pci_cfg_restore(child, dinfo);
|
|
||||||
if (!device_is_attached(child))
|
|
||||||
pci_cfg_save(child, dinfo, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Resume critical devices first, then everything else later.
|
* Resume critical devices first, then everything else later.
|
||||||
@ -3715,7 +3708,7 @@ pci_resume(device_t dev)
|
|||||||
case PCIC_MEMORY:
|
case PCIC_MEMORY:
|
||||||
case PCIC_BRIDGE:
|
case PCIC_BRIDGE:
|
||||||
case PCIC_BASEPERIPH:
|
case PCIC_BASEPERIPH:
|
||||||
DEVICE_RESUME(child);
|
BUS_RESUME_CHILD(dev, child);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3728,7 +3721,7 @@ pci_resume(device_t dev)
|
|||||||
case PCIC_BASEPERIPH:
|
case PCIC_BASEPERIPH:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
DEVICE_RESUME(child);
|
BUS_RESUME_CHILD(dev, child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
free(devlist, M_TEMP);
|
free(devlist, M_TEMP);
|
||||||
|
@ -123,7 +123,8 @@ int pci_child_pnpinfo_str_method(device_t cbdev, device_t child,
|
|||||||
char *buf, size_t buflen);
|
char *buf, size_t buflen);
|
||||||
int pci_assign_interrupt_method(device_t dev, device_t child);
|
int pci_assign_interrupt_method(device_t dev, device_t child);
|
||||||
int pci_resume(device_t dev);
|
int pci_resume(device_t dev);
|
||||||
int pci_suspend(device_t dev);
|
int pci_resume_child(device_t dev, device_t child);
|
||||||
|
int pci_suspend_child(device_t dev, device_t child);
|
||||||
bus_dma_tag_t pci_get_dma_tag(device_t bus, device_t dev);
|
bus_dma_tag_t pci_get_dma_tag(device_t bus, device_t dev);
|
||||||
void pci_child_added_method(device_t dev, device_t child);
|
void pci_child_added_method(device_t dev, device_t child);
|
||||||
|
|
||||||
|
@ -670,3 +670,25 @@ METHOD int remap_intr {
|
|||||||
device_t _child;
|
device_t _child;
|
||||||
u_int _irq;
|
u_int _irq;
|
||||||
} DEFAULT null_remap_intr;
|
} DEFAULT null_remap_intr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Suspend a given child
|
||||||
|
*
|
||||||
|
* @param _dev the parent device of @p _child
|
||||||
|
* @param _child the device to suspend
|
||||||
|
*/
|
||||||
|
METHOD int suspend_child {
|
||||||
|
device_t _dev;
|
||||||
|
device_t _child;
|
||||||
|
} DEFAULT bus_generic_suspend_child;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Resume a given child
|
||||||
|
*
|
||||||
|
* @param _dev the parent device of @p _child
|
||||||
|
* @param _child the device to resume
|
||||||
|
*/
|
||||||
|
METHOD int resume_child {
|
||||||
|
device_t _dev;
|
||||||
|
device_t _child;
|
||||||
|
} DEFAULT bus_generic_resume_child;
|
||||||
|
@ -135,6 +135,7 @@ struct device {
|
|||||||
#define DF_DONENOMATCH 0x20 /* don't execute DEVICE_NOMATCH again */
|
#define DF_DONENOMATCH 0x20 /* don't execute DEVICE_NOMATCH again */
|
||||||
#define DF_EXTERNALSOFTC 0x40 /* softc not allocated by us */
|
#define DF_EXTERNALSOFTC 0x40 /* softc not allocated by us */
|
||||||
#define DF_REBID 0x80 /* Can rebid after attach */
|
#define DF_REBID 0x80 /* Can rebid after attach */
|
||||||
|
#define DF_SUSPENDED 0x100 /* Device is suspended. */
|
||||||
u_int order; /**< order from device_add_child_ordered() */
|
u_int order; /**< order from device_add_child_ordered() */
|
||||||
void *ivars; /**< instance variables */
|
void *ivars; /**< instance variables */
|
||||||
void *softc; /**< current driver's variables */
|
void *softc; /**< current driver's variables */
|
||||||
@ -3630,6 +3631,39 @@ bus_generic_shutdown(device_t dev)
|
|||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Default function for suspending a child device.
|
||||||
|
*
|
||||||
|
* This function is to be used by a bus's DEVICE_SUSPEND_CHILD().
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
bus_generic_suspend_child(device_t dev, device_t child)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
|
||||||
|
error = DEVICE_SUSPEND(child);
|
||||||
|
|
||||||
|
if (error == 0)
|
||||||
|
dev->flags |= DF_SUSPENDED;
|
||||||
|
|
||||||
|
return (error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Default function for resuming a child device.
|
||||||
|
*
|
||||||
|
* This function is to be used by a bus's DEVICE_RESUME_CHILD().
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
bus_generic_resume_child(device_t dev, device_t child)
|
||||||
|
{
|
||||||
|
|
||||||
|
DEVICE_RESUME(child);
|
||||||
|
dev->flags &= ~DF_SUSPENDED;
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Helper function for implementing DEVICE_SUSPEND()
|
* @brief Helper function for implementing DEVICE_SUSPEND()
|
||||||
*
|
*
|
||||||
@ -3646,12 +3680,12 @@ bus_generic_suspend(device_t dev)
|
|||||||
device_t child, child2;
|
device_t child, child2;
|
||||||
|
|
||||||
TAILQ_FOREACH(child, &dev->children, link) {
|
TAILQ_FOREACH(child, &dev->children, link) {
|
||||||
error = DEVICE_SUSPEND(child);
|
error = BUS_SUSPEND_CHILD(dev, child);
|
||||||
if (error) {
|
if (error) {
|
||||||
for (child2 = TAILQ_FIRST(&dev->children);
|
for (child2 = TAILQ_FIRST(&dev->children);
|
||||||
child2 && child2 != child;
|
child2 && child2 != child;
|
||||||
child2 = TAILQ_NEXT(child2, link))
|
child2 = TAILQ_NEXT(child2, link))
|
||||||
DEVICE_RESUME(child2);
|
BUS_RESUME_CHILD(dev, child2);
|
||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3670,7 +3704,7 @@ bus_generic_resume(device_t dev)
|
|||||||
device_t child;
|
device_t child;
|
||||||
|
|
||||||
TAILQ_FOREACH(child, &dev->children, link) {
|
TAILQ_FOREACH(child, &dev->children, link) {
|
||||||
DEVICE_RESUME(child);
|
BUS_RESUME_CHILD(dev, child);
|
||||||
/* if resume fails, there's nothing we can usefully do... */
|
/* if resume fails, there's nothing we can usefully do... */
|
||||||
}
|
}
|
||||||
return (0);
|
return (0);
|
||||||
|
@ -339,6 +339,7 @@ int bus_generic_read_ivar(device_t dev, device_t child, int which,
|
|||||||
int bus_generic_release_resource(device_t bus, device_t child,
|
int bus_generic_release_resource(device_t bus, device_t child,
|
||||||
int type, int rid, struct resource *r);
|
int type, int rid, struct resource *r);
|
||||||
int bus_generic_resume(device_t dev);
|
int bus_generic_resume(device_t dev);
|
||||||
|
int bus_generic_resume_child(device_t dev, device_t child);
|
||||||
int bus_generic_setup_intr(device_t dev, device_t child,
|
int bus_generic_setup_intr(device_t dev, device_t child,
|
||||||
struct resource *irq, int flags,
|
struct resource *irq, int flags,
|
||||||
driver_filter_t *filter, driver_intr_t *intr,
|
driver_filter_t *filter, driver_intr_t *intr,
|
||||||
@ -357,6 +358,7 @@ int bus_generic_rl_release_resource (device_t, device_t, int, int,
|
|||||||
|
|
||||||
int bus_generic_shutdown(device_t dev);
|
int bus_generic_shutdown(device_t dev);
|
||||||
int bus_generic_suspend(device_t dev);
|
int bus_generic_suspend(device_t dev);
|
||||||
|
int bus_generic_suspend_child(device_t dev, device_t child);
|
||||||
int bus_generic_teardown_intr(device_t dev, device_t child,
|
int bus_generic_teardown_intr(device_t dev, device_t child,
|
||||||
struct resource *irq, void *cookie);
|
struct resource *irq, void *cookie);
|
||||||
int bus_generic_write_ivar(device_t dev, device_t child, int which,
|
int bus_generic_write_ivar(device_t dev, device_t child, int which,
|
||||||
|
Loading…
Reference in New Issue
Block a user