aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/acpi_lpss.c
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-03-06 17:46:20 -0500
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-03-21 17:44:38 -0400
commitf58b082aed43400c03e53beacc50a9f9eb23ac91 (patch)
tree044ce964e1a619b33181f1d4c80504e7d6219844 /drivers/acpi/acpi_lpss.c
parentf6161aa153581da4a3867a2d1a7caf4be19b6ec9 (diff)
ACPI / scan: Add special handler for Intel Lynxpoint LPSS devices
Devices on the Intel Lynxpoint Low Power Subsystem (LPSS) have some common features that aren't shared with any other platform devices, including the clock and LTR (Latency Tolerance Reporting) registers. It is better to handle those features in common code than to bother device drivers with doing that (I/O functionality-wise the LPSS devices are generally compatible with other devices that don't have those special registers and may be handled by the same drivers). The clock registers of the LPSS devices are now taken care of by the special clk-x86-lpss driver, but the MMIO mappings used for accessing those registers can also be used for accessing the LTR registers on those devices (LTR support for the Lynxpoint LPSS is going to be added by a subsequent patch). Thus it is convenient to add a special ACPI scan handler for the Lynxpoint LPSS devices that will create the MMIO mappings for accessing the clock (and LTR in the future) registers and will register the LPSS devices' clocks, so the clk-x86-lpss driver will only need to take care of the main Lynxpoint LPSS clock. Introduce a special ACPI scan handler for Intel Lynxpoint LPSS devices as described above. This also reduces overhead related to browsing the ACPI namespace in search of the LPSS devices before the registration of their clocks, removes some LPSS-specific (and somewhat ugly) code from acpi_platform.c and shrinks the overall code size slightly. Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Acked-by: Mike Turquette <mturquette@linaro.org>
Diffstat (limited to 'drivers/acpi/acpi_lpss.c')
-rw-r--r--drivers/acpi/acpi_lpss.c163
1 files changed, 163 insertions, 0 deletions
diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c
new file mode 100644
index 000000000000..823df46a3deb
--- /dev/null
+++ b/drivers/acpi/acpi_lpss.c
@@ -0,0 +1,163 @@
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
22#include "internal.h"
23
24ACPI_MODULE_NAME("acpi_lpss");
25
26#define LPSS_CLK_OFFSET 0x800
27#define LPSS_CLK_SIZE 0x04
28
29struct lpss_device_desc {
30 bool clk_required;
31 const char *clk_parent;
32};
33
34struct lpss_private_data {
35 void __iomem *mmio_base;
36 resource_size_t mmio_size;
37 struct clk *clk;
38 const struct lpss_device_desc *dev_desc;
39};
40
41static struct lpss_device_desc lpt_dev_desc = {
42 .clk_required = true,
43 .clk_parent = "lpss_clk",
44};
45
46static const struct acpi_device_id acpi_lpss_device_ids[] = {
47 /* Lynxpoint LPSS devices */
48 { "INT33C0", (unsigned long)&lpt_dev_desc },
49 { "INT33C1", (unsigned long)&lpt_dev_desc },
50 { "INT33C2", (unsigned long)&lpt_dev_desc },
51 { "INT33C3", (unsigned long)&lpt_dev_desc },
52 { "INT33C4", (unsigned long)&lpt_dev_desc },
53 { "INT33C5", (unsigned long)&lpt_dev_desc },
54 { "INT33C6", },
55 { "INT33C7", },
56
57 { }
58};
59
60static int is_memory(struct acpi_resource *res, void *not_used)
61{
62 struct resource r;
63 return !acpi_dev_resource_memory(res, &r);
64}
65
66/* LPSS main clock device. */
67static struct platform_device *lpss_clk_dev;
68
69static inline void lpt_register_clock_device(void)
70{
71 lpss_clk_dev = platform_device_register_simple("clk-lpt", -1, NULL, 0);
72}
73
74static int register_device_clock(struct acpi_device *adev,
75 struct lpss_private_data *pdata)
76{
77 const struct lpss_device_desc *dev_desc = pdata->dev_desc;
78
79 if (!lpss_clk_dev)
80 lpt_register_clock_device();
81
82 if (!dev_desc->clk_parent || !pdata->mmio_base
83 || pdata->mmio_size < LPSS_CLK_OFFSET + LPSS_CLK_SIZE)
84 return -ENODATA;
85
86 pdata->clk = clk_register_gate(NULL, dev_name(&adev->dev),
87 dev_desc->clk_parent, 0,
88 pdata->mmio_base + LPSS_CLK_OFFSET,
89 0, 0, NULL);
90 if (IS_ERR(pdata->clk))
91 return PTR_ERR(pdata->clk);
92
93 clk_register_clkdev(pdata->clk, NULL, dev_name(&adev->dev));
94 return 0;
95}
96
97static int acpi_lpss_create_device(struct acpi_device *adev,
98 const struct acpi_device_id *id)
99{
100 struct lpss_device_desc *dev_desc;
101 struct lpss_private_data *pdata;
102 struct resource_list_entry *rentry;
103 struct list_head resource_list;
104 int ret;
105
106 dev_desc = (struct lpss_device_desc *)id->driver_data;
107 if (!dev_desc)
108 return acpi_create_platform_device(adev, id);
109
110 pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
111 if (!pdata)
112 return -ENOMEM;
113
114 INIT_LIST_HEAD(&resource_list);
115 ret = acpi_dev_get_resources(adev, &resource_list, is_memory, NULL);
116 if (ret < 0)
117 goto err_out;
118
119 list_for_each_entry(rentry, &resource_list, node)
120 if (resource_type(&rentry->res) == IORESOURCE_MEM) {
121 pdata->mmio_size = resource_size(&rentry->res);
122 pdata->mmio_base = ioremap(rentry->res.start,
123 pdata->mmio_size);
124 pdata->dev_desc = dev_desc;
125 break;
126 }
127
128 acpi_dev_free_resource_list(&resource_list);
129
130 if (dev_desc->clk_required) {
131 ret = register_device_clock(adev, pdata);
132 if (ret) {
133 /*
134 * Skip the device, but don't terminate the namespace
135 * scan.
136 */
137 ret = 0;
138 goto err_out;
139 }
140 }
141
142 adev->driver_data = pdata;
143 ret = acpi_create_platform_device(adev, id);
144 if (ret > 0)
145 return ret;
146
147 adev->driver_data = NULL;
148
149 err_out:
150 kfree(pdata);
151 return ret;
152}
153
154static struct acpi_scan_handler lpss_handler = {
155 .ids = acpi_lpss_device_ids,
156 .attach = acpi_lpss_create_device,
157};
158
159void __init acpi_lpss_init(void)
160{
161 if (!lpt_clk_init())
162 acpi_scan_add_handler(&lpss_handler);
163}