Commit | Line | Data |
---|---|---|
dc161b9f PP |
1 | /* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. |
2 | * | |
3 | * This program is free software; you can redistribute it and/or modify | |
4 | * it under the terms of the GNU General Public License version 2 and | |
5 | * only version 2 as published by the Free Software Foundation. | |
6 | * | |
7 | * This program is distributed in the hope that it will be useful, | |
8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | * GNU General Public License for more details. | |
11 | */ | |
12 | ||
13 | #include <linux/kernel.h> | |
14 | #include <linux/module.h> | |
15 | #include <linux/init.h> | |
16 | #include <linux/device.h> | |
17 | #include <linux/io.h> | |
18 | #include <linux/err.h> | |
19 | #include <linux/slab.h> | |
b5913d64 | 20 | #include <linux/pm_runtime.h> |
dc161b9f PP |
21 | #include <linux/coresight.h> |
22 | #include <linux/amba/bus.h> | |
db341d3d | 23 | #include <linux/clk.h> |
dc161b9f PP |
24 | |
25 | #include "coresight-priv.h" | |
26 | ||
27 | #define TPIU_SUPP_PORTSZ 0x000 | |
28 | #define TPIU_CURR_PORTSZ 0x004 | |
29 | #define TPIU_SUPP_TRIGMODES 0x100 | |
30 | #define TPIU_TRIG_CNTRVAL 0x104 | |
31 | #define TPIU_TRIG_MULT 0x108 | |
32 | #define TPIU_SUPP_TESTPATM 0x200 | |
33 | #define TPIU_CURR_TESTPATM 0x204 | |
34 | #define TPIU_TEST_PATREPCNTR 0x208 | |
35 | #define TPIU_FFSR 0x300 | |
36 | #define TPIU_FFCR 0x304 | |
37 | #define TPIU_FSYNC_CNTR 0x308 | |
38 | #define TPIU_EXTCTL_INPORT 0x400 | |
39 | #define TPIU_EXTCTL_OUTPORT 0x404 | |
40 | #define TPIU_ITTRFLINACK 0xee4 | |
41 | #define TPIU_ITTRFLIN 0xee8 | |
42 | #define TPIU_ITATBDATA0 0xeec | |
43 | #define TPIU_ITATBCTR2 0xef0 | |
44 | #define TPIU_ITATBCTR1 0xef4 | |
45 | #define TPIU_ITATBCTR0 0xef8 | |
46 | ||
47 | /** register definition **/ | |
48 | /* FFCR - 0x304 */ | |
49 | #define FFCR_FON_MAN BIT(6) | |
50 | ||
51 | /** | |
52 | * @base: memory mapped base address for this component. | |
53 | * @dev: the device entity associated to this component. | |
db341d3d | 54 | * @atclk: optional clock for the core parts of the TPIU. |
dc161b9f | 55 | * @csdev: component vitals needed by the framework. |
dc161b9f PP |
56 | */ |
57 | struct tpiu_drvdata { | |
58 | void __iomem *base; | |
59 | struct device *dev; | |
db341d3d | 60 | struct clk *atclk; |
dc161b9f | 61 | struct coresight_device *csdev; |
dc161b9f PP |
62 | }; |
63 | ||
64 | static void tpiu_enable_hw(struct tpiu_drvdata *drvdata) | |
65 | { | |
66 | CS_UNLOCK(drvdata->base); | |
67 | ||
68 | /* TODO: fill this up */ | |
69 | ||
70 | CS_LOCK(drvdata->base); | |
71 | } | |
72 | ||
73 | static int tpiu_enable(struct coresight_device *csdev) | |
74 | { | |
75 | struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); | |
dc161b9f | 76 | |
b5913d64 | 77 | pm_runtime_get_sync(csdev->dev.parent); |
dc161b9f PP |
78 | tpiu_enable_hw(drvdata); |
79 | ||
80 | dev_info(drvdata->dev, "TPIU enabled\n"); | |
81 | return 0; | |
82 | } | |
83 | ||
84 | static void tpiu_disable_hw(struct tpiu_drvdata *drvdata) | |
85 | { | |
86 | CS_UNLOCK(drvdata->base); | |
87 | ||
88 | /* Clear formatter controle reg. */ | |
89 | writel_relaxed(0x0, drvdata->base + TPIU_FFCR); | |
90 | /* Generate manual flush */ | |
91 | writel_relaxed(FFCR_FON_MAN, drvdata->base + TPIU_FFCR); | |
92 | ||
93 | CS_LOCK(drvdata->base); | |
94 | } | |
95 | ||
96 | static void tpiu_disable(struct coresight_device *csdev) | |
97 | { | |
98 | struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); | |
99 | ||
100 | tpiu_disable_hw(drvdata); | |
b5913d64 | 101 | pm_runtime_put(csdev->dev.parent); |
dc161b9f PP |
102 | |
103 | dev_info(drvdata->dev, "TPIU disabled\n"); | |
104 | } | |
105 | ||
106 | static const struct coresight_ops_sink tpiu_sink_ops = { | |
107 | .enable = tpiu_enable, | |
108 | .disable = tpiu_disable, | |
109 | }; | |
110 | ||
111 | static const struct coresight_ops tpiu_cs_ops = { | |
112 | .sink_ops = &tpiu_sink_ops, | |
113 | }; | |
114 | ||
115 | static int tpiu_probe(struct amba_device *adev, const struct amba_id *id) | |
116 | { | |
db341d3d | 117 | int ret; |
dc161b9f PP |
118 | void __iomem *base; |
119 | struct device *dev = &adev->dev; | |
120 | struct coresight_platform_data *pdata = NULL; | |
121 | struct tpiu_drvdata *drvdata; | |
122 | struct resource *res = &adev->res; | |
123 | struct coresight_desc *desc; | |
124 | struct device_node *np = adev->dev.of_node; | |
125 | ||
126 | if (np) { | |
127 | pdata = of_get_coresight_platform_data(dev, np); | |
128 | if (IS_ERR(pdata)) | |
129 | return PTR_ERR(pdata); | |
130 | adev->dev.platform_data = pdata; | |
131 | } | |
132 | ||
133 | drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); | |
134 | if (!drvdata) | |
135 | return -ENOMEM; | |
136 | ||
137 | drvdata->dev = &adev->dev; | |
db341d3d LW |
138 | drvdata->atclk = devm_clk_get(&adev->dev, "atclk"); /* optional */ |
139 | if (!IS_ERR(drvdata->atclk)) { | |
140 | ret = clk_prepare_enable(drvdata->atclk); | |
141 | if (ret) | |
142 | return ret; | |
143 | } | |
dc161b9f PP |
144 | dev_set_drvdata(dev, drvdata); |
145 | ||
146 | /* Validity for the resource is already checked by the AMBA core */ | |
147 | base = devm_ioremap_resource(dev, res); | |
148 | if (IS_ERR(base)) | |
149 | return PTR_ERR(base); | |
150 | ||
151 | drvdata->base = base; | |
152 | ||
dc161b9f PP |
153 | /* Disable tpiu to support older devices */ |
154 | tpiu_disable_hw(drvdata); | |
155 | ||
b5913d64 | 156 | pm_runtime_put(&adev->dev); |
dc161b9f PP |
157 | |
158 | desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); | |
159 | if (!desc) | |
160 | return -ENOMEM; | |
161 | ||
162 | desc->type = CORESIGHT_DEV_TYPE_SINK; | |
163 | desc->subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_PORT; | |
164 | desc->ops = &tpiu_cs_ops; | |
165 | desc->pdata = pdata; | |
166 | desc->dev = dev; | |
167 | drvdata->csdev = coresight_register(desc); | |
168 | if (IS_ERR(drvdata->csdev)) | |
169 | return PTR_ERR(drvdata->csdev); | |
170 | ||
171 | dev_info(dev, "TPIU initialized\n"); | |
172 | return 0; | |
173 | } | |
174 | ||
175 | static int tpiu_remove(struct amba_device *adev) | |
176 | { | |
177 | struct tpiu_drvdata *drvdata = amba_get_drvdata(adev); | |
178 | ||
179 | coresight_unregister(drvdata->csdev); | |
180 | return 0; | |
181 | } | |
182 | ||
db341d3d LW |
183 | #ifdef CONFIG_PM |
184 | static int tpiu_runtime_suspend(struct device *dev) | |
185 | { | |
186 | struct tpiu_drvdata *drvdata = dev_get_drvdata(dev); | |
187 | ||
188 | if (drvdata && !IS_ERR(drvdata->atclk)) | |
189 | clk_disable_unprepare(drvdata->atclk); | |
190 | ||
191 | return 0; | |
192 | } | |
193 | ||
194 | static int tpiu_runtime_resume(struct device *dev) | |
195 | { | |
196 | struct tpiu_drvdata *drvdata = dev_get_drvdata(dev); | |
197 | ||
198 | if (drvdata && !IS_ERR(drvdata->atclk)) | |
199 | clk_prepare_enable(drvdata->atclk); | |
200 | ||
201 | return 0; | |
202 | } | |
203 | #endif | |
204 | ||
205 | static const struct dev_pm_ops tpiu_dev_pm_ops = { | |
206 | SET_RUNTIME_PM_OPS(tpiu_runtime_suspend, tpiu_runtime_resume, NULL) | |
207 | }; | |
208 | ||
dc161b9f PP |
209 | static struct amba_id tpiu_ids[] = { |
210 | { | |
211 | .id = 0x0003b912, | |
212 | .mask = 0x0003ffff, | |
213 | }, | |
4339b699 LW |
214 | { |
215 | .id = 0x0004b912, | |
216 | .mask = 0x0007ffff, | |
217 | }, | |
dc161b9f PP |
218 | { 0, 0}, |
219 | }; | |
220 | ||
221 | static struct amba_driver tpiu_driver = { | |
222 | .drv = { | |
223 | .name = "coresight-tpiu", | |
224 | .owner = THIS_MODULE, | |
db341d3d | 225 | .pm = &tpiu_dev_pm_ops, |
dc161b9f PP |
226 | }, |
227 | .probe = tpiu_probe, | |
228 | .remove = tpiu_remove, | |
229 | .id_table = tpiu_ids, | |
230 | }; | |
231 | ||
3ff7ca05 | 232 | module_amba_driver(tpiu_driver); |
dc161b9f PP |
233 | |
234 | MODULE_LICENSE("GPL v2"); | |
235 | MODULE_DESCRIPTION("CoreSight Trace Port Interface Unit driver"); |