Commit 1a8f8351 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki

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: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent 9cb32acf
...@@ -33,6 +33,13 @@ ACPI_MODULE_NAME("acpi_lpss"); ...@@ -33,6 +33,13 @@ ACPI_MODULE_NAME("acpi_lpss");
#define LPSS_GENERAL_UART_RTS_OVRD BIT(3) #define LPSS_GENERAL_UART_RTS_OVRD BIT(3)
#define LPSS_SW_LTR 0x10 #define LPSS_SW_LTR 0x10
#define LPSS_AUTO_LTR 0x14 #define LPSS_AUTO_LTR 0x14
#define LPSS_LTR_SNOOP_REQ BIT(15)
#define LPSS_LTR_SNOOP_MASK 0x0000FFFF
#define LPSS_LTR_SNOOP_LAT_1US 0x800
#define LPSS_LTR_SNOOP_LAT_32US 0xC00
#define LPSS_LTR_SNOOP_LAT_SHIFT 5
#define LPSS_LTR_SNOOP_LAT_CUTOFF 3000
#define LPSS_LTR_MAX_VAL 0x3FF
#define LPSS_TX_INT 0x20 #define LPSS_TX_INT 0x20
#define LPSS_TX_INT_MASK BIT(1) #define LPSS_TX_INT_MASK BIT(1)
...@@ -315,6 +322,17 @@ static int acpi_lpss_create_device(struct acpi_device *adev, ...@@ -315,6 +322,17 @@ static int acpi_lpss_create_device(struct acpi_device *adev,
return ret; return ret;
} }
static u32 __lpss_reg_read(struct lpss_private_data *pdata, unsigned int reg)
{
return readl(pdata->mmio_base + pdata->dev_desc->prv_offset + reg);
}
static void __lpss_reg_write(u32 val, struct lpss_private_data *pdata,
unsigned int reg)
{
writel(val, pdata->mmio_base + pdata->dev_desc->prv_offset + reg);
}
static int lpss_reg_read(struct device *dev, unsigned int reg, u32 *val) static int lpss_reg_read(struct device *dev, unsigned int reg, u32 *val)
{ {
struct acpi_device *adev; struct acpi_device *adev;
...@@ -336,7 +354,7 @@ static int lpss_reg_read(struct device *dev, unsigned int reg, u32 *val) ...@@ -336,7 +354,7 @@ static int lpss_reg_read(struct device *dev, unsigned int reg, u32 *val)
ret = -ENODEV; ret = -ENODEV;
goto out; goto out;
} }
*val = readl(pdata->mmio_base + pdata->dev_desc->prv_offset + reg); *val = __lpss_reg_read(pdata, reg);
out: out:
spin_unlock_irqrestore(&dev->power.lock, flags); spin_unlock_irqrestore(&dev->power.lock, flags);
...@@ -389,6 +407,37 @@ static struct attribute_group lpss_attr_group = { ...@@ -389,6 +407,37 @@ static struct attribute_group lpss_attr_group = {
.name = "lpss_ltr", .name = "lpss_ltr",
}; };
static void acpi_lpss_set_ltr(struct device *dev, s32 val)
{
struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
u32 ltr_mode, ltr_val;
ltr_mode = __lpss_reg_read(pdata, LPSS_GENERAL);
if (val < 0) {
if (ltr_mode & LPSS_GENERAL_LTR_MODE_SW) {
ltr_mode &= ~LPSS_GENERAL_LTR_MODE_SW;
__lpss_reg_write(ltr_mode, pdata, LPSS_GENERAL);
}
return;
}
ltr_val = __lpss_reg_read(pdata, LPSS_SW_LTR) & ~LPSS_LTR_SNOOP_MASK;
if (val >= LPSS_LTR_SNOOP_LAT_CUTOFF) {
ltr_val |= LPSS_LTR_SNOOP_LAT_32US;
val = LPSS_LTR_MAX_VAL;
} else if (val > LPSS_LTR_MAX_VAL) {
ltr_val |= LPSS_LTR_SNOOP_LAT_32US | LPSS_LTR_SNOOP_REQ;
val >>= LPSS_LTR_SNOOP_LAT_SHIFT;
} else {
ltr_val |= LPSS_LTR_SNOOP_LAT_1US | LPSS_LTR_SNOOP_REQ;
}
ltr_val |= val;
__lpss_reg_write(ltr_val, pdata, LPSS_SW_LTR);
if (!(ltr_mode & LPSS_GENERAL_LTR_MODE_SW)) {
ltr_mode |= LPSS_GENERAL_LTR_MODE_SW;
__lpss_reg_write(ltr_mode, pdata, LPSS_GENERAL);
}
}
static int acpi_lpss_platform_notify(struct notifier_block *nb, static int acpi_lpss_platform_notify(struct notifier_block *nb,
unsigned long action, void *data) unsigned long action, void *data)
{ {
...@@ -426,9 +475,29 @@ static struct notifier_block acpi_lpss_nb = { ...@@ -426,9 +475,29 @@ static struct notifier_block acpi_lpss_nb = {
.notifier_call = acpi_lpss_platform_notify, .notifier_call = acpi_lpss_platform_notify,
}; };
static void acpi_lpss_bind(struct device *dev)
{
struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
if (!pdata || !pdata->mmio_base || !pdata->dev_desc->ltr_required)
return;
if (pdata->mmio_size >= pdata->dev_desc->prv_offset + LPSS_LTR_SIZE)
dev->power.set_latency_tolerance = acpi_lpss_set_ltr;
else
dev_err(dev, "MMIO size insufficient to access LTR\n");
}
static void acpi_lpss_unbind(struct device *dev)
{
dev->power.set_latency_tolerance = NULL;
}
static struct acpi_scan_handler lpss_handler = { static struct acpi_scan_handler lpss_handler = {
.ids = acpi_lpss_device_ids, .ids = acpi_lpss_device_ids,
.attach = acpi_lpss_create_device, .attach = acpi_lpss_create_device,
.bind = acpi_lpss_bind,
.unbind = acpi_lpss_unbind,
}; };
void __init acpi_lpss_init(void) void __init acpi_lpss_init(void)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment