diff --git a/drivers/dma/ioat_dma.c b/drivers/dma/ioat_dma.c index 43b8cefad2c..b0438c4f0c3 100644 --- a/drivers/dma/ioat_dma.c +++ b/drivers/dma/ioat_dma.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "ioatdma.h" #include "ioatdma_registers.h" #include "ioatdma_hw.h" @@ -171,8 +172,10 @@ static int ioat_dma_enumerate_channels(struct ioatdma_device *device) xfercap_scale = readb(device->reg_base + IOAT_XFERCAP_OFFSET); xfercap = (xfercap_scale == 0 ? -1 : (1UL << xfercap_scale)); -#if CONFIG_I7300_IDLE_IOAT_CHANNEL - device->common.chancnt--; +#ifdef CONFIG_I7300_IDLE_IOAT_CHANNEL + if (i7300_idle_platform_probe(NULL, NULL) == 0) { + device->common.chancnt--; + } #endif for (i = 0; i < device->common.chancnt; i++) { ioat_chan = kzalloc(sizeof(*ioat_chan), GFP_KERNEL); diff --git a/drivers/idle/Kconfig b/drivers/idle/Kconfig index f5b26dd579e..108264de0ac 100644 --- a/drivers/idle/Kconfig +++ b/drivers/idle/Kconfig @@ -5,12 +5,13 @@ config I7300_IDLE_IOAT_CHANNEL bool config I7300_IDLE - tristate "Intel chipset idle power saving driver" + tristate "Intel chipset idle memory power saving driver" select I7300_IDLE_IOAT_CHANNEL - depends on X86_64 + depends on X86_64 && EXPERIMENTAL help - Enable idle power savings with certain Intel server chipsets. - The chipset must have I/O AT support, such as the Intel 7300. - The power savings depends on the type and quantity of DRAM devices. + Enable memory power savings when idle with certain Intel server + chipsets. The chipset must have I/O AT support, such as the + Intel 7300. The power savings depends on the type and quantity of + DRAM devices. endmenu diff --git a/drivers/idle/i7300_idle.c b/drivers/idle/i7300_idle.c index 59d1bbc3cd3..fb176f6ef9f 100644 --- a/drivers/idle/i7300_idle.c +++ b/drivers/idle/i7300_idle.c @@ -25,6 +25,7 @@ #include #include #include +#include #include @@ -34,6 +35,8 @@ #define I7300_IDLE_DRIVER_VERSION "1.55" #define I7300_PRINT "i7300_idle:" +#define MAX_STOP_RETRIES 10 + static int debug; module_param_named(debug, debug, uint, 0644); MODULE_PARM_DESC(debug, "Enable debug printks in this driver"); @@ -46,12 +49,12 @@ MODULE_PARM_DESC(debug, "Enable debug printks in this driver"); * 0 = No throttling * 1 = Throttle when > 4 activations per eval window (Maximum throttling) * 2 = Throttle when > 8 activations - * 168 = Throttle when > 168 activations (Minimum throttling) + * 168 = Throttle when > 672 activations (Minimum throttling) */ -#define MAX_THRTLWLIMIT 168 -static uint i7300_idle_thrtlowlm = 1; -module_param_named(thrtlwlimit, i7300_idle_thrtlowlm, uint, 0644); -MODULE_PARM_DESC(thrtlwlimit, +#define MAX_THROTTLE_LOW_LIMIT 168 +static uint throttle_low_limit = 1; +module_param_named(throttle_low_limit, throttle_low_limit, uint, 0644); +MODULE_PARM_DESC(throttle_low_limit, "Value for THRTLOWLM activation field " "(0 = disable throttle, 1 = Max throttle, 168 = Min throttle)"); @@ -110,9 +113,9 @@ static int i7300_idle_ioat_start(void) static void i7300_idle_ioat_stop(void) { int i; - u8 sts; + u64 sts; - for (i = 0; i < 5; i++) { + for (i = 0; i < MAX_STOP_RETRIES; i++) { writeb(IOAT_CHANCMD_RESET, ioat_chanbase + IOAT1_CHANCMD_OFFSET); @@ -126,9 +129,10 @@ static void i7300_idle_ioat_stop(void) } - if (i == 5) - dprintk("failed to suspend+reset I/O AT after 5 retries\n"); - + if (i == MAX_STOP_RETRIES) { + dprintk("failed to stop I/O AT after %d retries\n", + MAX_STOP_RETRIES); + } } /* Test I/O AT by copying 1024 byte from 2k to 1k */ @@ -275,7 +279,7 @@ static void __exit i7300_idle_ioat_exit(void) i7300_idle_ioat_stop(); /* Wait for a while for the channel to halt before releasing */ - for (i = 0; i < 10; i++) { + for (i = 0; i < MAX_STOP_RETRIES; i++) { writeb(IOAT_CHANCMD_RESET, ioat_chanbase + IOAT1_CHANCMD_OFFSET); @@ -389,9 +393,9 @@ static void i7300_idle_start(void) new_ctl = i7300_idle_thrtctl_saved & ~DIMM_THRTCTL_THRMHUNT; pci_write_config_byte(fbd_dev, DIMM_THRTCTL, new_ctl); - limit = i7300_idle_thrtlowlm; - if (unlikely(limit > MAX_THRTLWLIMIT)) - limit = MAX_THRTLWLIMIT; + limit = throttle_low_limit; + if (unlikely(limit > MAX_THROTTLE_LOW_LIMIT)) + limit = MAX_THROTTLE_LOW_LIMIT; pci_write_config_byte(fbd_dev, DIMM_THRTLOW, limit); @@ -440,7 +444,7 @@ static int i7300_idle_notifier(struct notifier_block *nb, unsigned long val, static ktime_t idle_begin_time; static int time_init = 1; - if (!i7300_idle_thrtlowlm) + if (!throttle_low_limit) return 0; if (unlikely(time_init)) { @@ -505,77 +509,8 @@ static struct notifier_block i7300_idle_nb = { .notifier_call = i7300_idle_notifier, }; -/* - * I/O AT controls (PCI bus 0 device 8 function 0) - * DIMM controls (PCI bus 0 device 16 function 1) - */ -#define IOAT_BUS 0 -#define IOAT_DEVFN PCI_DEVFN(8, 0) -#define MEMCTL_BUS 0 -#define MEMCTL_DEVFN PCI_DEVFN(16, 1) - -struct fbd_ioat { - unsigned int vendor; - unsigned int ioat_dev; -}; - -/* - * The i5000 chip-set has the same hooks as the i7300 - * but support is disabled by default because this driver - * has not been validated on that platform. - */ -#define SUPPORT_I5000 0 - -static const struct fbd_ioat fbd_ioat_list[] = { - {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_CNB}, -#if SUPPORT_I5000 - {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT}, -#endif - {0, 0} -}; - -/* table of devices that work with this driver */ -static const struct pci_device_id pci_tbl[] = { - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_FBD_CNB) }, -#if SUPPORT_I5000 - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5000_ERR) }, -#endif - { } /* Terminating entry */ -}; - MODULE_DEVICE_TABLE(pci, pci_tbl); -/* Check for known platforms with I/O-AT */ -static int __init i7300_idle_platform_probe(void) -{ - int i; - - fbd_dev = pci_get_bus_and_slot(MEMCTL_BUS, MEMCTL_DEVFN); - if (!fbd_dev) - return -ENODEV; - - for (i = 0; pci_tbl[i].vendor != 0; i++) { - if (fbd_dev->vendor == pci_tbl[i].vendor && - fbd_dev->device == pci_tbl[i].device) { - break; - } - } - if (pci_tbl[i].vendor == 0) - return -ENODEV; - - ioat_dev = pci_get_bus_and_slot(IOAT_BUS, IOAT_DEVFN); - if (!ioat_dev) - return -ENODEV; - - for (i = 0; fbd_ioat_list[i].vendor != 0; i++) { - if (ioat_dev->vendor == fbd_ioat_list[i].vendor && - ioat_dev->device == fbd_ioat_list[i].ioat_dev) { - return 0; - } - } - return -ENODEV; -} - int stats_open_generic(struct inode *inode, struct file *fp) { fp->private_data = inode->i_private; @@ -617,7 +552,7 @@ static int __init i7300_idle_init(void) cpus_clear(idle_cpumask); total_us = 0; - if (i7300_idle_platform_probe()) + if (i7300_idle_platform_probe(&fbd_dev, &ioat_dev)) return -ENODEV; if (i7300_idle_thrt_save()) diff --git a/include/linux/i7300_idle.h b/include/linux/i7300_idle.h new file mode 100644 index 00000000000..05a80c44513 --- /dev/null +++ b/include/linux/i7300_idle.h @@ -0,0 +1,83 @@ + +#ifndef I7300_IDLE_H +#define I7300_IDLE_H + +#include + +/* + * I/O AT controls (PCI bus 0 device 8 function 0) + * DIMM controls (PCI bus 0 device 16 function 1) + */ +#define IOAT_BUS 0 +#define IOAT_DEVFN PCI_DEVFN(8, 0) +#define MEMCTL_BUS 0 +#define MEMCTL_DEVFN PCI_DEVFN(16, 1) + +struct fbd_ioat { + unsigned int vendor; + unsigned int ioat_dev; +}; + +/* + * The i5000 chip-set has the same hooks as the i7300 + * but support is disabled by default because this driver + * has not been validated on that platform. + */ +#define SUPPORT_I5000 0 + +static const struct fbd_ioat fbd_ioat_list[] = { + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_CNB}, +#if SUPPORT_I5000 + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT}, +#endif + {0, 0} +}; + +/* table of devices that work with this driver */ +static const struct pci_device_id pci_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_FBD_CNB) }, +#if SUPPORT_I5000 + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5000_ERR) }, +#endif + { } /* Terminating entry */ +}; + +/* Check for known platforms with I/O-AT */ +static inline int i7300_idle_platform_probe(struct pci_dev **fbd_dev, + struct pci_dev **ioat_dev) +{ + int i; + struct pci_dev *memdev, *dmadev; + + memdev = pci_get_bus_and_slot(MEMCTL_BUS, MEMCTL_DEVFN); + if (!memdev) + return -ENODEV; + + for (i = 0; pci_tbl[i].vendor != 0; i++) { + if (memdev->vendor == pci_tbl[i].vendor && + memdev->device == pci_tbl[i].device) { + break; + } + } + if (pci_tbl[i].vendor == 0) + return -ENODEV; + + dmadev = pci_get_bus_and_slot(IOAT_BUS, IOAT_DEVFN); + if (!dmadev) + return -ENODEV; + + for (i = 0; fbd_ioat_list[i].vendor != 0; i++) { + if (dmadev->vendor == fbd_ioat_list[i].vendor && + dmadev->device == fbd_ioat_list[i].ioat_dev) { + if (fbd_dev) + *fbd_dev = memdev; + if (ioat_dev) + *ioat_dev = dmadev; + + return 0; + } + } + return -ENODEV; +} + +#endif