aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2014-02-10 18:35:53 -0500
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2014-02-10 18:35:53 -0500
commit1a8f83515c1646e134163f0ab310362fae49fcca (patch)
tree02aac0914cd9d048e76735424d3982086af92b6f /drivers/acpi
parent9cb32acf095e806e864c29d060dd79580fcd3d4f (diff)
ACPI / LPSS: Support for device latency tolerance PM QoS
Add a new routine, acpi_lpss_set_ltr(), for setting latency tolerance values for LPSS devices having LTR (Latency Tolerance Reporting) registers. Add .bind()/.unbind() callbacks to lpss_handler to set the LPSS devices' power.set_latency_tolerance callback pointers to acpi_lpss_set_ltr() during device addition and to clear them on device removal, respectively. That will cause the device latency tolerance PM QoS to work for the devices in question as documented. This changeset includes a fix from Mika Westerberg. Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/acpi')
-rw-r--r--drivers/acpi/acpi_lpss.c71
1 files changed, 70 insertions, 1 deletions
diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c
index 6745fe137b9e..0a68fc6b5800 100644
--- a/drivers/acpi/acpi_lpss.c
+++ b/drivers/acpi/acpi_lpss.c
@@ -33,6 +33,13 @@ ACPI_MODULE_NAME("acpi_lpss");
33#define LPSS_GENERAL_UART_RTS_OVRD BIT(3) 33#define LPSS_GENERAL_UART_RTS_OVRD BIT(3)
34#define LPSS_SW_LTR 0x10 34#define LPSS_SW_LTR 0x10
35#define LPSS_AUTO_LTR 0x14 35#define LPSS_AUTO_LTR 0x14
36#define LPSS_LTR_SNOOP_REQ BIT(15)
37#define LPSS_LTR_SNOOP_MASK 0x0000FFFF
38#define LPSS_LTR_SNOOP_LAT_1US 0x800
39#define LPSS_LTR_SNOOP_LAT_32US 0xC00
40#define LPSS_LTR_SNOOP_LAT_SHIFT 5
41#define LPSS_LTR_SNOOP_LAT_CUTOFF 3000
42#define LPSS_LTR_MAX_VAL 0x3FF
36#define LPSS_TX_INT 0x20 43#define LPSS_TX_INT 0x20
37#define LPSS_TX_INT_MASK BIT(1) 44#define LPSS_TX_INT_MASK BIT(1)
38 45
@@ -315,6 +322,17 @@ static int acpi_lpss_create_device(struct acpi_device *adev,
315 return ret; 322 return ret;
316} 323}
317 324
325static u32 __lpss_reg_read(struct lpss_private_data *pdata, unsigned int reg)
326{
327 return readl(pdata->mmio_base + pdata->dev_desc->prv_offset + reg);
328}
329
330static void __lpss_reg_write(u32 val, struct lpss_private_data *pdata,
331 unsigned int reg)
332{
333 writel(val, pdata->mmio_base + pdata->dev_desc->prv_offset + reg);
334}
335
318static int lpss_reg_read(struct device *dev, unsigned int reg, u32 *val) 336static int lpss_reg_read(struct device *dev, unsigned int reg, u32 *val)
319{ 337{
320 struct acpi_device *adev; 338 struct acpi_device *adev;
@@ -336,7 +354,7 @@ static int lpss_reg_read(struct device *dev, unsigned int reg, u32 *val)
336 ret = -ENODEV; 354 ret = -ENODEV;
337 goto out; 355 goto out;
338 } 356 }
339 *val = readl(pdata->mmio_base + pdata->dev_desc->prv_offset + reg); 357 *val = __lpss_reg_read(pdata, reg);
340 358
341 out: 359 out:
342 spin_unlock_irqrestore(&dev->power.lock, flags); 360 spin_unlock_irqrestore(&dev->power.lock, flags);
@@ -389,6 +407,37 @@ static struct attribute_group lpss_attr_group = {
389 .name = "lpss_ltr", 407 .name = "lpss_ltr",
390}; 408};
391 409
410static void acpi_lpss_set_ltr(struct device *dev, s32 val)
411{
412 struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
413 u32 ltr_mode, ltr_val;
414
415 ltr_mode = __lpss_reg_read(pdata, LPSS_GENERAL);
416 if (val < 0) {
417 if (ltr_mode & LPSS_GENERAL_LTR_MODE_SW) {
418 ltr_mode &= ~LPSS_GENERAL_LTR_MODE_SW;
419 __lpss_reg_write(ltr_mode, pdata, LPSS_GENERAL);
420 }
421 return;
422 }
423 ltr_val = __lpss_reg_read(pdata, LPSS_SW_LTR) & ~LPSS_LTR_SNOOP_MASK;
424 if (val >= LPSS_LTR_SNOOP_LAT_CUTOFF) {
425 ltr_val |= LPSS_LTR_SNOOP_LAT_32US;
426 val = LPSS_LTR_MAX_VAL;
427 } else if (val > LPSS_LTR_MAX_VAL) {
428 ltr_val |= LPSS_LTR_SNOOP_LAT_32US | LPSS_LTR_SNOOP_REQ;
429 val >>= LPSS_LTR_SNOOP_LAT_SHIFT;
430 } else {
431 ltr_val |= LPSS_LTR_SNOOP_LAT_1US | LPSS_LTR_SNOOP_REQ;
432 }
433 ltr_val |= val;
434 __lpss_reg_write(ltr_val, pdata, LPSS_SW_LTR);
435 if (!(ltr_mode & LPSS_GENERAL_LTR_MODE_SW)) {
436 ltr_mode |= LPSS_GENERAL_LTR_MODE_SW;
437 __lpss_reg_write(ltr_mode, pdata, LPSS_GENERAL);
438 }
439}
440
392static int acpi_lpss_platform_notify(struct notifier_block *nb, 441static int acpi_lpss_platform_notify(struct notifier_block *nb,
393 unsigned long action, void *data) 442 unsigned long action, void *data)
394{ 443{
@@ -426,9 +475,29 @@ static struct notifier_block acpi_lpss_nb = {
426 .notifier_call = acpi_lpss_platform_notify, 475 .notifier_call = acpi_lpss_platform_notify,
427}; 476};
428 477
478static void acpi_lpss_bind(struct device *dev)
479{
480 struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
481
482 if (!pdata || !pdata->mmio_base || !pdata->dev_desc->ltr_required)
483 return;
484
485 if (pdata->mmio_size >= pdata->dev_desc->prv_offset + LPSS_LTR_SIZE)
486 dev->power.set_latency_tolerance = acpi_lpss_set_ltr;
487 else
488 dev_err(dev, "MMIO size insufficient to access LTR\n");
489}
490
491static void acpi_lpss_unbind(struct device *dev)
492{
493 dev->power.set_latency_tolerance = NULL;
494}
495
429static struct acpi_scan_handler lpss_handler = { 496static struct acpi_scan_handler lpss_handler = {
430 .ids = acpi_lpss_device_ids, 497 .ids = acpi_lpss_device_ids,
431 .attach = acpi_lpss_create_device, 498 .attach = acpi_lpss_create_device,
499 .bind = acpi_lpss_bind,
500 .unbind = acpi_lpss_unbind,
432}; 501};
433 502
434void __init acpi_lpss_init(void) 503void __init acpi_lpss_init(void)