diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2013-02-11 07:21:27 -0500 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2013-02-11 07:21:27 -0500 |
commit | 17b1639b30e080dc577cbffe7a08d063708ec789 (patch) | |
tree | 3185a9a556b032504e753a536e693a5edfc413c3 | |
parent | a9834cb205bf0d2b52da7f603f43b62a09f503b7 (diff) | |
parent | e375325ce55eb841ccda54a4472cf3b0139ea5f2 (diff) |
Merge branch 'acpi-lpss'
* 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
-rw-r--r-- | arch/x86/Kconfig | 10 | ||||
-rw-r--r-- | drivers/acpi/Makefile | 1 | ||||
-rw-r--r-- | drivers/acpi/acpi_platform.c | 27 | ||||
-rw-r--r-- | drivers/acpi/csrt.c | 159 | ||||
-rw-r--r-- | drivers/acpi/internal.h | 7 | ||||
-rw-r--r-- | drivers/acpi/scan.c | 23 | ||||
-rw-r--r-- | drivers/clk/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/x86/Makefile | 2 | ||||
-rw-r--r-- | drivers/clk/x86/clk-lpss.c | 99 | ||||
-rw-r--r-- | drivers/clk/x86/clk-lpss.h | 36 | ||||
-rw-r--r-- | drivers/clk/x86/clk-lpt.c | 86 |
11 files changed, 438 insertions, 13 deletions
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 225543bf45a5..4f7c2da2f9f8 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig | |||
@@ -454,6 +454,16 @@ config X86_MDFLD | |||
454 | 454 | ||
455 | endif | 455 | endif |
456 | 456 | ||
457 | config X86_INTEL_LPSS | ||
458 | bool "Intel Low Power Subsystem Support" | ||
459 | depends on ACPI | ||
460 | select COMMON_CLK | ||
461 | ---help--- | ||
462 | Select to build support for Intel Low Power Subsystem such as | ||
463 | found on Intel Lynxpoint PCH. Selecting this option enables | ||
464 | things like clock tree (common clock framework) which are needed | ||
465 | by the LPSS peripheral drivers. | ||
466 | |||
457 | config X86_RDC321X | 467 | config X86_RDC321X |
458 | bool "RDC R-321x SoC" | 468 | bool "RDC R-321x SoC" |
459 | depends on X86_32 | 469 | depends on X86_32 |
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 4ee2e753306a..474fcfeba66c 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile | |||
@@ -38,6 +38,7 @@ acpi-y += processor_core.o | |||
38 | acpi-y += ec.o | 38 | acpi-y += ec.o |
39 | acpi-$(CONFIG_ACPI_DOCK) += dock.o | 39 | acpi-$(CONFIG_ACPI_DOCK) += dock.o |
40 | acpi-y += pci_root.o pci_link.o pci_irq.o | 40 | acpi-y += pci_root.o pci_link.o pci_irq.o |
41 | acpi-y += csrt.o | ||
41 | acpi-y += acpi_platform.o | 42 | acpi-y += acpi_platform.o |
42 | acpi-y += power.o | 43 | acpi-y += power.o |
43 | acpi-y += event.o | 44 | acpi-y += event.o |
diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c index db129b9f52cb..2d1fb4c21605 100644 --- a/drivers/acpi/acpi_platform.c +++ b/drivers/acpi/acpi_platform.c | |||
@@ -13,6 +13,7 @@ | |||
13 | 13 | ||
14 | #include <linux/acpi.h> | 14 | #include <linux/acpi.h> |
15 | #include <linux/device.h> | 15 | #include <linux/device.h> |
16 | #include <linux/err.h> | ||
16 | #include <linux/kernel.h> | 17 | #include <linux/kernel.h> |
17 | #include <linux/module.h> | 18 | #include <linux/module.h> |
18 | #include <linux/platform_device.h> | 19 | #include <linux/platform_device.h> |
@@ -21,17 +22,34 @@ | |||
21 | 22 | ||
22 | ACPI_MODULE_NAME("platform"); | 23 | ACPI_MODULE_NAME("platform"); |
23 | 24 | ||
25 | static int acpi_create_platform_clks(struct acpi_device *adev) | ||
26 | { | ||
27 | static struct platform_device *pdev; | ||
28 | |||
29 | /* Create Lynxpoint LPSS clocks */ | ||
30 | if (!pdev && !strncmp(acpi_device_hid(adev), "INT33C", 6)) { | ||
31 | pdev = platform_device_register_simple("clk-lpt", -1, NULL, 0); | ||
32 | if (IS_ERR(pdev)) | ||
33 | return PTR_ERR(pdev); | ||
34 | } | ||
35 | |||
36 | return 0; | ||
37 | } | ||
38 | |||
24 | /** | 39 | /** |
25 | * acpi_create_platform_device - Create platform device for ACPI device node | 40 | * acpi_create_platform_device - Create platform device for ACPI device node |
26 | * @adev: ACPI device node to create a platform device for. | 41 | * @adev: ACPI device node to create a platform device for. |
42 | * @flags: ACPI_PLATFORM_* flags that affect the creation of the platform | ||
43 | * devices. | ||
27 | * | 44 | * |
28 | * Check if the given @adev can be represented as a platform device and, if | 45 | * Check if the given @adev can be represented as a platform device and, if |
29 | * that's the case, create and register a platform device, populate its common | 46 | * that's the case, create and register a platform device, populate its common |
30 | * resources and returns a pointer to it. Otherwise, return %NULL. | 47 | * resources and returns a pointer to it. Otherwise, return %NULL. |
31 | * | 48 | * |
32 | * The platform device's name will be taken from the @adev's _HID and _UID. | 49 | * Name of the platform device will be the same as @adev's. |
33 | */ | 50 | */ |
34 | struct platform_device *acpi_create_platform_device(struct acpi_device *adev) | 51 | struct platform_device *acpi_create_platform_device(struct acpi_device *adev, |
52 | unsigned long flags) | ||
35 | { | 53 | { |
36 | struct platform_device *pdev = NULL; | 54 | struct platform_device *pdev = NULL; |
37 | struct acpi_device *acpi_parent; | 55 | struct acpi_device *acpi_parent; |
@@ -41,6 +59,11 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev) | |||
41 | struct resource *resources; | 59 | struct resource *resources; |
42 | int count; | 60 | int count; |
43 | 61 | ||
62 | if ((flags & ACPI_PLATFORM_CLK) && acpi_create_platform_clks(adev)) { | ||
63 | dev_err(&adev->dev, "failed to create clocks\n"); | ||
64 | return NULL; | ||
65 | } | ||
66 | |||
44 | /* If the ACPI node already has a physical device attached, skip it. */ | 67 | /* If the ACPI node already has a physical device attached, skip it. */ |
45 | if (adev->physical_node_count) | 68 | if (adev->physical_node_count) |
46 | return NULL; | 69 | return NULL; |
diff --git a/drivers/acpi/csrt.c b/drivers/acpi/csrt.c new file mode 100644 index 000000000000..5c15a91faf0b --- /dev/null +++ b/drivers/acpi/csrt.c | |||
@@ -0,0 +1,159 @@ | |||
1 | /* | ||
2 | * Support for Core System Resources Table (CSRT) | ||
3 | * | ||
4 | * Copyright (C) 2013, Intel Corporation | ||
5 | * Authors: Mika Westerberg <mika.westerberg@linux.intel.com> | ||
6 | * Andy Shevchenko <andriy.shevchenko@linux.intel.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #define pr_fmt(fmt) "ACPI: CSRT: " fmt | ||
14 | |||
15 | #include <linux/acpi.h> | ||
16 | #include <linux/device.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | #include <linux/sizes.h> | ||
21 | |||
22 | ACPI_MODULE_NAME("CSRT"); | ||
23 | |||
24 | static int __init acpi_csrt_parse_shared_info(struct platform_device *pdev, | ||
25 | const struct acpi_csrt_group *grp) | ||
26 | { | ||
27 | const struct acpi_csrt_shared_info *si; | ||
28 | struct resource res[3]; | ||
29 | size_t nres; | ||
30 | int ret; | ||
31 | |||
32 | memset(res, 0, sizeof(res)); | ||
33 | nres = 0; | ||
34 | |||
35 | si = (const struct acpi_csrt_shared_info *)&grp[1]; | ||
36 | /* | ||
37 | * The peripherals that are listed on CSRT typically support only | ||
38 | * 32-bit addresses so we only use the low part of MMIO base for | ||
39 | * now. | ||
40 | */ | ||
41 | if (!si->mmio_base_high && si->mmio_base_low) { | ||
42 | /* | ||
43 | * There is no size of the memory resource in shared_info | ||
44 | * so we assume that it is 4k here. | ||
45 | */ | ||
46 | res[nres].start = si->mmio_base_low; | ||
47 | res[nres].end = res[0].start + SZ_4K - 1; | ||
48 | res[nres++].flags = IORESOURCE_MEM; | ||
49 | } | ||
50 | |||
51 | if (si->gsi_interrupt) { | ||
52 | int irq = acpi_register_gsi(NULL, si->gsi_interrupt, | ||
53 | si->interrupt_mode, | ||
54 | si->interrupt_polarity); | ||
55 | res[nres].start = irq; | ||
56 | res[nres].end = irq; | ||
57 | res[nres++].flags = IORESOURCE_IRQ; | ||
58 | } | ||
59 | |||
60 | if (si->base_request_line || si->num_handshake_signals) { | ||
61 | /* | ||
62 | * We pass the driver a DMA resource describing the range | ||
63 | * of request lines the device supports. | ||
64 | */ | ||
65 | res[nres].start = si->base_request_line; | ||
66 | res[nres].end = res[nres].start + si->num_handshake_signals - 1; | ||
67 | res[nres++].flags = IORESOURCE_DMA; | ||
68 | } | ||
69 | |||
70 | ret = platform_device_add_resources(pdev, res, nres); | ||
71 | if (ret) { | ||
72 | if (si->gsi_interrupt) | ||
73 | acpi_unregister_gsi(si->gsi_interrupt); | ||
74 | return ret; | ||
75 | } | ||
76 | |||
77 | return 0; | ||
78 | } | ||
79 | |||
80 | static int __init | ||
81 | acpi_csrt_parse_resource_group(const struct acpi_csrt_group *grp) | ||
82 | { | ||
83 | struct platform_device *pdev; | ||
84 | char vendor[5], name[16]; | ||
85 | int ret, i; | ||
86 | |||
87 | vendor[0] = grp->vendor_id; | ||
88 | vendor[1] = grp->vendor_id >> 8; | ||
89 | vendor[2] = grp->vendor_id >> 16; | ||
90 | vendor[3] = grp->vendor_id >> 24; | ||
91 | vendor[4] = '\0'; | ||
92 | |||
93 | if (grp->shared_info_length != sizeof(struct acpi_csrt_shared_info)) | ||
94 | return -ENODEV; | ||
95 | |||
96 | snprintf(name, sizeof(name), "%s%04X", vendor, grp->device_id); | ||
97 | pdev = platform_device_alloc(name, PLATFORM_DEVID_AUTO); | ||
98 | if (!pdev) | ||
99 | return -ENOMEM; | ||
100 | |||
101 | /* Add resources based on the shared info */ | ||
102 | ret = acpi_csrt_parse_shared_info(pdev, grp); | ||
103 | if (ret) | ||
104 | goto fail; | ||
105 | |||
106 | ret = platform_device_add(pdev); | ||
107 | if (ret) | ||
108 | goto fail; | ||
109 | |||
110 | for (i = 0; i < pdev->num_resources; i++) | ||
111 | dev_dbg(&pdev->dev, "%pR\n", &pdev->resource[i]); | ||
112 | |||
113 | return 0; | ||
114 | |||
115 | fail: | ||
116 | platform_device_put(pdev); | ||
117 | return ret; | ||
118 | } | ||
119 | |||
120 | /* | ||
121 | * CSRT or Core System Resources Table is a proprietary ACPI table | ||
122 | * introduced by Microsoft. This table can contain devices that are not in | ||
123 | * the system DSDT table. In particular DMA controllers might be described | ||
124 | * here. | ||
125 | * | ||
126 | * We present these devices as normal platform devices that don't have ACPI | ||
127 | * IDs or handle. The platform device name will be something like | ||
128 | * <VENDOR><DEVID>.<n>.auto for example: INTL9C06.0.auto. | ||
129 | */ | ||
130 | void __init acpi_csrt_init(void) | ||
131 | { | ||
132 | struct acpi_csrt_group *grp, *end; | ||
133 | struct acpi_table_csrt *csrt; | ||
134 | acpi_status status; | ||
135 | int ret; | ||
136 | |||
137 | status = acpi_get_table(ACPI_SIG_CSRT, 0, | ||
138 | (struct acpi_table_header **)&csrt); | ||
139 | if (ACPI_FAILURE(status)) { | ||
140 | if (status != AE_NOT_FOUND) | ||
141 | pr_warn("failed to get the CSRT table\n"); | ||
142 | return; | ||
143 | } | ||
144 | |||
145 | pr_debug("parsing CSRT table for devices\n"); | ||
146 | |||
147 | grp = (struct acpi_csrt_group *)(csrt + 1); | ||
148 | end = (struct acpi_csrt_group *)((void *)csrt + csrt->header.length); | ||
149 | |||
150 | while (grp < end) { | ||
151 | ret = acpi_csrt_parse_resource_group(grp); | ||
152 | if (ret) { | ||
153 | pr_warn("error in parsing resource group: %d\n", ret); | ||
154 | return; | ||
155 | } | ||
156 | |||
157 | grp = (struct acpi_csrt_group *)((void *)grp + grp->length); | ||
158 | } | ||
159 | } | ||
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index c5a61cd6c1a5..da233477d260 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h | |||
@@ -26,6 +26,7 @@ | |||
26 | int init_acpi_device_notify(void); | 26 | int init_acpi_device_notify(void); |
27 | int acpi_scan_init(void); | 27 | int acpi_scan_init(void); |
28 | int acpi_sysfs_init(void); | 28 | int acpi_sysfs_init(void); |
29 | void acpi_csrt_init(void); | ||
29 | 30 | ||
30 | #ifdef CONFIG_DEBUG_FS | 31 | #ifdef CONFIG_DEBUG_FS |
31 | extern struct dentry *acpi_debugfs_dir; | 32 | extern struct dentry *acpi_debugfs_dir; |
@@ -117,6 +118,10 @@ static inline void suspend_nvs_restore(void) {} | |||
117 | -------------------------------------------------------------------------- */ | 118 | -------------------------------------------------------------------------- */ |
118 | struct platform_device; | 119 | struct platform_device; |
119 | 120 | ||
120 | struct platform_device *acpi_create_platform_device(struct acpi_device *adev); | 121 | /* Flags for acpi_create_platform_device */ |
122 | #define ACPI_PLATFORM_CLK BIT(0) | ||
123 | |||
124 | struct platform_device *acpi_create_platform_device(struct acpi_device *adev, | ||
125 | unsigned long flags); | ||
121 | 126 | ||
122 | #endif /* _ACPI_INTERNAL_H_ */ | 127 | #endif /* _ACPI_INTERNAL_H_ */ |
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index cbf6e7729c39..c4358716aadc 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c | |||
@@ -38,14 +38,14 @@ static const struct acpi_device_id acpi_platform_device_ids[] = { | |||
38 | { "PNP0D40" }, | 38 | { "PNP0D40" }, |
39 | 39 | ||
40 | /* Haswell LPSS devices */ | 40 | /* Haswell LPSS devices */ |
41 | { "INT33C0", 0 }, | 41 | { "INT33C0", ACPI_PLATFORM_CLK }, |
42 | { "INT33C1", 0 }, | 42 | { "INT33C1", ACPI_PLATFORM_CLK }, |
43 | { "INT33C2", 0 }, | 43 | { "INT33C2", ACPI_PLATFORM_CLK }, |
44 | { "INT33C3", 0 }, | 44 | { "INT33C3", ACPI_PLATFORM_CLK }, |
45 | { "INT33C4", 0 }, | 45 | { "INT33C4", ACPI_PLATFORM_CLK }, |
46 | { "INT33C5", 0 }, | 46 | { "INT33C5", ACPI_PLATFORM_CLK }, |
47 | { "INT33C6", 0 }, | 47 | { "INT33C6", ACPI_PLATFORM_CLK }, |
48 | { "INT33C7", 0 }, | 48 | { "INT33C7", ACPI_PLATFORM_CLK }, |
49 | 49 | ||
50 | { } | 50 | { } |
51 | }; | 51 | }; |
@@ -1578,6 +1578,7 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, | |||
1578 | static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used, | 1578 | static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used, |
1579 | void *not_used, void **ret_not_used) | 1579 | void *not_used, void **ret_not_used) |
1580 | { | 1580 | { |
1581 | const struct acpi_device_id *id; | ||
1581 | acpi_status status = AE_OK; | 1582 | acpi_status status = AE_OK; |
1582 | struct acpi_device *device; | 1583 | struct acpi_device *device; |
1583 | unsigned long long sta_not_used; | 1584 | unsigned long long sta_not_used; |
@@ -1593,9 +1594,10 @@ static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used, | |||
1593 | if (acpi_bus_get_device(handle, &device)) | 1594 | if (acpi_bus_get_device(handle, &device)) |
1594 | return AE_CTRL_DEPTH; | 1595 | return AE_CTRL_DEPTH; |
1595 | 1596 | ||
1596 | if (!acpi_match_device_ids(device, acpi_platform_device_ids)) { | 1597 | id = __acpi_match_device(device, acpi_platform_device_ids); |
1598 | if (id) { | ||
1597 | /* This is a known good platform device. */ | 1599 | /* This is a known good platform device. */ |
1598 | acpi_create_platform_device(device); | 1600 | acpi_create_platform_device(device, id->driver_data); |
1599 | } else if (device_attach(&device->dev) < 0) { | 1601 | } else if (device_attach(&device->dev) < 0) { |
1600 | status = AE_CTRL_DEPTH; | 1602 | status = AE_CTRL_DEPTH; |
1601 | } | 1603 | } |
@@ -1717,6 +1719,7 @@ int __init acpi_scan_init(void) | |||
1717 | } | 1719 | } |
1718 | 1720 | ||
1719 | acpi_pci_root_init(); | 1721 | acpi_pci_root_init(); |
1722 | acpi_csrt_init(); | ||
1720 | 1723 | ||
1721 | /* | 1724 | /* |
1722 | * Enumerate devices in the ACPI namespace. | 1725 | * Enumerate devices in the ACPI namespace. |
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index ee90e87e7675..ee11460a970d 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile | |||
@@ -22,6 +22,7 @@ obj-$(CONFIG_ARCH_U8500) += ux500/ | |||
22 | obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o | 22 | obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o |
23 | obj-$(CONFIG_ARCH_SUNXI) += clk-sunxi.o | 23 | obj-$(CONFIG_ARCH_SUNXI) += clk-sunxi.o |
24 | obj-$(CONFIG_ARCH_ZYNQ) += clk-zynq.o | 24 | obj-$(CONFIG_ARCH_ZYNQ) += clk-zynq.o |
25 | obj-$(CONFIG_X86) += x86/ | ||
25 | 26 | ||
26 | # Chip specific | 27 | # Chip specific |
27 | obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o | 28 | obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o |
diff --git a/drivers/clk/x86/Makefile b/drivers/clk/x86/Makefile new file mode 100644 index 000000000000..f9ba4fab0ddc --- /dev/null +++ b/drivers/clk/x86/Makefile | |||
@@ -0,0 +1,2 @@ | |||
1 | clk-x86-lpss-objs := clk-lpss.o clk-lpt.o | ||
2 | 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 index 000000000000..b5e229f3c3d9 --- /dev/null +++ b/drivers/clk/x86/clk-lpss.c | |||
@@ -0,0 +1,99 @@ | |||
1 | /* | ||
2 | * Intel Low Power Subsystem clocks. | ||
3 | * | ||
4 | * Copyright (C) 2013, Intel Corporation | ||
5 | * Authors: Mika Westerberg <mika.westerberg@linux.intel.com> | ||
6 | * Heikki Krogerus <heikki.krogerus@linux.intel.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/acpi.h> | ||
14 | #include <linux/clk.h> | ||
15 | #include <linux/clk-provider.h> | ||
16 | #include <linux/err.h> | ||
17 | #include <linux/io.h> | ||
18 | #include <linux/module.h> | ||
19 | |||
20 | static int clk_lpss_is_mmio_resource(struct acpi_resource *res, void *data) | ||
21 | { | ||
22 | struct resource r; | ||
23 | return !acpi_dev_resource_memory(res, &r); | ||
24 | } | ||
25 | |||
26 | static acpi_status clk_lpss_find_mmio(acpi_handle handle, u32 level, | ||
27 | void *data, void **retval) | ||
28 | { | ||
29 | struct resource_list_entry *rentry; | ||
30 | struct list_head resource_list; | ||
31 | struct acpi_device *adev; | ||
32 | const char *uid = data; | ||
33 | int ret; | ||
34 | |||
35 | if (acpi_bus_get_device(handle, &adev)) | ||
36 | return AE_OK; | ||
37 | |||
38 | if (uid) { | ||
39 | if (!adev->pnp.unique_id) | ||
40 | return AE_OK; | ||
41 | if (strcmp(uid, adev->pnp.unique_id)) | ||
42 | return AE_OK; | ||
43 | } | ||
44 | |||
45 | INIT_LIST_HEAD(&resource_list); | ||
46 | ret = acpi_dev_get_resources(adev, &resource_list, | ||
47 | clk_lpss_is_mmio_resource, NULL); | ||
48 | if (ret < 0) | ||
49 | return AE_NO_MEMORY; | ||
50 | |||
51 | list_for_each_entry(rentry, &resource_list, node) | ||
52 | if (resource_type(&rentry->res) == IORESOURCE_MEM) { | ||
53 | *(struct resource *)retval = rentry->res; | ||
54 | break; | ||
55 | } | ||
56 | |||
57 | acpi_dev_free_resource_list(&resource_list); | ||
58 | return AE_OK; | ||
59 | } | ||
60 | |||
61 | /** | ||
62 | * clk_register_lpss_gate - register LPSS clock gate | ||
63 | * @name: name of this clock gate | ||
64 | * @parent_name: parent clock name | ||
65 | * @hid: ACPI _HID of the device | ||
66 | * @uid: ACPI _UID of the device (optional) | ||
67 | * @offset: LPSS PRV_CLOCK_PARAMS offset | ||
68 | * | ||
69 | * Creates and registers LPSS clock gate. | ||
70 | */ | ||
71 | struct clk *clk_register_lpss_gate(const char *name, const char *parent_name, | ||
72 | const char *hid, const char *uid, | ||
73 | unsigned offset) | ||
74 | { | ||
75 | struct resource res = { }; | ||
76 | void __iomem *mmio_base; | ||
77 | acpi_status status; | ||
78 | struct clk *clk; | ||
79 | |||
80 | /* | ||
81 | * First try to look the device and its mmio resource from the | ||
82 | * ACPI namespace. | ||
83 | */ | ||
84 | status = acpi_get_devices(hid, clk_lpss_find_mmio, (void *)uid, | ||
85 | (void **)&res); | ||
86 | if (ACPI_FAILURE(status) || !res.start) | ||
87 | return ERR_PTR(-ENODEV); | ||
88 | |||
89 | mmio_base = ioremap(res.start, resource_size(&res)); | ||
90 | if (!mmio_base) | ||
91 | return ERR_PTR(-ENOMEM); | ||
92 | |||
93 | clk = clk_register_gate(NULL, name, parent_name, 0, mmio_base + offset, | ||
94 | 0, 0, NULL); | ||
95 | if (IS_ERR(clk)) | ||
96 | iounmap(mmio_base); | ||
97 | |||
98 | return clk; | ||
99 | } | ||
diff --git a/drivers/clk/x86/clk-lpss.h b/drivers/clk/x86/clk-lpss.h new file mode 100644 index 000000000000..e9460f442297 --- /dev/null +++ b/drivers/clk/x86/clk-lpss.h | |||
@@ -0,0 +1,36 @@ | |||
1 | /* | ||
2 | * Intel Low Power Subsystem clock. | ||
3 | * | ||
4 | * Copyright (C) 2013, Intel Corporation | ||
5 | * Authors: Mika Westerberg <mika.westerberg@linux.intel.com> | ||
6 | * Heikki Krogerus <heikki.krogerus@linux.intel.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #ifndef __CLK_LPSS_H | ||
14 | #define __CLK_LPSS_H | ||
15 | |||
16 | #include <linux/err.h> | ||
17 | #include <linux/errno.h> | ||
18 | #include <linux/clk.h> | ||
19 | |||
20 | #ifdef CONFIG_ACPI | ||
21 | extern struct clk *clk_register_lpss_gate(const char *name, | ||
22 | const char *parent_name, | ||
23 | const char *hid, const char *uid, | ||
24 | unsigned offset); | ||
25 | #else | ||
26 | static inline struct clk *clk_register_lpss_gate(const char *name, | ||
27 | const char *parent_name, | ||
28 | const char *hid, | ||
29 | const char *uid, | ||
30 | unsigned offset) | ||
31 | { | ||
32 | return ERR_PTR(-ENODEV); | ||
33 | } | ||
34 | #endif | ||
35 | |||
36 | #endif /* __CLK_LPSS_H */ | ||
diff --git a/drivers/clk/x86/clk-lpt.c b/drivers/clk/x86/clk-lpt.c new file mode 100644 index 000000000000..81298aeef7e3 --- /dev/null +++ b/drivers/clk/x86/clk-lpt.c | |||
@@ -0,0 +1,86 @@ | |||
1 | /* | ||
2 | * Intel Lynxpoint LPSS clocks. | ||
3 | * | ||
4 | * Copyright (C) 2013, Intel Corporation | ||
5 | * Authors: Mika Westerberg <mika.westerberg@linux.intel.com> | ||
6 | * Heikki Krogerus <heikki.krogerus@linux.intel.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/acpi.h> | ||
14 | #include <linux/clk.h> | ||
15 | #include <linux/clkdev.h> | ||
16 | #include <linux/clk-provider.h> | ||
17 | #include <linux/err.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | |||
21 | #include "clk-lpss.h" | ||
22 | |||
23 | #define PRV_CLOCK_PARAMS 0x800 | ||
24 | |||
25 | static int lpt_clk_probe(struct platform_device *pdev) | ||
26 | { | ||
27 | struct clk *clk; | ||
28 | |||
29 | /* LPSS free running clock */ | ||
30 | clk = clk_register_fixed_rate(&pdev->dev, "lpss_clk", NULL, CLK_IS_ROOT, | ||
31 | 100000000); | ||
32 | if (IS_ERR(clk)) | ||
33 | return PTR_ERR(clk); | ||
34 | |||
35 | /* Shared DMA clock */ | ||
36 | clk_register_clkdev(clk, "hclk", "INTL9C60.0.auto"); | ||
37 | |||
38 | /* SPI clocks */ | ||
39 | clk = clk_register_lpss_gate("spi0_clk", "lpss_clk", "INT33C0", NULL, | ||
40 | PRV_CLOCK_PARAMS); | ||
41 | if (!IS_ERR(clk)) | ||
42 | clk_register_clkdev(clk, NULL, "INT33C0:00"); | ||
43 | |||
44 | clk = clk_register_lpss_gate("spi1_clk", "lpss_clk", "INT33C1", NULL, | ||
45 | PRV_CLOCK_PARAMS); | ||
46 | if (!IS_ERR(clk)) | ||
47 | clk_register_clkdev(clk, NULL, "INT33C1:00"); | ||
48 | |||
49 | /* I2C clocks */ | ||
50 | clk = clk_register_lpss_gate("i2c0_clk", "lpss_clk", "INT33C2", NULL, | ||
51 | PRV_CLOCK_PARAMS); | ||
52 | if (!IS_ERR(clk)) | ||
53 | clk_register_clkdev(clk, NULL, "INT33C2:00"); | ||
54 | |||
55 | clk = clk_register_lpss_gate("i2c1_clk", "lpss_clk", "INT33C3", NULL, | ||
56 | PRV_CLOCK_PARAMS); | ||
57 | if (!IS_ERR(clk)) | ||
58 | clk_register_clkdev(clk, NULL, "INT33C3:00"); | ||
59 | |||
60 | /* UART clocks */ | ||
61 | clk = clk_register_lpss_gate("uart0_clk", "lpss_clk", "INT33C4", NULL, | ||
62 | PRV_CLOCK_PARAMS); | ||
63 | if (!IS_ERR(clk)) | ||
64 | clk_register_clkdev(clk, NULL, "INT33C4:00"); | ||
65 | |||
66 | clk = clk_register_lpss_gate("uart1_clk", "lpss_clk", "INT33C5", NULL, | ||
67 | PRV_CLOCK_PARAMS); | ||
68 | if (!IS_ERR(clk)) | ||
69 | clk_register_clkdev(clk, NULL, "INT33C5:00"); | ||
70 | |||
71 | return 0; | ||
72 | } | ||
73 | |||
74 | static struct platform_driver lpt_clk_driver = { | ||
75 | .driver = { | ||
76 | .name = "clk-lpt", | ||
77 | .owner = THIS_MODULE, | ||
78 | }, | ||
79 | .probe = lpt_clk_probe, | ||
80 | }; | ||
81 | |||
82 | static int __init lpt_clk_init(void) | ||
83 | { | ||
84 | return platform_driver_register(&lpt_clk_driver); | ||
85 | } | ||
86 | arch_initcall(lpt_clk_init); | ||