blob: 823df46a3debf9698fc17cd6514859b94e9b7b7b [file] [log] [blame]
Rafael J. Wysockif58b0822013-03-06 23:46:20 +01001/*
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}