blob: df0f4e98220410ca4a6ad4ed1f9ba9dae065bd45 [file] [log] [blame]
Saravana Kannaned84c522014-05-28 18:59:54 -07001/*
Saravana Kannan217b9b12014-08-07 20:04:45 -07002 * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
Saravana Kannaned84c522014-05-28 18:59:54 -07003 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#define pr_fmt(fmt) "bimc-bwmon: " fmt
15
16#include <linux/kernel.h>
17#include <linux/module.h>
18#include <linux/init.h>
19#include <linux/io.h>
20#include <linux/delay.h>
21#include <linux/err.h>
22#include <linux/errno.h>
23#include <linux/interrupt.h>
24#include <linux/platform_device.h>
25#include <linux/of.h>
Saravana Kannan75206c22014-09-22 17:42:57 -070026#include <linux/of_device.h>
Saravana Kannaned84c522014-05-28 18:59:54 -070027#include <linux/spinlock.h>
28#include "governor_bw_hwmon.h"
29
30#define GLB_INT_STATUS(m) ((m)->global_base + 0x100)
31#define GLB_INT_CLR(m) ((m)->global_base + 0x108)
32#define GLB_INT_EN(m) ((m)->global_base + 0x10C)
33#define MON_INT_STATUS(m) ((m)->base + 0x100)
34#define MON_INT_CLR(m) ((m)->base + 0x108)
35#define MON_INT_EN(m) ((m)->base + 0x10C)
36#define MON_EN(m) ((m)->base + 0x280)
37#define MON_CLEAR(m) ((m)->base + 0x284)
38#define MON_CNT(m) ((m)->base + 0x288)
39#define MON_THRES(m) ((m)->base + 0x290)
40#define MON_MASK(m) ((m)->base + 0x298)
41#define MON_MATCH(m) ((m)->base + 0x29C)
42
Saravana Kannan75206c22014-09-22 17:42:57 -070043struct bwmon_spec {
44 bool wrap_on_thres;
45 bool overflow;
46};
47
Saravana Kannaned84c522014-05-28 18:59:54 -070048struct bwmon {
49 void __iomem *base;
50 void __iomem *global_base;
51 unsigned int mport;
52 unsigned int irq;
Saravana Kannan75206c22014-09-22 17:42:57 -070053 const struct bwmon_spec *spec;
Saravana Kannaned84c522014-05-28 18:59:54 -070054 struct device *dev;
55 struct bw_hwmon hw;
56};
57
58#define to_bwmon(ptr) container_of(ptr, struct bwmon, hw)
59
60static DEFINE_SPINLOCK(glb_lock);
61static void mon_enable(struct bwmon *m)
62{
63 writel_relaxed(0x1, MON_EN(m));
64}
65
66static void mon_disable(struct bwmon *m)
67{
68 writel_relaxed(0x0, MON_EN(m));
69}
70
71static void mon_clear(struct bwmon *m)
72{
73 writel_relaxed(0x1, MON_CLEAR(m));
74 /*
75 * The counter clear and IRQ clear bits are not in the same 4KB
76 * region. So, we need to make sure the counter clear is completed
77 * before we try to clear the IRQ or do any other counter operations.
78 */
79 mb();
80}
81
82static void mon_irq_enable(struct bwmon *m)
83{
84 u32 val;
85
86 spin_lock(&glb_lock);
87 val = readl_relaxed(GLB_INT_EN(m));
88 val |= 1 << m->mport;
89 writel_relaxed(val, GLB_INT_EN(m));
90 spin_unlock(&glb_lock);
91
92 val = readl_relaxed(MON_INT_EN(m));
93 val |= 0x1;
94 writel_relaxed(val, MON_INT_EN(m));
95}
96
97static void mon_irq_disable(struct bwmon *m)
98{
99 u32 val;
100
101 spin_lock(&glb_lock);
102 val = readl_relaxed(GLB_INT_EN(m));
103 val &= ~(1 << m->mport);
104 writel_relaxed(val, GLB_INT_EN(m));
105 spin_unlock(&glb_lock);
106
107 val = readl_relaxed(MON_INT_EN(m));
108 val &= ~0x1;
109 writel_relaxed(val, MON_INT_EN(m));
110}
111
Saravana Kannan75206c22014-09-22 17:42:57 -0700112static unsigned int mon_irq_status(struct bwmon *m)
Saravana Kannaned84c522014-05-28 18:59:54 -0700113{
114 u32 mval;
115
116 mval = readl_relaxed(MON_INT_STATUS(m));
117
118 dev_dbg(m->dev, "IRQ status p:%x, g:%x\n", mval,
119 readl_relaxed(GLB_INT_STATUS(m)));
120
Saravana Kannan75206c22014-09-22 17:42:57 -0700121 return mval;
Saravana Kannaned84c522014-05-28 18:59:54 -0700122}
123
124static void mon_irq_clear(struct bwmon *m)
125{
Saravana Kannan75206c22014-09-22 17:42:57 -0700126 writel_relaxed(0x3, MON_INT_CLR(m));
Saravana Kannaned84c522014-05-28 18:59:54 -0700127 mb();
128 writel_relaxed(1 << m->mport, GLB_INT_CLR(m));
129 mb();
130}
131
132static void mon_set_limit(struct bwmon *m, u32 count)
133{
134 writel_relaxed(count, MON_THRES(m));
135 dev_dbg(m->dev, "Thres: %08x\n", count);
136}
137
138static u32 mon_get_limit(struct bwmon *m)
139{
140 return readl_relaxed(MON_THRES(m));
141}
142
Saravana Kannan75206c22014-09-22 17:42:57 -0700143#define THRES_HIT(status) (status & BIT(0))
144#define OVERFLOW(status) (status & BIT(1))
Saravana Kannaned84c522014-05-28 18:59:54 -0700145static unsigned long mon_get_count(struct bwmon *m)
146{
Saravana Kannan75206c22014-09-22 17:42:57 -0700147 unsigned long count, status;
Saravana Kannaned84c522014-05-28 18:59:54 -0700148
149 count = readl_relaxed(MON_CNT(m));
Saravana Kannan75206c22014-09-22 17:42:57 -0700150 status = mon_irq_status(m);
151
Saravana Kannaned84c522014-05-28 18:59:54 -0700152 dev_dbg(m->dev, "Counter: %08lx\n", count);
Saravana Kannan75206c22014-09-22 17:42:57 -0700153
154 if (OVERFLOW(status) && m->spec->overflow)
155 count += 0xFFFFFFFF;
156 if (THRES_HIT(status) && m->spec->wrap_on_thres)
Saravana Kannaned84c522014-05-28 18:59:54 -0700157 count += mon_get_limit(m);
Saravana Kannan75206c22014-09-22 17:42:57 -0700158
Saravana Kannaned84c522014-05-28 18:59:54 -0700159 dev_dbg(m->dev, "Actual Count: %08lx\n", count);
160
161 return count;
162}
163
164/* ********** CPUBW specific code ********** */
165
166/* Returns MBps of read/writes for the sampling window. */
167static unsigned int bytes_to_mbps(long long bytes, unsigned int us)
168{
169 bytes *= USEC_PER_SEC;
170 do_div(bytes, us);
171 bytes = DIV_ROUND_UP_ULL(bytes, SZ_1M);
172 return bytes;
173}
174
175static unsigned int mbps_to_bytes(unsigned long mbps, unsigned int ms,
176 unsigned int tolerance_percent)
177{
178 mbps *= (100 + tolerance_percent) * ms;
179 mbps /= 100;
180 mbps = DIV_ROUND_UP(mbps, MSEC_PER_SEC);
181 mbps *= SZ_1M;
182 return mbps;
183}
184
185static unsigned long meas_bw_and_set_irq(struct bw_hwmon *hw,
186 unsigned int tol, unsigned int us)
187{
188 unsigned long mbps;
189 u32 limit;
190 unsigned int sample_ms = hw->df->profile->polling_ms;
191 struct bwmon *m = to_bwmon(hw);
192
193 mon_disable(m);
194
195 mbps = mon_get_count(m);
196 mbps = bytes_to_mbps(mbps, us);
Saravana Kannan75206c22014-09-22 17:42:57 -0700197
Saravana Kannaned84c522014-05-28 18:59:54 -0700198 /*
Saravana Kannan75206c22014-09-22 17:42:57 -0700199 * If the counter wraps on thres, don't set the thres too low.
200 * Setting it too low runs the risk of the counter wrapping around
201 * multiple times before the IRQ is processed.
Saravana Kannaned84c522014-05-28 18:59:54 -0700202 */
Saravana Kannan75206c22014-09-22 17:42:57 -0700203 if (likely(!m->spec->wrap_on_thres))
204 limit = mbps_to_bytes(mbps, sample_ms, tol);
205 else
206 limit = mbps_to_bytes(max(mbps, 400UL), sample_ms, tol);
207
Saravana Kannaned84c522014-05-28 18:59:54 -0700208 mon_set_limit(m, limit);
209
210 mon_clear(m);
211 mon_irq_clear(m);
212 mon_enable(m);
213
214 dev_dbg(m->dev, "MBps = %lu\n", mbps);
215 return mbps;
216}
217
218static irqreturn_t bwmon_intr_handler(int irq, void *dev)
219{
220 struct bwmon *m = dev;
221 if (mon_irq_status(m)) {
222 update_bw_hwmon(&m->hw);
223 return IRQ_HANDLED;
224 }
225
226 return IRQ_NONE;
227}
228
229static int start_bw_hwmon(struct bw_hwmon *hw, unsigned long mbps)
230{
231 struct bwmon *m = to_bwmon(hw);
232 u32 limit;
233 int ret;
234
235 ret = request_threaded_irq(m->irq, NULL, bwmon_intr_handler,
236 IRQF_ONESHOT | IRQF_SHARED,
237 dev_name(m->dev), m);
238 if (ret) {
Saravana Kannancddae1b2014-08-07 19:38:02 -0700239 dev_err(m->dev, "Unable to register interrupt handler! (%d)\n",
240 ret);
Saravana Kannaned84c522014-05-28 18:59:54 -0700241 return ret;
242 }
243
244 mon_disable(m);
245
246 limit = mbps_to_bytes(mbps, hw->df->profile->polling_ms, 0);
247 mon_set_limit(m, limit);
248
249 mon_clear(m);
250 mon_irq_clear(m);
251 mon_irq_enable(m);
252 mon_enable(m);
253
254 return 0;
255}
256
257static void stop_bw_hwmon(struct bw_hwmon *hw)
258{
259 struct bwmon *m = to_bwmon(hw);
260
Saravana Kannaned84c522014-05-28 18:59:54 -0700261 free_irq(m->irq, m);
262 mon_disable(m);
263 mon_irq_disable(m);
264 mon_clear(m);
265 mon_irq_clear(m);
266}
267
Saravana Kannancddae1b2014-08-07 19:38:02 -0700268static int suspend_bw_hwmon(struct bw_hwmon *hw)
269{
270 struct bwmon *m = to_bwmon(hw);
271
Saravana Kannan217b9b12014-08-07 20:04:45 -0700272 free_irq(m->irq, m);
Saravana Kannancddae1b2014-08-07 19:38:02 -0700273 mon_disable(m);
274 mon_irq_disable(m);
275 mon_irq_clear(m);
276
277 return 0;
278}
279
280static int resume_bw_hwmon(struct bw_hwmon *hw)
281{
282 struct bwmon *m = to_bwmon(hw);
Saravana Kannan217b9b12014-08-07 20:04:45 -0700283 int ret;
Saravana Kannancddae1b2014-08-07 19:38:02 -0700284
285 mon_clear(m);
286 mon_irq_enable(m);
287 mon_enable(m);
Saravana Kannan217b9b12014-08-07 20:04:45 -0700288 ret = request_threaded_irq(m->irq, NULL, bwmon_intr_handler,
289 IRQF_ONESHOT | IRQF_SHARED,
290 dev_name(m->dev), m);
291 if (ret) {
292 dev_err(m->dev, "Unable to register interrupt handler! (%d)\n",
293 ret);
294 return ret;
295 }
Saravana Kannancddae1b2014-08-07 19:38:02 -0700296
297 return 0;
298}
299
Saravana Kannaned84c522014-05-28 18:59:54 -0700300/*************************************************************************/
301
Saravana Kannan75206c22014-09-22 17:42:57 -0700302static const struct bwmon_spec spec[] = {
303 { .wrap_on_thres = true, .overflow = false },
304 { .wrap_on_thres = false, .overflow = true },
305};
306
307static const struct of_device_id bimc_bwmon_match_table[] = {
308 { .compatible = "qcom,bimc-bwmon", .data = &spec[0] },
309 { .compatible = "qcom,bimc-bwmon2", .data = &spec[1] },
310 {}
311};
312
Saravana Kannaned84c522014-05-28 18:59:54 -0700313static int bimc_bwmon_driver_probe(struct platform_device *pdev)
314{
315 struct device *dev = &pdev->dev;
316 struct resource *res;
317 struct bwmon *m;
Saravana Kannan75206c22014-09-22 17:42:57 -0700318 const struct of_device_id *id;
Saravana Kannaned84c522014-05-28 18:59:54 -0700319 int ret;
320 u32 data;
321
322 m = devm_kzalloc(dev, sizeof(*m), GFP_KERNEL);
323 if (!m)
324 return -ENOMEM;
325 m->dev = dev;
326
327 ret = of_property_read_u32(dev->of_node, "qcom,mport", &data);
328 if (ret) {
329 dev_err(dev, "mport not found!\n");
330 return ret;
331 }
332 m->mport = data;
333
Saravana Kannan75206c22014-09-22 17:42:57 -0700334 id = of_match_device(bimc_bwmon_match_table, dev);
335 if (!id) {
336 dev_err(dev, "Unknown device type!\n");
337 return -ENODEV;
338 }
339 m->spec = id->data;
340
Saravana Kannaned84c522014-05-28 18:59:54 -0700341 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "base");
342 if (!res) {
343 dev_err(dev, "base not found!\n");
344 return -EINVAL;
345 }
346 m->base = devm_ioremap(dev, res->start, resource_size(res));
347 if (!m->base) {
348 dev_err(dev, "Unable map base!\n");
349 return -ENOMEM;
350 }
351
352 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "global_base");
353 if (!res) {
354 dev_err(dev, "global_base not found!\n");
355 return -EINVAL;
356 }
357 m->global_base = devm_ioremap(dev, res->start, resource_size(res));
358 if (!m->global_base) {
359 dev_err(dev, "Unable map global_base!\n");
360 return -ENOMEM;
361 }
362
363 m->irq = platform_get_irq(pdev, 0);
364 if (m->irq < 0) {
365 dev_err(dev, "Unable to get IRQ number\n");
366 return m->irq;
367 }
368
369 m->hw.of_node = of_parse_phandle(dev->of_node, "qcom,target-dev", 0);
370 if (!m->hw.of_node)
371 return -EINVAL;
372 m->hw.start_hwmon = &start_bw_hwmon;
373 m->hw.stop_hwmon = &stop_bw_hwmon;
Saravana Kannancddae1b2014-08-07 19:38:02 -0700374 m->hw.suspend_hwmon = &suspend_bw_hwmon;
375 m->hw.resume_hwmon = &resume_bw_hwmon;
Saravana Kannaned84c522014-05-28 18:59:54 -0700376 m->hw.meas_bw_and_set_irq = &meas_bw_and_set_irq;
377
378 ret = register_bw_hwmon(dev, &m->hw);
379 if (ret) {
380 dev_err(dev, "Dev BW hwmon registration failed\n");
381 return ret;
382 }
383
384 return 0;
385}
386
Saravana Kannaned84c522014-05-28 18:59:54 -0700387static struct platform_driver bimc_bwmon_driver = {
388 .probe = bimc_bwmon_driver_probe,
389 .driver = {
390 .name = "bimc-bwmon",
391 .of_match_table = bimc_bwmon_match_table,
392 },
393};
394
395module_platform_driver(bimc_bwmon_driver);
396MODULE_DESCRIPTION("BIMC bandwidth monitor driver");
397MODULE_LICENSE("GPL v2");