blob: 02b070d82f4be4405050a4a14fc2c9f0f5ba2aea [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
Hanumath Prasad12401692015-05-14 20:22:11 +053043/*
44 * Don't set the threshold lower than this value. This helps avoid
45 * threshold IRQs when the traffic is close to zero and even small
46 * changes can exceed the threshold percentage.
47 */
48#define FLOOR_MBPS 100UL
49
Saravana Kannan75206c22014-09-22 17:42:57 -070050struct bwmon_spec {
51 bool wrap_on_thres;
52 bool overflow;
53};
54
Saravana Kannaned84c522014-05-28 18:59:54 -070055struct bwmon {
56 void __iomem *base;
57 void __iomem *global_base;
58 unsigned int mport;
59 unsigned int irq;
Saravana Kannan75206c22014-09-22 17:42:57 -070060 const struct bwmon_spec *spec;
Saravana Kannaned84c522014-05-28 18:59:54 -070061 struct device *dev;
62 struct bw_hwmon hw;
63};
64
65#define to_bwmon(ptr) container_of(ptr, struct bwmon, hw)
66
67static DEFINE_SPINLOCK(glb_lock);
68static void mon_enable(struct bwmon *m)
69{
70 writel_relaxed(0x1, MON_EN(m));
71}
72
73static void mon_disable(struct bwmon *m)
74{
75 writel_relaxed(0x0, MON_EN(m));
76}
77
78static void mon_clear(struct bwmon *m)
79{
80 writel_relaxed(0x1, MON_CLEAR(m));
81 /*
82 * The counter clear and IRQ clear bits are not in the same 4KB
83 * region. So, we need to make sure the counter clear is completed
84 * before we try to clear the IRQ or do any other counter operations.
85 */
86 mb();
87}
88
89static void mon_irq_enable(struct bwmon *m)
90{
91 u32 val;
92
93 spin_lock(&glb_lock);
94 val = readl_relaxed(GLB_INT_EN(m));
95 val |= 1 << m->mport;
96 writel_relaxed(val, GLB_INT_EN(m));
97 spin_unlock(&glb_lock);
98
99 val = readl_relaxed(MON_INT_EN(m));
100 val |= 0x1;
101 writel_relaxed(val, MON_INT_EN(m));
102}
103
104static void mon_irq_disable(struct bwmon *m)
105{
106 u32 val;
107
108 spin_lock(&glb_lock);
109 val = readl_relaxed(GLB_INT_EN(m));
110 val &= ~(1 << m->mport);
111 writel_relaxed(val, GLB_INT_EN(m));
112 spin_unlock(&glb_lock);
113
114 val = readl_relaxed(MON_INT_EN(m));
115 val &= ~0x1;
116 writel_relaxed(val, MON_INT_EN(m));
117}
118
Saravana Kannan75206c22014-09-22 17:42:57 -0700119static unsigned int mon_irq_status(struct bwmon *m)
Saravana Kannaned84c522014-05-28 18:59:54 -0700120{
121 u32 mval;
122
123 mval = readl_relaxed(MON_INT_STATUS(m));
124
125 dev_dbg(m->dev, "IRQ status p:%x, g:%x\n", mval,
126 readl_relaxed(GLB_INT_STATUS(m)));
127
Saravana Kannan75206c22014-09-22 17:42:57 -0700128 return mval;
Saravana Kannaned84c522014-05-28 18:59:54 -0700129}
130
131static void mon_irq_clear(struct bwmon *m)
132{
Saravana Kannan75206c22014-09-22 17:42:57 -0700133 writel_relaxed(0x3, MON_INT_CLR(m));
Saravana Kannaned84c522014-05-28 18:59:54 -0700134 mb();
135 writel_relaxed(1 << m->mport, GLB_INT_CLR(m));
136 mb();
137}
138
139static void mon_set_limit(struct bwmon *m, u32 count)
140{
141 writel_relaxed(count, MON_THRES(m));
142 dev_dbg(m->dev, "Thres: %08x\n", count);
143}
144
145static u32 mon_get_limit(struct bwmon *m)
146{
147 return readl_relaxed(MON_THRES(m));
148}
149
Saravana Kannan75206c22014-09-22 17:42:57 -0700150#define THRES_HIT(status) (status & BIT(0))
151#define OVERFLOW(status) (status & BIT(1))
Saravana Kannaned84c522014-05-28 18:59:54 -0700152static unsigned long mon_get_count(struct bwmon *m)
153{
Saravana Kannan75206c22014-09-22 17:42:57 -0700154 unsigned long count, status;
Saravana Kannaned84c522014-05-28 18:59:54 -0700155
156 count = readl_relaxed(MON_CNT(m));
Saravana Kannan75206c22014-09-22 17:42:57 -0700157 status = mon_irq_status(m);
158
Saravana Kannaned84c522014-05-28 18:59:54 -0700159 dev_dbg(m->dev, "Counter: %08lx\n", count);
Saravana Kannan75206c22014-09-22 17:42:57 -0700160
161 if (OVERFLOW(status) && m->spec->overflow)
162 count += 0xFFFFFFFF;
163 if (THRES_HIT(status) && m->spec->wrap_on_thres)
Saravana Kannaned84c522014-05-28 18:59:54 -0700164 count += mon_get_limit(m);
Saravana Kannan75206c22014-09-22 17:42:57 -0700165
Saravana Kannaned84c522014-05-28 18:59:54 -0700166 dev_dbg(m->dev, "Actual Count: %08lx\n", count);
167
168 return count;
169}
170
171/* ********** CPUBW specific code ********** */
172
173/* Returns MBps of read/writes for the sampling window. */
174static unsigned int bytes_to_mbps(long long bytes, unsigned int us)
175{
176 bytes *= USEC_PER_SEC;
177 do_div(bytes, us);
178 bytes = DIV_ROUND_UP_ULL(bytes, SZ_1M);
179 return bytes;
180}
181
182static unsigned int mbps_to_bytes(unsigned long mbps, unsigned int ms,
183 unsigned int tolerance_percent)
184{
185 mbps *= (100 + tolerance_percent) * ms;
186 mbps /= 100;
187 mbps = DIV_ROUND_UP(mbps, MSEC_PER_SEC);
188 mbps *= SZ_1M;
189 return mbps;
190}
191
192static unsigned long meas_bw_and_set_irq(struct bw_hwmon *hw,
193 unsigned int tol, unsigned int us)
194{
195 unsigned long mbps;
196 u32 limit;
197 unsigned int sample_ms = hw->df->profile->polling_ms;
198 struct bwmon *m = to_bwmon(hw);
199
200 mon_disable(m);
201
202 mbps = mon_get_count(m);
203 mbps = bytes_to_mbps(mbps, us);
Saravana Kannan75206c22014-09-22 17:42:57 -0700204
Saravana Kannaned84c522014-05-28 18:59:54 -0700205 /*
Saravana Kannan75206c22014-09-22 17:42:57 -0700206 * If the counter wraps on thres, don't set the thres too low.
207 * Setting it too low runs the risk of the counter wrapping around
208 * multiple times before the IRQ is processed.
Saravana Kannaned84c522014-05-28 18:59:54 -0700209 */
Saravana Kannan75206c22014-09-22 17:42:57 -0700210 if (likely(!m->spec->wrap_on_thres))
Hanumath Prasad12401692015-05-14 20:22:11 +0530211 limit = mbps_to_bytes(max(mbps, FLOOR_MBPS), sample_ms, tol);
Saravana Kannan75206c22014-09-22 17:42:57 -0700212 else
213 limit = mbps_to_bytes(max(mbps, 400UL), sample_ms, tol);
214
Saravana Kannaned84c522014-05-28 18:59:54 -0700215 mon_set_limit(m, limit);
216
217 mon_clear(m);
218 mon_irq_clear(m);
219 mon_enable(m);
220
221 dev_dbg(m->dev, "MBps = %lu\n", mbps);
222 return mbps;
223}
224
225static irqreturn_t bwmon_intr_handler(int irq, void *dev)
226{
227 struct bwmon *m = dev;
228 if (mon_irq_status(m)) {
229 update_bw_hwmon(&m->hw);
230 return IRQ_HANDLED;
231 }
232
233 return IRQ_NONE;
234}
235
236static int start_bw_hwmon(struct bw_hwmon *hw, unsigned long mbps)
237{
238 struct bwmon *m = to_bwmon(hw);
239 u32 limit;
240 int ret;
241
242 ret = request_threaded_irq(m->irq, NULL, bwmon_intr_handler,
243 IRQF_ONESHOT | IRQF_SHARED,
244 dev_name(m->dev), m);
245 if (ret) {
Saravana Kannancddae1b2014-08-07 19:38:02 -0700246 dev_err(m->dev, "Unable to register interrupt handler! (%d)\n",
247 ret);
Saravana Kannaned84c522014-05-28 18:59:54 -0700248 return ret;
249 }
250
251 mon_disable(m);
252
253 limit = mbps_to_bytes(mbps, hw->df->profile->polling_ms, 0);
254 mon_set_limit(m, limit);
255
256 mon_clear(m);
257 mon_irq_clear(m);
258 mon_irq_enable(m);
259 mon_enable(m);
260
261 return 0;
262}
263
264static void stop_bw_hwmon(struct bw_hwmon *hw)
265{
266 struct bwmon *m = to_bwmon(hw);
267
Saravana Kannaned84c522014-05-28 18:59:54 -0700268 free_irq(m->irq, m);
269 mon_disable(m);
270 mon_irq_disable(m);
271 mon_clear(m);
272 mon_irq_clear(m);
273}
274
Saravana Kannancddae1b2014-08-07 19:38:02 -0700275static int suspend_bw_hwmon(struct bw_hwmon *hw)
276{
277 struct bwmon *m = to_bwmon(hw);
278
Saravana Kannan217b9b12014-08-07 20:04:45 -0700279 free_irq(m->irq, m);
Saravana Kannancddae1b2014-08-07 19:38:02 -0700280 mon_disable(m);
281 mon_irq_disable(m);
282 mon_irq_clear(m);
283
284 return 0;
285}
286
287static int resume_bw_hwmon(struct bw_hwmon *hw)
288{
289 struct bwmon *m = to_bwmon(hw);
Saravana Kannan217b9b12014-08-07 20:04:45 -0700290 int ret;
Saravana Kannancddae1b2014-08-07 19:38:02 -0700291
292 mon_clear(m);
293 mon_irq_enable(m);
294 mon_enable(m);
Saravana Kannan217b9b12014-08-07 20:04:45 -0700295 ret = request_threaded_irq(m->irq, NULL, bwmon_intr_handler,
296 IRQF_ONESHOT | IRQF_SHARED,
297 dev_name(m->dev), m);
298 if (ret) {
299 dev_err(m->dev, "Unable to register interrupt handler! (%d)\n",
300 ret);
301 return ret;
302 }
Saravana Kannancddae1b2014-08-07 19:38:02 -0700303
304 return 0;
305}
306
Saravana Kannaned84c522014-05-28 18:59:54 -0700307/*************************************************************************/
308
Saravana Kannan75206c22014-09-22 17:42:57 -0700309static const struct bwmon_spec spec[] = {
310 { .wrap_on_thres = true, .overflow = false },
311 { .wrap_on_thres = false, .overflow = true },
312};
313
314static const struct of_device_id bimc_bwmon_match_table[] = {
315 { .compatible = "qcom,bimc-bwmon", .data = &spec[0] },
316 { .compatible = "qcom,bimc-bwmon2", .data = &spec[1] },
317 {}
318};
319
Saravana Kannaned84c522014-05-28 18:59:54 -0700320static int bimc_bwmon_driver_probe(struct platform_device *pdev)
321{
322 struct device *dev = &pdev->dev;
323 struct resource *res;
324 struct bwmon *m;
Saravana Kannan75206c22014-09-22 17:42:57 -0700325 const struct of_device_id *id;
Saravana Kannaned84c522014-05-28 18:59:54 -0700326 int ret;
327 u32 data;
328
329 m = devm_kzalloc(dev, sizeof(*m), GFP_KERNEL);
330 if (!m)
331 return -ENOMEM;
332 m->dev = dev;
333
334 ret = of_property_read_u32(dev->of_node, "qcom,mport", &data);
335 if (ret) {
336 dev_err(dev, "mport not found!\n");
337 return ret;
338 }
339 m->mport = data;
340
Saravana Kannan75206c22014-09-22 17:42:57 -0700341 id = of_match_device(bimc_bwmon_match_table, dev);
342 if (!id) {
343 dev_err(dev, "Unknown device type!\n");
344 return -ENODEV;
345 }
346 m->spec = id->data;
347
Saravana Kannaned84c522014-05-28 18:59:54 -0700348 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "base");
349 if (!res) {
350 dev_err(dev, "base not found!\n");
351 return -EINVAL;
352 }
353 m->base = devm_ioremap(dev, res->start, resource_size(res));
354 if (!m->base) {
355 dev_err(dev, "Unable map base!\n");
356 return -ENOMEM;
357 }
358
359 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "global_base");
360 if (!res) {
361 dev_err(dev, "global_base not found!\n");
362 return -EINVAL;
363 }
364 m->global_base = devm_ioremap(dev, res->start, resource_size(res));
365 if (!m->global_base) {
366 dev_err(dev, "Unable map global_base!\n");
367 return -ENOMEM;
368 }
369
370 m->irq = platform_get_irq(pdev, 0);
371 if (m->irq < 0) {
372 dev_err(dev, "Unable to get IRQ number\n");
373 return m->irq;
374 }
375
376 m->hw.of_node = of_parse_phandle(dev->of_node, "qcom,target-dev", 0);
377 if (!m->hw.of_node)
378 return -EINVAL;
379 m->hw.start_hwmon = &start_bw_hwmon;
380 m->hw.stop_hwmon = &stop_bw_hwmon;
Saravana Kannancddae1b2014-08-07 19:38:02 -0700381 m->hw.suspend_hwmon = &suspend_bw_hwmon;
382 m->hw.resume_hwmon = &resume_bw_hwmon;
Saravana Kannaned84c522014-05-28 18:59:54 -0700383 m->hw.meas_bw_and_set_irq = &meas_bw_and_set_irq;
384
385 ret = register_bw_hwmon(dev, &m->hw);
386 if (ret) {
387 dev_err(dev, "Dev BW hwmon registration failed\n");
388 return ret;
389 }
390
391 return 0;
392}
393
Saravana Kannaned84c522014-05-28 18:59:54 -0700394static struct platform_driver bimc_bwmon_driver = {
395 .probe = bimc_bwmon_driver_probe,
396 .driver = {
397 .name = "bimc-bwmon",
398 .of_match_table = bimc_bwmon_match_table,
399 },
400};
401
402module_platform_driver(bimc_bwmon_driver);
403MODULE_DESCRIPTION("BIMC bandwidth monitor driver");
404MODULE_LICENSE("GPL v2");