summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/acpi/Makefile1
-rw-r--r--drivers/acpi/acpi_lpss.c292
-rw-r--r--drivers/acpi/acpi_platform.c40
-rw-r--r--drivers/acpi/internal.h8
-rw-r--r--drivers/acpi/scan.c1
-rw-r--r--drivers/clk/x86/Makefile2
-rw-r--r--drivers/clk/x86/clk-lpss.c99
-rw-r--r--drivers/clk/x86/clk-lpss.h36
-rw-r--r--drivers/clk/x86/clk-lpt.c40
9 files changed, 306 insertions, 213 deletions
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 474fcfe..ecb743b 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -39,6 +39,7 @@ acpi-y += ec.o
acpi-$(CONFIG_ACPI_DOCK) += dock.o
acpi-y += pci_root.o pci_link.o pci_irq.o
acpi-y += csrt.o
+acpi-$(CONFIG_X86_INTEL_LPSS) += acpi_lpss.o
acpi-y += acpi_platform.o
acpi-y += power.o
acpi-y += event.o
diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c
new file mode 100644
index 0000000..b1c9542
--- /dev/null
+++ b/drivers/acpi/acpi_lpss.c
@@ -0,0 +1,292 @@
+/*
+ * ACPI support for Intel Lynxpoint LPSS.
+ *
+ * Copyright (C) 2013, Intel Corporation
+ * Authors: Mika Westerberg <mika.westerberg@linux.intel.com>
+ * Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/acpi.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/clk-lpss.h>
+#include <linux/pm_runtime.h>
+
+#include "internal.h"
+
+ACPI_MODULE_NAME("acpi_lpss");
+
+#define LPSS_CLK_SIZE 0x04
+#define LPSS_LTR_SIZE 0x18
+
+/* Offsets relative to LPSS_PRIVATE_OFFSET */
+#define LPSS_GENERAL 0x08
+#define LPSS_GENERAL_LTR_MODE_SW BIT(2)
+#define LPSS_SW_LTR 0x10
+#define LPSS_AUTO_LTR 0x14
+
+struct lpss_device_desc {
+ bool clk_required;
+ const char *clk_parent;
+ bool ltr_required;
+ unsigned int prv_offset;
+};
+
+struct lpss_private_data {
+ void __iomem *mmio_base;
+ resource_size_t mmio_size;
+ struct clk *clk;
+ const struct lpss_device_desc *dev_desc;
+};
+
+static struct lpss_device_desc lpt_dev_desc = {
+ .clk_required = true,
+ .clk_parent = "lpss_clk",
+ .prv_offset = 0x800,
+ .ltr_required = true,
+};
+
+static struct lpss_device_desc lpt_sdio_dev_desc = {
+ .prv_offset = 0x1000,
+ .ltr_required = true,
+};
+
+static const struct acpi_device_id acpi_lpss_device_ids[] = {
+ /* Lynxpoint LPSS devices */
+ { "INT33C0", (unsigned long)&lpt_dev_desc },
+ { "INT33C1", (unsigned long)&lpt_dev_desc },
+ { "INT33C2", (unsigned long)&lpt_dev_desc },
+ { "INT33C3", (unsigned long)&lpt_dev_desc },
+ { "INT33C4", (unsigned long)&lpt_dev_desc },
+ { "INT33C5", (unsigned long)&lpt_dev_desc },
+ { "INT33C6", (unsigned long)&lpt_sdio_dev_desc },
+ { "INT33C7", },
+
+ { }
+};
+
+static int is_memory(struct acpi_resource *res, void *not_used)
+{
+ struct resource r;
+ return !acpi_dev_resource_memory(res, &r);
+}
+
+/* LPSS main clock device. */
+static struct platform_device *lpss_clk_dev;
+
+static inline void lpt_register_clock_device(void)
+{
+ lpss_clk_dev = platform_device_register_simple("clk-lpt", -1, NULL, 0);
+}
+
+static int register_device_clock(struct acpi_device *adev,
+ struct lpss_private_data *pdata)
+{
+ const struct lpss_device_desc *dev_desc = pdata->dev_desc;
+
+ if (!lpss_clk_dev)
+ lpt_register_clock_device();
+
+ if (!dev_desc->clk_parent || !pdata->mmio_base
+ || pdata->mmio_size < dev_desc->prv_offset + LPSS_CLK_SIZE)
+ return -ENODATA;
+
+ pdata->clk = clk_register_gate(NULL, dev_name(&adev->dev),
+ dev_desc->clk_parent, 0,
+ pdata->mmio_base + dev_desc->prv_offset,
+ 0, 0, NULL);
+ if (IS_ERR(pdata->clk))
+ return PTR_ERR(pdata->clk);
+
+ clk_register_clkdev(pdata->clk, NULL, dev_name(&adev->dev));
+ return 0;
+}
+
+static int acpi_lpss_create_device(struct acpi_device *adev,
+ const struct acpi_device_id *id)
+{
+ struct lpss_device_desc *dev_desc;
+ struct lpss_private_data *pdata;
+ struct resource_list_entry *rentry;
+ struct list_head resource_list;
+ int ret;
+
+ dev_desc = (struct lpss_device_desc *)id->driver_data;
+ if (!dev_desc)
+ return acpi_create_platform_device(adev, id);
+
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&resource_list);
+ ret = acpi_dev_get_resources(adev, &resource_list, is_memory, NULL);
+ if (ret < 0)
+ goto err_out;
+
+ list_for_each_entry(rentry, &resource_list, node)
+ if (resource_type(&rentry->res) == IORESOURCE_MEM) {
+ pdata->mmio_size = resource_size(&rentry->res);
+ pdata->mmio_base = ioremap(rentry->res.start,
+ pdata->mmio_size);
+ pdata->dev_desc = dev_desc;
+ break;
+ }
+
+ acpi_dev_free_resource_list(&resource_list);
+
+ if (dev_desc->clk_required) {
+ ret = register_device_clock(adev, pdata);
+ if (ret) {
+ /*
+ * Skip the device, but don't terminate the namespace
+ * scan.
+ */
+ kfree(pdata);
+ return 0;
+ }
+ }
+
+ adev->driver_data = pdata;
+ ret = acpi_create_platform_device(adev, id);
+ if (ret > 0)
+ return ret;
+
+ adev->driver_data = NULL;
+
+ err_out:
+ kfree(pdata);
+ return ret;
+}
+
+static int lpss_reg_read(struct device *dev, unsigned int reg, u32 *val)
+{
+ struct acpi_device *adev;
+ struct lpss_private_data *pdata;
+ unsigned long flags;
+ int ret;
+
+ ret = acpi_bus_get_device(ACPI_HANDLE(dev), &adev);
+ if (WARN_ON(ret))
+ return ret;
+
+ spin_lock_irqsave(&dev->power.lock, flags);
+ if (pm_runtime_suspended(dev)) {
+ ret = -EAGAIN;
+ goto out;
+ }
+ pdata = acpi_driver_data(adev);
+ if (WARN_ON(!pdata || !pdata->mmio_base)) {
+ ret = -ENODEV;
+ goto out;
+ }
+ *val = readl(pdata->mmio_base + pdata->dev_desc->prv_offset + reg);
+
+ out:
+ spin_unlock_irqrestore(&dev->power.lock, flags);
+ return ret;
+}
+
+static ssize_t lpss_ltr_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ u32 ltr_value = 0;
+ unsigned int reg;
+ int ret;
+
+ reg = strcmp(attr->attr.name, "auto_ltr") ? LPSS_SW_LTR : LPSS_AUTO_LTR;
+ ret = lpss_reg_read(dev, reg, &ltr_value);
+ if (ret)
+ return ret;
+
+ return snprintf(buf, PAGE_SIZE, "%08x\n", ltr_value);
+}
+
+static ssize_t lpss_ltr_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ u32 ltr_mode = 0;
+ char *outstr;
+ int ret;
+
+ ret = lpss_reg_read(dev, LPSS_GENERAL, &ltr_mode);
+ if (ret)
+ return ret;
+
+ outstr = (ltr_mode & LPSS_GENERAL_LTR_MODE_SW) ? "sw" : "auto";
+ return sprintf(buf, "%s\n", outstr);
+}
+
+static DEVICE_ATTR(auto_ltr, S_IRUSR, lpss_ltr_show, NULL);
+static DEVICE_ATTR(sw_ltr, S_IRUSR, lpss_ltr_show, NULL);
+static DEVICE_ATTR(ltr_mode, S_IRUSR, lpss_ltr_mode_show, NULL);
+
+static struct attribute *lpss_attrs[] = {
+ &dev_attr_auto_ltr.attr,
+ &dev_attr_sw_ltr.attr,
+ &dev_attr_ltr_mode.attr,
+ NULL,
+};
+
+static struct attribute_group lpss_attr_group = {
+ .attrs = lpss_attrs,
+ .name = "lpss_ltr",
+};
+
+static int acpi_lpss_platform_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct platform_device *pdev = to_platform_device(data);
+ struct lpss_private_data *pdata;
+ struct acpi_device *adev;
+ const struct acpi_device_id *id;
+ int ret = 0;
+
+ id = acpi_match_device(acpi_lpss_device_ids, &pdev->dev);
+ if (!id || !id->driver_data)
+ return 0;
+
+ if (acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev))
+ return 0;
+
+ pdata = acpi_driver_data(adev);
+ if (!pdata || !pdata->mmio_base || !pdata->dev_desc->ltr_required)
+ return 0;
+
+ if (pdata->mmio_size < pdata->dev_desc->prv_offset + LPSS_LTR_SIZE) {
+ dev_err(&pdev->dev, "MMIO size insufficient to access LTR\n");
+ return 0;
+ }
+
+ if (action == BUS_NOTIFY_ADD_DEVICE)
+ ret = sysfs_create_group(&pdev->dev.kobj, &lpss_attr_group);
+ else if (action == BUS_NOTIFY_DEL_DEVICE)
+ sysfs_remove_group(&pdev->dev.kobj, &lpss_attr_group);
+
+ return ret;
+}
+
+static struct notifier_block acpi_lpss_nb = {
+ .notifier_call = acpi_lpss_platform_notify,
+};
+
+static struct acpi_scan_handler lpss_handler = {
+ .ids = acpi_lpss_device_ids,
+ .attach = acpi_lpss_create_device,
+};
+
+void __init acpi_lpss_init(void)
+{
+ if (!lpt_clk_init()) {
+ bus_register_notifier(&platform_bus_type, &acpi_lpss_nb);
+ acpi_scan_add_handler(&lpss_handler);
+ }
+}
diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c
index 26fce4b..fafec5d 100644
--- a/drivers/acpi/acpi_platform.c
+++ b/drivers/acpi/acpi_platform.c
@@ -22,9 +22,6 @@
ACPI_MODULE_NAME("platform");
-/* Flags for acpi_create_platform_device */
-#define ACPI_PLATFORM_CLK BIT(0)
-
/*
* The following ACPI IDs are known to be suitable for representing as
* platform devices.
@@ -33,33 +30,9 @@ static const struct acpi_device_id acpi_platform_device_ids[] = {
{ "PNP0D40" },
- /* Haswell LPSS devices */
- { "INT33C0", ACPI_PLATFORM_CLK },
- { "INT33C1", ACPI_PLATFORM_CLK },
- { "INT33C2", ACPI_PLATFORM_CLK },
- { "INT33C3", ACPI_PLATFORM_CLK },
- { "INT33C4", ACPI_PLATFORM_CLK },
- { "INT33C5", ACPI_PLATFORM_CLK },
- { "INT33C6", ACPI_PLATFORM_CLK },
- { "INT33C7", ACPI_PLATFORM_CLK },
-
{ }
};
-static int acpi_create_platform_clks(struct acpi_device *adev)
-{
- static struct platform_device *pdev;
-
- /* Create Lynxpoint LPSS clocks */
- if (!pdev && !strncmp(acpi_device_hid(adev), "INT33C", 6)) {
- pdev = platform_device_register_simple("clk-lpt", -1, NULL, 0);
- if (IS_ERR(pdev))
- return PTR_ERR(pdev);
- }
-
- return 0;
-}
-
/**
* acpi_create_platform_device - Create platform device for ACPI device node
* @adev: ACPI device node to create a platform device for.
@@ -71,10 +44,9 @@ static int acpi_create_platform_clks(struct acpi_device *adev)
*
* Name of the platform device will be the same as @adev's.
*/
-static int acpi_create_platform_device(struct acpi_device *adev,
- const struct acpi_device_id *id)
+int acpi_create_platform_device(struct acpi_device *adev,
+ const struct acpi_device_id *id)
{
- unsigned long flags = id->driver_data;
struct platform_device *pdev = NULL;
struct acpi_device *acpi_parent;
struct platform_device_info pdevinfo;
@@ -83,14 +55,6 @@ static int acpi_create_platform_device(struct acpi_device *adev,
struct resource *resources;
int count;
- if (flags & ACPI_PLATFORM_CLK) {
- int ret = acpi_create_platform_clks(adev);
- if (ret) {
- dev_err(&adev->dev, "failed to create clocks\n");
- return ret;
- }
- }
-
/* If the ACPI node already has a physical device attached, skip it. */
if (adev->physical_node_count)
return 0;
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index 7f094ad..6f1afd9 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -59,6 +59,11 @@ int acpi_debugfs_init(void);
#else
static inline void acpi_debugfs_init(void) { return; }
#endif
+#ifdef CONFIG_X86_INTEL_LPSS
+void acpi_lpss_init(void);
+#else
+static inline void acpi_lpss_init(void) {}
+#endif
/* --------------------------------------------------------------------------
Device Node Initialization / Removal
@@ -142,4 +147,7 @@ static inline void suspend_nvs_restore(void) {}
-------------------------------------------------------------------------- */
struct platform_device;
+int acpi_create_platform_device(struct acpi_device *adev,
+ const struct acpi_device_id *id);
+
#endif /* _ACPI_INTERNAL_H_ */
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 8cacc16..d7f3c8b 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -2041,6 +2041,7 @@ int __init acpi_scan_init(void)
acpi_pci_root_init();
acpi_pci_link_init();
acpi_platform_init();
+ acpi_lpss_init();
acpi_csrt_init();
acpi_container_init();
acpi_pci_slot_init();
diff --git a/drivers/clk/x86/Makefile b/drivers/clk/x86/Makefile
index f9ba4fa..0478138 100644
--- a/drivers/clk/x86/Makefile
+++ b/drivers/clk/x86/Makefile
@@ -1,2 +1,2 @@
-clk-x86-lpss-objs := clk-lpss.o clk-lpt.o
+clk-x86-lpss-objs := clk-lpt.o
obj-$(CONFIG_X86_INTEL_LPSS) += clk-x86-lpss.o
diff --git a/drivers/clk/x86/clk-lpss.c b/drivers/clk/x86/clk-lpss.c
deleted file mode 100644
index b5e229f..0000000
--- a/drivers/clk/x86/clk-lpss.c
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Intel Low Power Subsystem clocks.
- *
- * Copyright (C) 2013, Intel Corporation
- * Authors: Mika Westerberg <mika.westerberg@linux.intel.com>
- * Heikki Krogerus <heikki.krogerus@linux.intel.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/acpi.h>
-#include <linux/clk.h>
-#include <linux/clk-provider.h>
-#include <linux/err.h>
-#include <linux/io.h>
-#include <linux/module.h>
-
-static int clk_lpss_is_mmio_resource(struct acpi_resource *res, void *data)
-{
- struct resource r;
- return !acpi_dev_resource_memory(res, &r);
-}
-
-static acpi_status clk_lpss_find_mmio(acpi_handle handle, u32 level,
- void *data, void **retval)
-{
- struct resource_list_entry *rentry;
- struct list_head resource_list;
- struct acpi_device *adev;
- const char *uid = data;
- int ret;
-
- if (acpi_bus_get_device(handle, &adev))
- return AE_OK;
-
- if (uid) {
- if (!adev->pnp.unique_id)
- return AE_OK;
- if (strcmp(uid, adev->pnp.unique_id))
- return AE_OK;
- }
-
- INIT_LIST_HEAD(&resource_list);
- ret = acpi_dev_get_resources(adev, &resource_list,
- clk_lpss_is_mmio_resource, NULL);
- if (ret < 0)
- return AE_NO_MEMORY;
-
- list_for_each_entry(rentry, &resource_list, node)
- if (resource_type(&rentry->res) == IORESOURCE_MEM) {
- *(struct resource *)retval = rentry->res;
- break;
- }
-
- acpi_dev_free_resource_list(&resource_list);
- return AE_OK;
-}
-
-/**
- * clk_register_lpss_gate - register LPSS clock gate
- * @name: name of this clock gate
- * @parent_name: parent clock name
- * @hid: ACPI _HID of the device
- * @uid: ACPI _UID of the device (optional)
- * @offset: LPSS PRV_CLOCK_PARAMS offset
- *
- * Creates and registers LPSS clock gate.
- */
-struct clk *clk_register_lpss_gate(const char *name, const char *parent_name,
- const char *hid, const char *uid,
- unsigned offset)
-{
- struct resource res = { };
- void __iomem *mmio_base;
- acpi_status status;
- struct clk *clk;
-
- /*
- * First try to look the device and its mmio resource from the
- * ACPI namespace.
- */
- status = acpi_get_devices(hid, clk_lpss_find_mmio, (void *)uid,
- (void **)&res);
- if (ACPI_FAILURE(status) || !res.start)
- return ERR_PTR(-ENODEV);
-
- mmio_base = ioremap(res.start, resource_size(&res));
- if (!mmio_base)
- return ERR_PTR(-ENOMEM);
-
- clk = clk_register_gate(NULL, name, parent_name, 0, mmio_base + offset,
- 0, 0, NULL);
- if (IS_ERR(clk))
- iounmap(mmio_base);
-
- return clk;
-}
diff --git a/drivers/clk/x86/clk-lpss.h b/drivers/clk/x86/clk-lpss.h
deleted file mode 100644
index e9460f4..0000000
--- a/drivers/clk/x86/clk-lpss.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Intel Low Power Subsystem clock.
- *
- * Copyright (C) 2013, Intel Corporation
- * Authors: Mika Westerberg <mika.westerberg@linux.intel.com>
- * Heikki Krogerus <heikki.krogerus@linux.intel.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef __CLK_LPSS_H
-#define __CLK_LPSS_H
-
-#include <linux/err.h>
-#include <linux/errno.h>
-#include <linux/clk.h>
-
-#ifdef CONFIG_ACPI
-extern struct clk *clk_register_lpss_gate(const char *name,
- const char *parent_name,
- const char *hid, const char *uid,
- unsigned offset);
-#else
-static inline struct clk *clk_register_lpss_gate(const char *name,
- const char *parent_name,
- const char *hid,
- const char *uid,
- unsigned offset)
-{
- return ERR_PTR(-ENODEV);
-}
-#endif
-
-#endif /* __CLK_LPSS_H */
diff --git a/drivers/clk/x86/clk-lpt.c b/drivers/clk/x86/clk-lpt.c
index 81298ae..5cf4f46 100644
--- a/drivers/clk/x86/clk-lpt.c
+++ b/drivers/clk/x86/clk-lpt.c
@@ -10,7 +10,6 @@
* published by the Free Software Foundation.
*/
-#include <linux/acpi.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
@@ -18,8 +17,6 @@
#include <linux/module.h>
#include <linux/platform_device.h>
-#include "clk-lpss.h"
-
#define PRV_CLOCK_PARAMS 0x800
static int lpt_clk_probe(struct platform_device *pdev)
@@ -34,40 +31,6 @@ static int lpt_clk_probe(struct platform_device *pdev)
/* Shared DMA clock */
clk_register_clkdev(clk, "hclk", "INTL9C60.0.auto");
-
- /* SPI clocks */
- clk = clk_register_lpss_gate("spi0_clk", "lpss_clk", "INT33C0", NULL,
- PRV_CLOCK_PARAMS);
- if (!IS_ERR(clk))
- clk_register_clkdev(clk, NULL, "INT33C0:00");
-
- clk = clk_register_lpss_gate("spi1_clk", "lpss_clk", "INT33C1", NULL,
- PRV_CLOCK_PARAMS);
- if (!IS_ERR(clk))
- clk_register_clkdev(clk, NULL, "INT33C1:00");
-
- /* I2C clocks */
- clk = clk_register_lpss_gate("i2c0_clk", "lpss_clk", "INT33C2", NULL,
- PRV_CLOCK_PARAMS);
- if (!IS_ERR(clk))
- clk_register_clkdev(clk, NULL, "INT33C2:00");
-
- clk = clk_register_lpss_gate("i2c1_clk", "lpss_clk", "INT33C3", NULL,
- PRV_CLOCK_PARAMS);
- if (!IS_ERR(clk))
- clk_register_clkdev(clk, NULL, "INT33C3:00");
-
- /* UART clocks */
- clk = clk_register_lpss_gate("uart0_clk", "lpss_clk", "INT33C4", NULL,
- PRV_CLOCK_PARAMS);
- if (!IS_ERR(clk))
- clk_register_clkdev(clk, NULL, "INT33C4:00");
-
- clk = clk_register_lpss_gate("uart1_clk", "lpss_clk", "INT33C5", NULL,
- PRV_CLOCK_PARAMS);
- if (!IS_ERR(clk))
- clk_register_clkdev(clk, NULL, "INT33C5:00");
-
return 0;
}
@@ -79,8 +42,7 @@ static struct platform_driver lpt_clk_driver = {
.probe = lpt_clk_probe,
};
-static int __init lpt_clk_init(void)
+int __init lpt_clk_init(void)
{
return platform_driver_register(&lpt_clk_driver);
}
-arch_initcall(lpt_clk_init);