lkml.org 
[lkml]   [2006]   [Jan]   [5]   [last100]   RSS Feed
Views: [more markup]  [less markup]  [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    DateThu, 5 Jan 2006 17:37:30 -0800 (PST)
    FromPatrick Mochel <>
    SubjectRe: [linux-pm] [patch] pm: fix runtime powermanagement's /sys interface
    
    On Fri, 6 Jan 2006, Pavel Machek wrote:
    
    > On Čt 05-01-06 16:04:07, Patrick Mochel wrote:
    
    > > A better point, and one that would actually be useful, would be to remove
    > > the file altogether. Let Dominik export a power file, with complete
    > > control over the values, for each pcmcia device. Then you never have to
    > > worry about breaking PCMCIA again.
    >
    > Fine with me.
    
    ACK, you beat me to it.
    
    And, appended is a patch to export PM controls for PCI devices. The file
    "pm_possible_states" exports the states a device supports, and "pm_state"
    exports the current state (and provides the interface for entering a
    state).
    
    Eventually, some drivers will want to fix up those values so that it can
    mask of states that it doesn't support, as well as offer possible device-
    specific states.
    
    What's interesting is that with this patch, I can see that two more
    devices on my system support D1 and D2 -- the cardbus controllers, which
    are actually bridges whose PM capabilities aren't exported via lspci.
    
    Thanks,
    
    	Patrick
    
    
    diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
    index 6707df9..628f3a3 100644
    --- a/drivers/pci/Makefile
    +++ b/drivers/pci/Makefile
    @@ -36,6 +36,10 @@ obj-$(CONFIG_ACPI)    += pci-acpi.o
     # Cardbus & CompactPCI use setup-bus
     obj-$(CONFIG_HOTPLUG) += setup-bus.o
    
    +
    +# Power Management functionality
    +obj-$(CONFIG_PM)	+= pm.o
    +
     ifndef CONFIG_X86
     obj-y += syscall.o
     endif
    diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
    index eed67d9..83045d9 100644
    --- a/drivers/pci/bus.c
    +++ b/drivers/pci/bus.c
    @@ -85,6 +85,8 @@ void __devinit pci_bus_add_device(struct
     	list_add_tail(&dev->global_list, &pci_devices);
     	spin_unlock(&pci_bus_lock);
    
    +	pci_pm_init(dev);
    +
     	pci_proc_attach_device(dev);
     	pci_create_sysfs_dev_files(dev);
     }
    diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
    index 6527b36..6d7afbc 100644
    --- a/drivers/pci/pci.h
    +++ b/drivers/pci/pci.h
    @@ -63,6 +63,23 @@ extern int pcie_mch_quirk;
     extern struct device_attribute pci_dev_attrs[];
     extern struct class_device_attribute class_device_attr_cpuaffinity;
    
    +
    +#ifdef CONFIG_PM
    +extern int pci_pm_init(struct pci_dev *);
    +extern void pci_pm_exit(struct pci_dev *);
    +#else /* CONFIG_PM */
    +static inline int pci_pm_init(struct pci_dev *)
    +{
    +	return 0;
    +}
    +
    +static inline void pci_pm_exit(struct pci_dev *)
    +{
    +
    +}
    +
    +#endif
    +
     /**
      * pci_match_one_device - Tell if a PCI device structure has a matching
      *                        PCI device id structure
    diff --git a/drivers/pci/pm.c b/drivers/pci/pm.c
    new file mode 100644
    index 0000000..ce476e4
    --- /dev/null
    +++ b/drivers/pci/pm.c
    @@ -0,0 +1,138 @@
    +/*
    + * drivers/pci/pm.c - Power management support for PCI devices
    + */
    +
    +#include <linux/pci.h>
    +
    +
    +static ssize_t pm_possible_states_show(struct device * d,
    +				       struct device_attribute * a,
    +				       char * buffer)
    +{
    +	struct pci_dev * dev = to_pci_dev(d);
    +	char * s = buffer;
    +
    +	s += sprintf(s, "d0");
    +	if (dev->poss_states[PCI_D1])
    +		s += sprintf(s, " d1");
    +	if (dev->poss_states[PCI_D2])
    +		s += sprintf(s, " d2");
    +	if (dev->poss_states[PCI_D3hot])
    +		s += sprintf(s, " d3");
    +	s += sprintf(s, "\n");
    +	return (s - buffer);
    +}
    +
    +static DEVICE_ATTR(pm_possible_states, 0444, pm_possible_states_show, NULL);
    +
    +
    +static ssize_t pm_state_show(struct device * d, struct device_attribute * a,
    +			     char * buffer)
    +{
    +	struct pci_dev * dev = to_pci_dev(d);
    +	const char * str;
    +
    +	switch (dev->current_state) {
    +	case PCI_D0:
    +		str = "d0";
    +		break;
    +	case PCI_D1:
    +		str = "d1";
    +		break;
    +	case PCI_D2:
    +		str = "d2";
    +		break;
    +	case PCI_D3hot:
    +		str = "d3";
    +		break;
    +	default:
    +		str = "d?";
    +		break;
    +	}
    +
    +	return sprintf(buffer, "%s\n", str);
    +}
    +
    +
    +static ssize_t pm_state_store(struct device * d, struct device_attribute * a,
    +			      const char * buffer, size_t len)
    +{
    +	struct pci_dev * dev = to_pci_dev(d);
    +	pci_power_t state;
    +	int ret;
    +
    +	if (!strnicmp(buffer, "d0", len))
    +		state = PCI_D0;
    +	else if (!strnicmp(buffer, "d1", len))
    +		state = PCI_D1;
    +	else if (!strnicmp(buffer, "d2", len))
    +		state = PCI_D2;
    +	else if (!strnicmp(buffer, "d3", len))
    +		state = PCI_D3hot;
    +	else
    +		return -EINVAL;
    +
    +	if (state == dev->current_state)
    +		return 0;
    +
    +	if (dev->poss_states[state])
    +		ret = pci_set_power_state(dev, state);
    +	else
    +		ret = -EINVAL;
    +
    +	return ret == 0 ? len : ret;
    +}
    +
    +static DEVICE_ATTR(pm_state, 0644, pm_state_show, pm_state_store);
    +
    +
    +static int find_states(struct pci_dev * dev)
    +{
    +	int cap;
    +	u16 pmc;
    +
    +
    +	/*
    +	 * Every device supports D0
    +	 */
    +	dev->poss_states[PCI_D0] = 1;
    +
    +	/*
    +	 * Check if the device has PM capabilties in the config space
    +	 */
    +	cap = pci_find_capability(dev, PCI_CAP_ID_PM);
    +	if (!cap)
    +		return -EIO;
    +
    +	/*
    +	 * If it supports PM capabilities, it will support D3
    +	 */
    +	dev->poss_states[PCI_D3hot] = 1;
    +
    +	/*
    +	 * Check D1 and D2 support
    +	 */
    +	pci_read_config_word(dev, cap + PCI_PM_PMC, &pmc);
    +	if (pmc & PCI_PM_CAP_D1)
    +		dev->poss_states[PCI_D1] = 1;
    +	if (pmc & PCI_PM_CAP_D2)
    +		dev->poss_states[PCI_D2] = 1;
    +	return 0;
    +}
    +
    +
    +int pci_pm_init(struct pci_dev * dev)
    +{
    +	if (find_states(dev))
    +		return 0;
    +
    +	device_create_file(&dev->dev, &dev_attr_pm_possible_states);
    +	return device_create_file(&dev->dev, &dev_attr_pm_state);
    +}
    +
    +void pci_pm_exit(struct pci_dev * dev)
    +{
    +	device_remove_file(&dev->dev, &dev_attr_pm_state);
    +	device_remove_file(&dev->dev, &dev_attr_pm_possible_states);
    +}
    +
    diff --git a/include/linux/pci.h b/include/linux/pci.h
    index de690ca..2600119 100644
    --- a/include/linux/pci.h
    +++ b/include/linux/pci.h
    @@ -106,6 +106,7 @@ struct pci_dev {
     					   this if your device has broken DMA
     					   or supports 64-bit transfers.  */
    
    +	u32		poss_states[4];
     	pci_power_t     current_state;  /* Current operating state. In ACPI-speak,
     					   this is D0-D3, D0 being fully functional,
     					   and D3 being off. */
    -
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at  http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at  http://www.tux.org/lkml/
    
    \
     
     \ /
      Last update: 2006-01-06 02:40    [from the cache]
    ©2003-2008