]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge branch 'acpi-lpss'
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>
Mon, 11 Feb 2013 12:21:27 +0000 (13:21 +0100)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Mon, 11 Feb 2013 12:21:27 +0000 (13:21 +0100)
* acpi-lpss:
  ACPI / platform: create LPSS clocks if Lynxpoint devices are found during scan
  clk: x86: add support for Lynxpoint LPSS clocks
  x86: add support for Intel Low Power Subsystem
  ACPI / platform: fix comment about the platform device name
  ACPI: add support for CSRT table

arch/x86/Kconfig
drivers/acpi/Makefile
drivers/acpi/acpi_platform.c
drivers/acpi/csrt.c [new file with mode: 0644]
drivers/acpi/internal.h
drivers/acpi/scan.c
drivers/clk/Makefile
drivers/clk/x86/Makefile [new file with mode: 0644]
drivers/clk/x86/clk-lpss.c [new file with mode: 0644]
drivers/clk/x86/clk-lpss.h [new file with mode: 0644]
drivers/clk/x86/clk-lpt.c [new file with mode: 0644]

index 225543bf45a5ca551f9609b18cf8711f729f71e7..4f7c2da2f9f851a18c29249a2a5e0b16e0bf251a 100644 (file)
@@ -454,6 +454,16 @@ config X86_MDFLD
 
 endif
 
+config X86_INTEL_LPSS
+       bool "Intel Low Power Subsystem Support"
+       depends on ACPI
+       select COMMON_CLK
+       ---help---
+         Select to build support for Intel Low Power Subsystem such as
+         found on Intel Lynxpoint PCH. Selecting this option enables
+         things like clock tree (common clock framework) which are needed
+         by the LPSS peripheral drivers.
+
 config X86_RDC321X
        bool "RDC R-321x SoC"
        depends on X86_32
index 4ee2e753306a45a92055e3aaad246d9237abfece..474fcfeba66c45f2f8fb511c27e2e68f605b9988 100644 (file)
@@ -38,6 +38,7 @@ acpi-y                                += processor_core.o
 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-y                         += acpi_platform.o
 acpi-y                         += power.o
 acpi-y                         += event.o
index db129b9f52cba6c8634439fd2f753ec10bc1246e..2d1fb4c21605799d608e504962cacbc0df1bc655 100644 (file)
@@ -13,6 +13,7 @@
 
 #include <linux/acpi.h>
 #include <linux/device.h>
+#include <linux/err.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
 
 ACPI_MODULE_NAME("platform");
 
+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.
+ * @flags: ACPI_PLATFORM_* flags that affect the creation of the platform
+ *        devices.
  *
  * Check if the given @adev can be represented as a platform device and, if
  * that's the case, create and register a platform device, populate its common
  * resources and returns a pointer to it.  Otherwise, return %NULL.
  *
- * The platform device's name will be taken from the @adev's _HID and _UID.
+ * Name of the platform device will be the same as @adev's.
  */
-struct platform_device *acpi_create_platform_device(struct acpi_device *adev)
+struct platform_device *acpi_create_platform_device(struct acpi_device *adev,
+                                                   unsigned long flags)
 {
        struct platform_device *pdev = NULL;
        struct acpi_device *acpi_parent;
@@ -41,6 +59,11 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev)
        struct resource *resources;
        int count;
 
+       if ((flags & ACPI_PLATFORM_CLK) && acpi_create_platform_clks(adev)) {
+               dev_err(&adev->dev, "failed to create clocks\n");
+               return NULL;
+       }
+
        /* If the ACPI node already has a physical device attached, skip it. */
        if (adev->physical_node_count)
                return NULL;
diff --git a/drivers/acpi/csrt.c b/drivers/acpi/csrt.c
new file mode 100644 (file)
index 0000000..5c15a91
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * Support for Core System Resources Table (CSRT)
+ *
+ * Copyright (C) 2013, Intel Corporation
+ * Authors: Mika Westerberg <mika.westerberg@linux.intel.com>
+ *         Andy Shevchenko <andriy.shevchenko@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.
+ */
+
+#define pr_fmt(fmt) "ACPI: CSRT: " fmt
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sizes.h>
+
+ACPI_MODULE_NAME("CSRT");
+
+static int __init acpi_csrt_parse_shared_info(struct platform_device *pdev,
+                                             const struct acpi_csrt_group *grp)
+{
+       const struct acpi_csrt_shared_info *si;
+       struct resource res[3];
+       size_t nres;
+       int ret;
+
+       memset(res, 0, sizeof(res));
+       nres = 0;
+
+       si = (const struct acpi_csrt_shared_info *)&grp[1];
+       /*
+        * The peripherals that are listed on CSRT typically support only
+        * 32-bit addresses so we only use the low part of MMIO base for
+        * now.
+        */
+       if (!si->mmio_base_high && si->mmio_base_low) {
+               /*
+                * There is no size of the memory resource in shared_info
+                * so we assume that it is 4k here.
+                */
+               res[nres].start = si->mmio_base_low;
+               res[nres].end = res[0].start + SZ_4K - 1;
+               res[nres++].flags = IORESOURCE_MEM;
+       }
+
+       if (si->gsi_interrupt) {
+               int irq = acpi_register_gsi(NULL, si->gsi_interrupt,
+                                           si->interrupt_mode,
+                                           si->interrupt_polarity);
+               res[nres].start = irq;
+               res[nres].end = irq;
+               res[nres++].flags = IORESOURCE_IRQ;
+       }
+
+       if (si->base_request_line || si->num_handshake_signals) {
+               /*
+                * We pass the driver a DMA resource describing the range
+                * of request lines the device supports.
+                */
+               res[nres].start = si->base_request_line;
+               res[nres].end = res[nres].start + si->num_handshake_signals - 1;
+               res[nres++].flags = IORESOURCE_DMA;
+       }
+
+       ret = platform_device_add_resources(pdev, res, nres);
+       if (ret) {
+               if (si->gsi_interrupt)
+                       acpi_unregister_gsi(si->gsi_interrupt);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int __init
+acpi_csrt_parse_resource_group(const struct acpi_csrt_group *grp)
+{
+       struct platform_device *pdev;
+       char vendor[5], name[16];
+       int ret, i;
+
+       vendor[0] = grp->vendor_id;
+       vendor[1] = grp->vendor_id >> 8;
+       vendor[2] = grp->vendor_id >> 16;
+       vendor[3] = grp->vendor_id >> 24;
+       vendor[4] = '\0';
+
+       if (grp->shared_info_length != sizeof(struct acpi_csrt_shared_info))
+               return -ENODEV;
+
+       snprintf(name, sizeof(name), "%s%04X", vendor, grp->device_id);
+       pdev = platform_device_alloc(name, PLATFORM_DEVID_AUTO);
+       if (!pdev)
+               return -ENOMEM;
+
+       /* Add resources based on the shared info */
+       ret = acpi_csrt_parse_shared_info(pdev, grp);
+       if (ret)
+               goto fail;
+
+       ret = platform_device_add(pdev);
+       if (ret)
+               goto fail;
+
+       for (i = 0; i < pdev->num_resources; i++)
+               dev_dbg(&pdev->dev, "%pR\n", &pdev->resource[i]);
+
+       return 0;
+
+fail:
+       platform_device_put(pdev);
+       return ret;
+}
+
+/*
+ * CSRT or Core System Resources Table is a proprietary ACPI table
+ * introduced by Microsoft. This table can contain devices that are not in
+ * the system DSDT table. In particular DMA controllers might be described
+ * here.
+ *
+ * We present these devices as normal platform devices that don't have ACPI
+ * IDs or handle. The platform device name will be something like
+ * <VENDOR><DEVID>.<n>.auto for example: INTL9C06.0.auto.
+ */
+void __init acpi_csrt_init(void)
+{
+       struct acpi_csrt_group *grp, *end;
+       struct acpi_table_csrt *csrt;
+       acpi_status status;
+       int ret;
+
+       status = acpi_get_table(ACPI_SIG_CSRT, 0,
+                               (struct acpi_table_header **)&csrt);
+       if (ACPI_FAILURE(status)) {
+               if (status != AE_NOT_FOUND)
+                       pr_warn("failed to get the CSRT table\n");
+               return;
+       }
+
+       pr_debug("parsing CSRT table for devices\n");
+
+       grp = (struct acpi_csrt_group *)(csrt + 1);
+       end = (struct acpi_csrt_group *)((void *)csrt + csrt->header.length);
+
+       while (grp < end) {
+               ret = acpi_csrt_parse_resource_group(grp);
+               if (ret) {
+                       pr_warn("error in parsing resource group: %d\n", ret);
+                       return;
+               }
+
+               grp = (struct acpi_csrt_group *)((void *)grp + grp->length);
+       }
+}
index c5a61cd6c1a5200c5297f4f3260c23ee185140e7..da233477d26022157dd25a1ed1f28e99a261ea10 100644 (file)
@@ -26,6 +26,7 @@
 int init_acpi_device_notify(void);
 int acpi_scan_init(void);
 int acpi_sysfs_init(void);
+void acpi_csrt_init(void);
 
 #ifdef CONFIG_DEBUG_FS
 extern struct dentry *acpi_debugfs_dir;
@@ -117,6 +118,10 @@ static inline void suspend_nvs_restore(void) {}
   -------------------------------------------------------------------------- */
 struct platform_device;
 
-struct platform_device *acpi_create_platform_device(struct acpi_device *adev);
+/* Flags for acpi_create_platform_device */
+#define ACPI_PLATFORM_CLK      BIT(0)
+
+struct platform_device *acpi_create_platform_device(struct acpi_device *adev,
+                                                   unsigned long flags);
 
 #endif /* _ACPI_INTERNAL_H_ */
index cbf6e7729c3972813b1815fa2716b87bf86510b5..c4358716aadcca510a58ccfb5b0d554d159b917c 100644 (file)
@@ -38,14 +38,14 @@ static const struct acpi_device_id acpi_platform_device_ids[] = {
        { "PNP0D40" },
 
        /* Haswell LPSS devices */
-       { "INT33C0", 0 },
-       { "INT33C1", 0 },
-       { "INT33C2", 0 },
-       { "INT33C3", 0 },
-       { "INT33C4", 0 },
-       { "INT33C5", 0 },
-       { "INT33C6", 0 },
-       { "INT33C7", 0 },
+       { "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 },
 
        { }
 };
@@ -1578,6 +1578,7 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used,
 static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used,
                                          void *not_used, void **ret_not_used)
 {
+       const struct acpi_device_id *id;
        acpi_status status = AE_OK;
        struct acpi_device *device;
        unsigned long long sta_not_used;
@@ -1593,9 +1594,10 @@ static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used,
        if (acpi_bus_get_device(handle, &device))
                return AE_CTRL_DEPTH;
 
-       if (!acpi_match_device_ids(device, acpi_platform_device_ids)) {
+       id = __acpi_match_device(device, acpi_platform_device_ids);
+       if (id) {
                /* This is a known good platform device. */
-               acpi_create_platform_device(device);
+               acpi_create_platform_device(device, id->driver_data);
        } else if (device_attach(&device->dev) < 0) {
                status = AE_CTRL_DEPTH;
        }
@@ -1717,6 +1719,7 @@ int __init acpi_scan_init(void)
        }
 
        acpi_pci_root_init();
+       acpi_csrt_init();
 
        /*
         * Enumerate devices in the ACPI namespace.
index ee90e87e7675859e4ae80cf0587191732e8b6541..ee11460a970d767c6832e05664f240047d23cdfd 100644 (file)
@@ -22,6 +22,7 @@ obj-$(CONFIG_ARCH_U8500)      += ux500/
 obj-$(CONFIG_ARCH_VT8500)      += clk-vt8500.o
 obj-$(CONFIG_ARCH_SUNXI)       += clk-sunxi.o
 obj-$(CONFIG_ARCH_ZYNQ)                += clk-zynq.o
+obj-$(CONFIG_X86)              += x86/
 
 # Chip specific
 obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
diff --git a/drivers/clk/x86/Makefile b/drivers/clk/x86/Makefile
new file mode 100644 (file)
index 0000000..f9ba4fa
--- /dev/null
@@ -0,0 +1,2 @@
+clk-x86-lpss-objs              := clk-lpss.o 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
new file mode 100644 (file)
index 0000000..b5e229f
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * 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
new file mode 100644 (file)
index 0000000..e9460f4
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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
new file mode 100644 (file)
index 0000000..81298ae
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Intel Lynxpoint LPSS 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/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#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)
+{
+       struct clk *clk;
+
+       /* LPSS free running clock */
+       clk = clk_register_fixed_rate(&pdev->dev, "lpss_clk", NULL, CLK_IS_ROOT,
+                                     100000000);
+       if (IS_ERR(clk))
+               return PTR_ERR(clk);
+
+       /* 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;
+}
+
+static struct platform_driver lpt_clk_driver = {
+       .driver = {
+               .name = "clk-lpt",
+               .owner = THIS_MODULE,
+       },
+       .probe = lpt_clk_probe,
+};
+
+static int __init lpt_clk_init(void)
+{
+       return platform_driver_register(&lpt_clk_driver);
+}
+arch_initcall(lpt_clk_init);