aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-04-08 06:31:30 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-04-08 06:31:30 -0400
commit16b53e7e8542082ac628e9e69f6cdf725a576f81 (patch)
tree0b1d1a2f1881654ef0b2897aecaf2b853fb3a902 /drivers/acpi
parent408fb7f37bf8e76a22d6058cde7c1f0fc7fa7da9 (diff)
parentcf8df962aa830d05be1a8d5a9c7d2a67b2837b45 (diff)
Merge branch 'acpi-lpss' into linux-next
* acpi-lpss: ACPI / LPSS: make code less confusing for reader ACPI / LPSS: Add support for exposing LTR registers to user space ACPI / scan: Add special handler for Intel Lynxpoint LPSS devices
Diffstat (limited to 'drivers/acpi')
-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
5 files changed, 304 insertions, 38 deletions
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 474fcfeba66c..ecb743bf05a5 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -39,6 +39,7 @@ acpi-y += ec.o
39acpi-$(CONFIG_ACPI_DOCK) += dock.o 39acpi-$(CONFIG_ACPI_DOCK) += dock.o
40acpi-y += pci_root.o pci_link.o pci_irq.o 40acpi-y += pci_root.o pci_link.o pci_irq.o
41acpi-y += csrt.o 41acpi-y += csrt.o
42acpi-$(CONFIG_X86_INTEL_LPSS) += acpi_lpss.o
42acpi-y += acpi_platform.o 43acpi-y += acpi_platform.o
43acpi-y += power.o 44acpi-y += power.o
44acpi-y += event.o 45acpi-y += event.o
diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c
new file mode 100644
index 000000000000..b1c95422ce74
--- /dev/null
+++ b/drivers/acpi/acpi_lpss.c
@@ -0,0 +1,292 @@
1/*
2 * ACPI support for Intel Lynxpoint LPSS.
3 *
4 * Copyright (C) 2013, Intel Corporation
5 * Authors: Mika Westerberg <mika.westerberg@linux.intel.com>
6 * Rafael J. Wysocki <rafael.j.wysocki@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/io.h>
19#include <linux/platform_device.h>
20#include <linux/platform_data/clk-lpss.h>
21#include <linux/pm_runtime.h>
22
23#include "internal.h"
24
25ACPI_MODULE_NAME("acpi_lpss");
26
27#define LPSS_CLK_SIZE 0x04
28#define LPSS_LTR_SIZE 0x18
29
30/* Offsets relative to LPSS_PRIVATE_OFFSET */
31#define LPSS_GENERAL 0x08
32#define LPSS_GENERAL_LTR_MODE_SW BIT(2)
33#define LPSS_SW_LTR 0x10
34#define LPSS_AUTO_LTR 0x14
35
36struct lpss_device_desc {
37 bool clk_required;
38 const char *clk_parent;
39 bool ltr_required;
40 unsigned int prv_offset;
41};
42
43struct lpss_private_data {
44 void __iomem *mmio_base;
45 resource_size_t mmio_size;
46 struct clk *clk;
47 const struct lpss_device_desc *dev_desc;
48};
49
50static struct lpss_device_desc lpt_dev_desc = {
51 .clk_required = true,
52 .clk_parent = "lpss_clk",
53 .prv_offset = 0x800,
54 .ltr_required = true,
55};
56
57static struct lpss_device_desc lpt_sdio_dev_desc = {
58 .prv_offset = 0x1000,
59 .ltr_required = true,
60};
61
62static const struct acpi_device_id acpi_lpss_device_ids[] = {
63 /* Lynxpoint LPSS devices */
64 { "INT33C0", (unsigned long)&lpt_dev_desc },
65 { "INT33C1", (unsigned long)&lpt_dev_desc },
66 { "INT33C2", (unsigned long)&lpt_dev_desc },
67 { "INT33C3", (unsigned long)&lpt_dev_desc },
68 { "INT33C4", (unsigned long)&lpt_dev_desc },
69 { "INT33C5", (unsigned long)&lpt_dev_desc },
70 { "INT33C6", (unsigned long)&lpt_sdio_dev_desc },
71 { "INT33C7", },
72
73 { }
74};
75
76static int is_memory(struct acpi_resource *res, void *not_used)
77{
78 struct resource r;
79 return !acpi_dev_resource_memory(res, &r);
80}
81
82/* LPSS main clock device. */
83static struct platform_device *lpss_clk_dev;
84
85static inline void lpt_register_clock_device(void)
86{
87 lpss_clk_dev = platform_device_register_simple("clk-lpt", -1, NULL, 0);
88}
89
90static int register_device_clock(struct acpi_device *adev,
91 struct lpss_private_data *pdata)
92{
93 const struct lpss_device_desc *dev_desc = pdata->dev_desc;
94
95 if (!lpss_clk_dev)
96 lpt_register_clock_device();
97
98 if (!dev_desc->clk_parent || !pdata->mmio_base
99 || pdata->mmio_size < dev_desc->prv_offset + LPSS_CLK_SIZE)
100 return -ENODATA;
101
102 pdata->clk = clk_register_gate(NULL, dev_name(&adev->dev),
103 dev_desc->clk_parent, 0,
104 pdata->mmio_base + dev_desc->prv_offset,
105 0, 0, NULL);
106 if (IS_ERR(pdata->clk))
107 return PTR_ERR(pdata->clk);
108
109 clk_register_clkdev(pdata->clk, NULL, dev_name(&adev->dev));
110 return 0;
111}
112
113static int acpi_lpss_create_device(struct acpi_device *adev,
114 const struct acpi_device_id *id)
115{
116 struct lpss_device_desc *dev_desc;
117 struct lpss_private_data *pdata;
118 struct resource_list_entry *rentry;
119 struct list_head resource_list;
120 int ret;
121
122 dev_desc = (struct lpss_device_desc *)id->driver_data;
123 if (!dev_desc)
124 return acpi_create_platform_device(adev, id);
125
126 pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
127 if (!pdata)
128 return -ENOMEM;
129
130 INIT_LIST_HEAD(&resource_list);
131 ret = acpi_dev_get_resources(adev, &resource_list, is_memory, NULL);
132 if (ret < 0)
133 goto err_out;
134
135 list_for_each_entry(rentry, &resource_list, node)
136 if (resource_type(&rentry->res) == IORESOURCE_MEM) {
137 pdata->mmio_size = resource_size(&rentry->res);
138 pdata->mmio_base = ioremap(rentry->res.start,
139 pdata->mmio_size);
140 pdata->dev_desc = dev_desc;
141 break;
142 }
143
144 acpi_dev_free_resource_list(&resource_list);
145
146 if (dev_desc->clk_required) {
147 ret = register_device_clock(adev, pdata);
148 if (ret) {
149 /*
150 * Skip the device, but don't terminate the namespace
151 * scan.
152 */
153 kfree(pdata);
154 return 0;
155 }
156 }
157
158 adev->driver_data = pdata;
159 ret = acpi_create_platform_device(adev, id);
160 if (ret > 0)
161 return ret;
162
163 adev->driver_data = NULL;
164
165 err_out:
166 kfree(pdata);
167 return ret;
168}
169
170static int lpss_reg_read(struct device *dev, unsigned int reg, u32 *val)
171{
172 struct acpi_device *adev;
173 struct lpss_private_data *pdata;
174 unsigned long flags;
175 int ret;
176
177 ret = acpi_bus_get_device(ACPI_HANDLE(dev), &adev);
178 if (WARN_ON(ret))
179 return ret;
180
181 spin_lock_irqsave(&dev->power.lock, flags);
182 if (pm_runtime_suspended(dev)) {
183 ret = -EAGAIN;
184 goto out;
185 }
186 pdata = acpi_driver_data(adev);
187 if (WARN_ON(!pdata || !pdata->mmio_base)) {
188 ret = -ENODEV;
189 goto out;
190 }
191 *val = readl(pdata->mmio_base + pdata->dev_desc->prv_offset + reg);
192
193 out:
194 spin_unlock_irqrestore(&dev->power.lock, flags);
195 return ret;
196}
197
198static ssize_t lpss_ltr_show(struct device *dev, struct device_attribute *attr,
199 char *buf)
200{
201 u32 ltr_value = 0;
202 unsigned int reg;
203 int ret;
204
205 reg = strcmp(attr->attr.name, "auto_ltr") ? LPSS_SW_LTR : LPSS_AUTO_LTR;
206 ret = lpss_reg_read(dev, reg, &ltr_value);
207 if (ret)
208 return ret;
209
210 return snprintf(buf, PAGE_SIZE, "%08x\n", ltr_value);
211}
212
213static ssize_t lpss_ltr_mode_show(struct device *dev,
214 struct device_attribute *attr, char *buf)
215{
216 u32 ltr_mode = 0;
217 char *outstr;
218 int ret;
219
220 ret = lpss_reg_read(dev, LPSS_GENERAL, &ltr_mode);
221 if (ret)
222 return ret;
223
224 outstr = (ltr_mode & LPSS_GENERAL_LTR_MODE_SW) ? "sw" : "auto";
225 return sprintf(buf, "%s\n", outstr);
226}
227
228static DEVICE_ATTR(auto_ltr, S_IRUSR, lpss_ltr_show, NULL);
229static DEVICE_ATTR(sw_ltr, S_IRUSR, lpss_ltr_show, NULL);
230static DEVICE_ATTR(ltr_mode, S_IRUSR, lpss_ltr_mode_show, NULL);
231
232static struct attribute *lpss_attrs[] = {
233 &dev_attr_auto_ltr.attr,
234 &dev_attr_sw_ltr.attr,
235 &dev_attr_ltr_mode.attr,
236 NULL,
237};
238
239static struct attribute_group lpss_attr_group = {
240 .attrs = lpss_attrs,
241 .name = "lpss_ltr",
242};
243
244static int acpi_lpss_platform_notify(struct notifier_block *nb,
245 unsigned long action, void *data)
246{
247 struct platform_device *pdev = to_platform_device(data);
248 struct lpss_private_data *pdata;
249 struct acpi_device *adev;
250 const struct acpi_device_id *id;
251 int ret = 0;
252
253 id = acpi_match_device(acpi_lpss_device_ids, &pdev->dev);
254 if (!id || !id->driver_data)
255 return 0;
256
257 if (acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev))
258 return 0;
259
260 pdata = acpi_driver_data(adev);
261 if (!pdata || !pdata->mmio_base || !pdata->dev_desc->ltr_required)
262 return 0;
263
264 if (pdata->mmio_size < pdata->dev_desc->prv_offset + LPSS_LTR_SIZE) {
265 dev_err(&pdev->dev, "MMIO size insufficient to access LTR\n");
266 return 0;
267 }
268
269 if (action == BUS_NOTIFY_ADD_DEVICE)
270 ret = sysfs_create_group(&pdev->dev.kobj, &lpss_attr_group);
271 else if (action == BUS_NOTIFY_DEL_DEVICE)
272 sysfs_remove_group(&pdev->dev.kobj, &lpss_attr_group);
273
274 return ret;
275}
276
277static struct notifier_block acpi_lpss_nb = {
278 .notifier_call = acpi_lpss_platform_notify,
279};
280
281static struct acpi_scan_handler lpss_handler = {
282 .ids = acpi_lpss_device_ids,
283 .attach = acpi_lpss_create_device,
284};
285
286void __init acpi_lpss_init(void)
287{
288 if (!lpt_clk_init()) {
289 bus_register_notifier(&platform_bus_type, &acpi_lpss_nb);
290 acpi_scan_add_handler(&lpss_handler);
291 }
292}
diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c
index 26fce4b8a632..fafec5ddf17f 100644
--- a/drivers/acpi/acpi_platform.c
+++ b/drivers/acpi/acpi_platform.c
@@ -22,9 +22,6 @@
22 22
23ACPI_MODULE_NAME("platform"); 23ACPI_MODULE_NAME("platform");
24 24
25/* Flags for acpi_create_platform_device */
26#define ACPI_PLATFORM_CLK BIT(0)
27
28/* 25/*
29 * The following ACPI IDs are known to be suitable for representing as 26 * The following ACPI IDs are known to be suitable for representing as
30 * platform devices. 27 * platform devices.
@@ -33,33 +30,9 @@ static const struct acpi_device_id acpi_platform_device_ids[] = {
33 30
34 { "PNP0D40" }, 31 { "PNP0D40" },
35 32
36 /* Haswell LPSS devices */
37 { "INT33C0", ACPI_PLATFORM_CLK },
38 { "INT33C1", ACPI_PLATFORM_CLK },
39 { "INT33C2", ACPI_PLATFORM_CLK },
40 { "INT33C3", ACPI_PLATFORM_CLK },
41 { "INT33C4", ACPI_PLATFORM_CLK },
42 { "INT33C5", ACPI_PLATFORM_CLK },
43 { "INT33C6", ACPI_PLATFORM_CLK },
44 { "INT33C7", ACPI_PLATFORM_CLK },
45
46 { } 33 { }
47}; 34};
48 35
49static int acpi_create_platform_clks(struct acpi_device *adev)
50{
51 static struct platform_device *pdev;
52
53 /* Create Lynxpoint LPSS clocks */
54 if (!pdev && !strncmp(acpi_device_hid(adev), "INT33C", 6)) {
55 pdev = platform_device_register_simple("clk-lpt", -1, NULL, 0);
56 if (IS_ERR(pdev))
57 return PTR_ERR(pdev);
58 }
59
60 return 0;
61}
62
63/** 36/**
64 * acpi_create_platform_device - Create platform device for ACPI device node 37 * acpi_create_platform_device - Create platform device for ACPI device node
65 * @adev: ACPI device node to create a platform device for. 38 * @adev: ACPI device node to create a platform device for.
@@ -71,10 +44,9 @@ static int acpi_create_platform_clks(struct acpi_device *adev)
71 * 44 *
72 * Name of the platform device will be the same as @adev's. 45 * Name of the platform device will be the same as @adev's.
73 */ 46 */
74static int acpi_create_platform_device(struct acpi_device *adev, 47int acpi_create_platform_device(struct acpi_device *adev,
75 const struct acpi_device_id *id) 48 const struct acpi_device_id *id)
76{ 49{
77 unsigned long flags = id->driver_data;
78 struct platform_device *pdev = NULL; 50 struct platform_device *pdev = NULL;
79 struct acpi_device *acpi_parent; 51 struct acpi_device *acpi_parent;
80 struct platform_device_info pdevinfo; 52 struct platform_device_info pdevinfo;
@@ -83,14 +55,6 @@ static int acpi_create_platform_device(struct acpi_device *adev,
83 struct resource *resources; 55 struct resource *resources;
84 int count; 56 int count;
85 57
86 if (flags & ACPI_PLATFORM_CLK) {
87 int ret = acpi_create_platform_clks(adev);
88 if (ret) {
89 dev_err(&adev->dev, "failed to create clocks\n");
90 return ret;
91 }
92 }
93
94 /* If the ACPI node already has a physical device attached, skip it. */ 58 /* If the ACPI node already has a physical device attached, skip it. */
95 if (adev->physical_node_count) 59 if (adev->physical_node_count)
96 return 0; 60 return 0;
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index 7f094adfe05a..6f1afd9118c8 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -59,6 +59,11 @@ int acpi_debugfs_init(void);
59#else 59#else
60static inline void acpi_debugfs_init(void) { return; } 60static inline void acpi_debugfs_init(void) { return; }
61#endif 61#endif
62#ifdef CONFIG_X86_INTEL_LPSS
63void acpi_lpss_init(void);
64#else
65static inline void acpi_lpss_init(void) {}
66#endif
62 67
63/* -------------------------------------------------------------------------- 68/* --------------------------------------------------------------------------
64 Device Node Initialization / Removal 69 Device Node Initialization / Removal
@@ -142,4 +147,7 @@ static inline void suspend_nvs_restore(void) {}
142 -------------------------------------------------------------------------- */ 147 -------------------------------------------------------------------------- */
143struct platform_device; 148struct platform_device;
144 149
150int acpi_create_platform_device(struct acpi_device *adev,
151 const struct acpi_device_id *id);
152
145#endif /* _ACPI_INTERNAL_H_ */ 153#endif /* _ACPI_INTERNAL_H_ */
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 8cacc16af6e7..d7f3c8b66fcf 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -2041,6 +2041,7 @@ int __init acpi_scan_init(void)
2041 acpi_pci_root_init(); 2041 acpi_pci_root_init();
2042 acpi_pci_link_init(); 2042 acpi_pci_link_init();
2043 acpi_platform_init(); 2043 acpi_platform_init();
2044 acpi_lpss_init();
2044 acpi_csrt_init(); 2045 acpi_csrt_init();
2045 acpi_container_init(); 2046 acpi_container_init();
2046 acpi_pci_slot_init(); 2047 acpi_pci_slot_init();