xref: /openbmc/linux/drivers/soc/qcom/icc-bwmon.c (revision 6356c7bb)
1b9c2ae6cSKrzysztof Kozlowski // SPDX-License-Identifier: GPL-2.0-only
2b9c2ae6cSKrzysztof Kozlowski /*
3b9c2ae6cSKrzysztof Kozlowski  * Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
4b9c2ae6cSKrzysztof Kozlowski  * Copyright (C) 2021-2022 Linaro Ltd
5b9c2ae6cSKrzysztof Kozlowski  * Author: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>, based on
6b9c2ae6cSKrzysztof Kozlowski  *         previous work of Thara Gopinath and msm-4.9 downstream sources.
7b9c2ae6cSKrzysztof Kozlowski  */
8b9c2ae6cSKrzysztof Kozlowski #include <linux/interconnect.h>
9b9c2ae6cSKrzysztof Kozlowski #include <linux/interrupt.h>
10b9c2ae6cSKrzysztof Kozlowski #include <linux/io.h>
11b9c2ae6cSKrzysztof Kozlowski #include <linux/kernel.h>
12b9c2ae6cSKrzysztof Kozlowski #include <linux/module.h>
13b9c2ae6cSKrzysztof Kozlowski #include <linux/of_device.h>
14b9c2ae6cSKrzysztof Kozlowski #include <linux/platform_device.h>
15b9c2ae6cSKrzysztof Kozlowski #include <linux/pm_opp.h>
16b9c2ae6cSKrzysztof Kozlowski #include <linux/sizes.h>
17b9c2ae6cSKrzysztof Kozlowski 
18b9c2ae6cSKrzysztof Kozlowski /*
19b9c2ae6cSKrzysztof Kozlowski  * The BWMON samples data throughput within 'sample_ms' time. With three
20b9c2ae6cSKrzysztof Kozlowski  * configurable thresholds (Low, Medium and High) gives four windows (called
21b9c2ae6cSKrzysztof Kozlowski  * zones) of current bandwidth:
22b9c2ae6cSKrzysztof Kozlowski  *
23b9c2ae6cSKrzysztof Kozlowski  * Zone 0: byte count < THRES_LO
24b9c2ae6cSKrzysztof Kozlowski  * Zone 1: THRES_LO < byte count < THRES_MED
25b9c2ae6cSKrzysztof Kozlowski  * Zone 2: THRES_MED < byte count < THRES_HIGH
26b9c2ae6cSKrzysztof Kozlowski  * Zone 3: THRES_HIGH < byte count
27b9c2ae6cSKrzysztof Kozlowski  *
28b9c2ae6cSKrzysztof Kozlowski  * Zones 0 and 2 are not used by this driver.
29b9c2ae6cSKrzysztof Kozlowski  */
30b9c2ae6cSKrzysztof Kozlowski 
31b9c2ae6cSKrzysztof Kozlowski /* Internal sampling clock frequency */
32b9c2ae6cSKrzysztof Kozlowski #define HW_TIMER_HZ				19200000
33b9c2ae6cSKrzysztof Kozlowski 
34b9c2ae6cSKrzysztof Kozlowski #define BWMON_GLOBAL_IRQ_STATUS			0x0
35b9c2ae6cSKrzysztof Kozlowski #define BWMON_GLOBAL_IRQ_CLEAR			0x8
36b9c2ae6cSKrzysztof Kozlowski #define BWMON_GLOBAL_IRQ_ENABLE			0xc
37b9c2ae6cSKrzysztof Kozlowski #define BWMON_GLOBAL_IRQ_ENABLE_ENABLE		BIT(0)
38b9c2ae6cSKrzysztof Kozlowski 
39b9c2ae6cSKrzysztof Kozlowski #define BWMON_IRQ_STATUS			0x100
40b9c2ae6cSKrzysztof Kozlowski #define BWMON_IRQ_STATUS_ZONE_SHIFT		4
41b9c2ae6cSKrzysztof Kozlowski #define BWMON_IRQ_CLEAR				0x108
42b9c2ae6cSKrzysztof Kozlowski #define BWMON_IRQ_ENABLE			0x10c
43b9c2ae6cSKrzysztof Kozlowski #define BWMON_IRQ_ENABLE_ZONE1_SHIFT		5
44b9c2ae6cSKrzysztof Kozlowski #define BWMON_IRQ_ENABLE_ZONE2_SHIFT		6
45b9c2ae6cSKrzysztof Kozlowski #define BWMON_IRQ_ENABLE_ZONE3_SHIFT		7
46b9c2ae6cSKrzysztof Kozlowski #define BWMON_IRQ_ENABLE_MASK			(BIT(BWMON_IRQ_ENABLE_ZONE1_SHIFT) | \
47b9c2ae6cSKrzysztof Kozlowski 						 BIT(BWMON_IRQ_ENABLE_ZONE3_SHIFT))
48b9c2ae6cSKrzysztof Kozlowski 
49b9c2ae6cSKrzysztof Kozlowski #define BWMON_ENABLE				0x2a0
50b9c2ae6cSKrzysztof Kozlowski #define BWMON_ENABLE_ENABLE			BIT(0)
51b9c2ae6cSKrzysztof Kozlowski 
52b9c2ae6cSKrzysztof Kozlowski #define BWMON_CLEAR				0x2a4
53b9c2ae6cSKrzysztof Kozlowski #define BWMON_CLEAR_CLEAR			BIT(0)
54b9c2ae6cSKrzysztof Kozlowski 
55b9c2ae6cSKrzysztof Kozlowski #define BWMON_SAMPLE_WINDOW			0x2a8
56b9c2ae6cSKrzysztof Kozlowski #define BWMON_THRESHOLD_HIGH			0x2ac
57b9c2ae6cSKrzysztof Kozlowski #define BWMON_THRESHOLD_MED			0x2b0
58b9c2ae6cSKrzysztof Kozlowski #define BWMON_THRESHOLD_LOW			0x2b4
59b9c2ae6cSKrzysztof Kozlowski 
60b9c2ae6cSKrzysztof Kozlowski #define BWMON_ZONE_ACTIONS			0x2b8
61b9c2ae6cSKrzysztof Kozlowski /*
62b9c2ae6cSKrzysztof Kozlowski  * Actions to perform on some zone 'z' when current zone hits the threshold:
63b9c2ae6cSKrzysztof Kozlowski  * Increment counter of zone 'z'
64b9c2ae6cSKrzysztof Kozlowski  */
65b9c2ae6cSKrzysztof Kozlowski #define BWMON_ZONE_ACTIONS_INCREMENT(z)		(0x2 << ((z) * 2))
66b9c2ae6cSKrzysztof Kozlowski /* Clear counter of zone 'z' */
67b9c2ae6cSKrzysztof Kozlowski #define BWMON_ZONE_ACTIONS_CLEAR(z)		(0x1 << ((z) * 2))
68b9c2ae6cSKrzysztof Kozlowski 
69b9c2ae6cSKrzysztof Kozlowski /* Zone 0 threshold hit: Clear zone count */
70b9c2ae6cSKrzysztof Kozlowski #define BWMON_ZONE_ACTIONS_ZONE0		(BWMON_ZONE_ACTIONS_CLEAR(0))
71b9c2ae6cSKrzysztof Kozlowski 
72b9c2ae6cSKrzysztof Kozlowski /* Zone 1 threshold hit: Increment zone count & clear lower zones */
73b9c2ae6cSKrzysztof Kozlowski #define BWMON_ZONE_ACTIONS_ZONE1		(BWMON_ZONE_ACTIONS_INCREMENT(1) | \
74b9c2ae6cSKrzysztof Kozlowski 						 BWMON_ZONE_ACTIONS_CLEAR(0))
75b9c2ae6cSKrzysztof Kozlowski 
76b9c2ae6cSKrzysztof Kozlowski /* Zone 2 threshold hit: Increment zone count & clear lower zones */
77b9c2ae6cSKrzysztof Kozlowski #define BWMON_ZONE_ACTIONS_ZONE2		(BWMON_ZONE_ACTIONS_INCREMENT(2) | \
78b9c2ae6cSKrzysztof Kozlowski 						 BWMON_ZONE_ACTIONS_CLEAR(1) | \
79b9c2ae6cSKrzysztof Kozlowski 						 BWMON_ZONE_ACTIONS_CLEAR(0))
80b9c2ae6cSKrzysztof Kozlowski 
81b9c2ae6cSKrzysztof Kozlowski /* Zone 3 threshold hit: Increment zone count & clear lower zones */
82b9c2ae6cSKrzysztof Kozlowski #define BWMON_ZONE_ACTIONS_ZONE3		(BWMON_ZONE_ACTIONS_INCREMENT(3) | \
83b9c2ae6cSKrzysztof Kozlowski 						 BWMON_ZONE_ACTIONS_CLEAR(2) | \
84b9c2ae6cSKrzysztof Kozlowski 						 BWMON_ZONE_ACTIONS_CLEAR(1) | \
85b9c2ae6cSKrzysztof Kozlowski 						 BWMON_ZONE_ACTIONS_CLEAR(0))
86b9c2ae6cSKrzysztof Kozlowski /* Value for BWMON_ZONE_ACTIONS */
87b9c2ae6cSKrzysztof Kozlowski #define BWMON_ZONE_ACTIONS_DEFAULT		(BWMON_ZONE_ACTIONS_ZONE0 | \
88b9c2ae6cSKrzysztof Kozlowski 						 BWMON_ZONE_ACTIONS_ZONE1 << 8 | \
89b9c2ae6cSKrzysztof Kozlowski 						 BWMON_ZONE_ACTIONS_ZONE2 << 16 | \
90b9c2ae6cSKrzysztof Kozlowski 						 BWMON_ZONE_ACTIONS_ZONE3 << 24)
91b9c2ae6cSKrzysztof Kozlowski 
92b9c2ae6cSKrzysztof Kozlowski /*
93b9c2ae6cSKrzysztof Kozlowski  * There is no clear documentation/explanation of BWMON_THRESHOLD_COUNT
94b9c2ae6cSKrzysztof Kozlowski  * register. Based on observations, this is number of times one threshold has to
95b9c2ae6cSKrzysztof Kozlowski  * be reached, to trigger interrupt in given zone.
96b9c2ae6cSKrzysztof Kozlowski  *
97b9c2ae6cSKrzysztof Kozlowski  * 0xff are maximum values meant to ignore the zones 0 and 2.
98b9c2ae6cSKrzysztof Kozlowski  */
99b9c2ae6cSKrzysztof Kozlowski #define BWMON_THRESHOLD_COUNT			0x2bc
100b9c2ae6cSKrzysztof Kozlowski #define BWMON_THRESHOLD_COUNT_ZONE1_SHIFT	8
101b9c2ae6cSKrzysztof Kozlowski #define BWMON_THRESHOLD_COUNT_ZONE2_SHIFT	16
102b9c2ae6cSKrzysztof Kozlowski #define BWMON_THRESHOLD_COUNT_ZONE3_SHIFT	24
103b9c2ae6cSKrzysztof Kozlowski #define BWMON_THRESHOLD_COUNT_ZONE0_DEFAULT	0xff
104b9c2ae6cSKrzysztof Kozlowski #define BWMON_THRESHOLD_COUNT_ZONE2_DEFAULT	0xff
105b9c2ae6cSKrzysztof Kozlowski 
106b9c2ae6cSKrzysztof Kozlowski /* BWMONv4 count registers use count unit of 64 kB */
107b9c2ae6cSKrzysztof Kozlowski #define BWMON_COUNT_UNIT_KB			64
108b9c2ae6cSKrzysztof Kozlowski #define BWMON_ZONE_COUNT			0x2d8
109b9c2ae6cSKrzysztof Kozlowski #define BWMON_ZONE_MAX(zone)			(0x2e0 + 4 * (zone))
110b9c2ae6cSKrzysztof Kozlowski 
111b9c2ae6cSKrzysztof Kozlowski struct icc_bwmon_data {
112b9c2ae6cSKrzysztof Kozlowski 	unsigned int sample_ms;
113b9c2ae6cSKrzysztof Kozlowski 	unsigned int default_highbw_kbps;
114b9c2ae6cSKrzysztof Kozlowski 	unsigned int default_medbw_kbps;
115b9c2ae6cSKrzysztof Kozlowski 	unsigned int default_lowbw_kbps;
116b9c2ae6cSKrzysztof Kozlowski 	u8 zone1_thres_count;
117b9c2ae6cSKrzysztof Kozlowski 	u8 zone3_thres_count;
118b9c2ae6cSKrzysztof Kozlowski };
119b9c2ae6cSKrzysztof Kozlowski 
120b9c2ae6cSKrzysztof Kozlowski struct icc_bwmon {
121b9c2ae6cSKrzysztof Kozlowski 	struct device *dev;
122b9c2ae6cSKrzysztof Kozlowski 	void __iomem *base;
123b9c2ae6cSKrzysztof Kozlowski 	int irq;
124b9c2ae6cSKrzysztof Kozlowski 
125b9c2ae6cSKrzysztof Kozlowski 	unsigned int default_lowbw_kbps;
126b9c2ae6cSKrzysztof Kozlowski 	unsigned int sample_ms;
127b9c2ae6cSKrzysztof Kozlowski 	unsigned int max_bw_kbps;
128b9c2ae6cSKrzysztof Kozlowski 	unsigned int min_bw_kbps;
129b9c2ae6cSKrzysztof Kozlowski 	unsigned int target_kbps;
130b9c2ae6cSKrzysztof Kozlowski 	unsigned int current_kbps;
131b9c2ae6cSKrzysztof Kozlowski };
132b9c2ae6cSKrzysztof Kozlowski 
133b9c2ae6cSKrzysztof Kozlowski static void bwmon_clear_counters(struct icc_bwmon *bwmon)
134b9c2ae6cSKrzysztof Kozlowski {
135b9c2ae6cSKrzysztof Kozlowski 	/*
136b9c2ae6cSKrzysztof Kozlowski 	 * Clear counters. The order and barriers are
137b9c2ae6cSKrzysztof Kozlowski 	 * important. Quoting downstream Qualcomm msm-4.9 tree:
138b9c2ae6cSKrzysztof Kozlowski 	 *
139b9c2ae6cSKrzysztof Kozlowski 	 * The counter clear and IRQ clear bits are not in the same 4KB
140b9c2ae6cSKrzysztof Kozlowski 	 * region. So, we need to make sure the counter clear is completed
141b9c2ae6cSKrzysztof Kozlowski 	 * before we try to clear the IRQ or do any other counter operations.
142b9c2ae6cSKrzysztof Kozlowski 	 */
143b9c2ae6cSKrzysztof Kozlowski 	writel(BWMON_CLEAR_CLEAR, bwmon->base + BWMON_CLEAR);
144b9c2ae6cSKrzysztof Kozlowski }
145b9c2ae6cSKrzysztof Kozlowski 
146b9c2ae6cSKrzysztof Kozlowski static void bwmon_clear_irq(struct icc_bwmon *bwmon)
147b9c2ae6cSKrzysztof Kozlowski {
148b9c2ae6cSKrzysztof Kozlowski 	/*
149b9c2ae6cSKrzysztof Kozlowski 	 * Clear zone and global interrupts. The order and barriers are
150b9c2ae6cSKrzysztof Kozlowski 	 * important. Quoting downstream Qualcomm msm-4.9 tree:
151b9c2ae6cSKrzysztof Kozlowski 	 *
152b9c2ae6cSKrzysztof Kozlowski 	 * Synchronize the local interrupt clear in mon_irq_clear()
153b9c2ae6cSKrzysztof Kozlowski 	 * with the global interrupt clear here. Otherwise, the CPU
154b9c2ae6cSKrzysztof Kozlowski 	 * may reorder the two writes and clear the global interrupt
155b9c2ae6cSKrzysztof Kozlowski 	 * before the local interrupt, causing the global interrupt
156b9c2ae6cSKrzysztof Kozlowski 	 * to be retriggered by the local interrupt still being high.
157b9c2ae6cSKrzysztof Kozlowski 	 *
158b9c2ae6cSKrzysztof Kozlowski 	 * Similarly, because the global registers are in a different
159b9c2ae6cSKrzysztof Kozlowski 	 * region than the local registers, we need to ensure any register
160b9c2ae6cSKrzysztof Kozlowski 	 * writes to enable the monitor after this call are ordered with the
161b9c2ae6cSKrzysztof Kozlowski 	 * clearing here so that local writes don't happen before the
162b9c2ae6cSKrzysztof Kozlowski 	 * interrupt is cleared.
163b9c2ae6cSKrzysztof Kozlowski 	 */
164b9c2ae6cSKrzysztof Kozlowski 	writel(BWMON_IRQ_ENABLE_MASK, bwmon->base + BWMON_IRQ_CLEAR);
165*6356c7bbSKrzysztof Kozlowski 	writel(BWMON_GLOBAL_IRQ_ENABLE_ENABLE,
166*6356c7bbSKrzysztof Kozlowski 	       bwmon->base + BWMON_GLOBAL_IRQ_CLEAR);
167b9c2ae6cSKrzysztof Kozlowski }
168b9c2ae6cSKrzysztof Kozlowski 
169b9c2ae6cSKrzysztof Kozlowski static void bwmon_disable(struct icc_bwmon *bwmon)
170b9c2ae6cSKrzysztof Kozlowski {
171b9c2ae6cSKrzysztof Kozlowski 	/* Disable interrupts. Strict ordering, see bwmon_clear_irq(). */
172b9c2ae6cSKrzysztof Kozlowski 	writel(0x0, bwmon->base + BWMON_GLOBAL_IRQ_ENABLE);
173b9c2ae6cSKrzysztof Kozlowski 	writel(0x0, bwmon->base + BWMON_IRQ_ENABLE);
174b9c2ae6cSKrzysztof Kozlowski 
175b9c2ae6cSKrzysztof Kozlowski 	/*
176b9c2ae6cSKrzysztof Kozlowski 	 * Disable bwmon. Must happen before bwmon_clear_irq() to avoid spurious
177b9c2ae6cSKrzysztof Kozlowski 	 * IRQ.
178b9c2ae6cSKrzysztof Kozlowski 	 */
179b9c2ae6cSKrzysztof Kozlowski 	writel(0x0, bwmon->base + BWMON_ENABLE);
180b9c2ae6cSKrzysztof Kozlowski }
181b9c2ae6cSKrzysztof Kozlowski 
182b9c2ae6cSKrzysztof Kozlowski static void bwmon_enable(struct icc_bwmon *bwmon, unsigned int irq_enable)
183b9c2ae6cSKrzysztof Kozlowski {
184b9c2ae6cSKrzysztof Kozlowski 	/* Enable interrupts */
185b9c2ae6cSKrzysztof Kozlowski 	writel(BWMON_GLOBAL_IRQ_ENABLE_ENABLE,
186b9c2ae6cSKrzysztof Kozlowski 	       bwmon->base + BWMON_GLOBAL_IRQ_ENABLE);
187b9c2ae6cSKrzysztof Kozlowski 	writel(irq_enable, bwmon->base + BWMON_IRQ_ENABLE);
188b9c2ae6cSKrzysztof Kozlowski 
189b9c2ae6cSKrzysztof Kozlowski 	/* Enable bwmon */
190b9c2ae6cSKrzysztof Kozlowski 	writel(BWMON_ENABLE_ENABLE, bwmon->base + BWMON_ENABLE);
191b9c2ae6cSKrzysztof Kozlowski }
192b9c2ae6cSKrzysztof Kozlowski 
193b9c2ae6cSKrzysztof Kozlowski static unsigned int bwmon_kbps_to_count(unsigned int kbps)
194b9c2ae6cSKrzysztof Kozlowski {
195b9c2ae6cSKrzysztof Kozlowski 	return kbps / BWMON_COUNT_UNIT_KB;
196b9c2ae6cSKrzysztof Kozlowski }
197b9c2ae6cSKrzysztof Kozlowski 
198b9c2ae6cSKrzysztof Kozlowski static void bwmon_set_threshold(struct icc_bwmon *bwmon, unsigned int reg,
199b9c2ae6cSKrzysztof Kozlowski 				unsigned int kbps)
200b9c2ae6cSKrzysztof Kozlowski {
201b9c2ae6cSKrzysztof Kozlowski 	unsigned int thres;
202b9c2ae6cSKrzysztof Kozlowski 
203b9c2ae6cSKrzysztof Kozlowski 	thres = mult_frac(bwmon_kbps_to_count(kbps), bwmon->sample_ms,
204b9c2ae6cSKrzysztof Kozlowski 			  MSEC_PER_SEC);
205b9c2ae6cSKrzysztof Kozlowski 	writel_relaxed(thres, bwmon->base + reg);
206b9c2ae6cSKrzysztof Kozlowski }
207b9c2ae6cSKrzysztof Kozlowski 
208b9c2ae6cSKrzysztof Kozlowski static void bwmon_start(struct icc_bwmon *bwmon,
209b9c2ae6cSKrzysztof Kozlowski 			const struct icc_bwmon_data *data)
210b9c2ae6cSKrzysztof Kozlowski {
211b9c2ae6cSKrzysztof Kozlowski 	unsigned int thres_count;
212b9c2ae6cSKrzysztof Kozlowski 	int window;
213b9c2ae6cSKrzysztof Kozlowski 
214b9c2ae6cSKrzysztof Kozlowski 	bwmon_clear_counters(bwmon);
215b9c2ae6cSKrzysztof Kozlowski 
216b9c2ae6cSKrzysztof Kozlowski 	window = mult_frac(bwmon->sample_ms, HW_TIMER_HZ, MSEC_PER_SEC);
217b9c2ae6cSKrzysztof Kozlowski 	/* Maximum sampling window: 0xfffff */
218b9c2ae6cSKrzysztof Kozlowski 	writel_relaxed(window, bwmon->base + BWMON_SAMPLE_WINDOW);
219b9c2ae6cSKrzysztof Kozlowski 
220b9c2ae6cSKrzysztof Kozlowski 	bwmon_set_threshold(bwmon, BWMON_THRESHOLD_HIGH,
221b9c2ae6cSKrzysztof Kozlowski 			    data->default_highbw_kbps);
222b9c2ae6cSKrzysztof Kozlowski 	bwmon_set_threshold(bwmon, BWMON_THRESHOLD_MED,
223b9c2ae6cSKrzysztof Kozlowski 			    data->default_medbw_kbps);
224b9c2ae6cSKrzysztof Kozlowski 	bwmon_set_threshold(bwmon, BWMON_THRESHOLD_LOW,
225b9c2ae6cSKrzysztof Kozlowski 			    data->default_lowbw_kbps);
226b9c2ae6cSKrzysztof Kozlowski 
227b9c2ae6cSKrzysztof Kozlowski 	thres_count = data->zone3_thres_count << BWMON_THRESHOLD_COUNT_ZONE3_SHIFT |
228b9c2ae6cSKrzysztof Kozlowski 		      BWMON_THRESHOLD_COUNT_ZONE2_DEFAULT << BWMON_THRESHOLD_COUNT_ZONE2_SHIFT |
229b9c2ae6cSKrzysztof Kozlowski 		      data->zone1_thres_count << BWMON_THRESHOLD_COUNT_ZONE1_SHIFT |
230b9c2ae6cSKrzysztof Kozlowski 		      BWMON_THRESHOLD_COUNT_ZONE0_DEFAULT;
231b9c2ae6cSKrzysztof Kozlowski 	writel_relaxed(thres_count, bwmon->base + BWMON_THRESHOLD_COUNT);
232b9c2ae6cSKrzysztof Kozlowski 	writel_relaxed(BWMON_ZONE_ACTIONS_DEFAULT,
233b9c2ae6cSKrzysztof Kozlowski 		       bwmon->base + BWMON_ZONE_ACTIONS);
234b9c2ae6cSKrzysztof Kozlowski 	/* Write barriers in bwmon_clear_irq() */
235b9c2ae6cSKrzysztof Kozlowski 
236b9c2ae6cSKrzysztof Kozlowski 	bwmon_clear_irq(bwmon);
237b9c2ae6cSKrzysztof Kozlowski 	bwmon_enable(bwmon, BWMON_IRQ_ENABLE_MASK);
238b9c2ae6cSKrzysztof Kozlowski }
239b9c2ae6cSKrzysztof Kozlowski 
240b9c2ae6cSKrzysztof Kozlowski static irqreturn_t bwmon_intr(int irq, void *dev_id)
241b9c2ae6cSKrzysztof Kozlowski {
242b9c2ae6cSKrzysztof Kozlowski 	struct icc_bwmon *bwmon = dev_id;
243b9c2ae6cSKrzysztof Kozlowski 	unsigned int status, max;
244b9c2ae6cSKrzysztof Kozlowski 	int zone;
245b9c2ae6cSKrzysztof Kozlowski 
246b9c2ae6cSKrzysztof Kozlowski 	status = readl(bwmon->base + BWMON_IRQ_STATUS);
247b9c2ae6cSKrzysztof Kozlowski 	status &= BWMON_IRQ_ENABLE_MASK;
248b9c2ae6cSKrzysztof Kozlowski 	if (!status) {
249b9c2ae6cSKrzysztof Kozlowski 		/*
250b9c2ae6cSKrzysztof Kozlowski 		 * Only zone 1 and zone 3 interrupts are enabled but zone 2
251b9c2ae6cSKrzysztof Kozlowski 		 * threshold could be hit and trigger interrupt even if not
252b9c2ae6cSKrzysztof Kozlowski 		 * enabled.
253b9c2ae6cSKrzysztof Kozlowski 		 * Such spurious interrupt might come with valuable max count or
254b9c2ae6cSKrzysztof Kozlowski 		 * not, so solution would be to always check all
255b9c2ae6cSKrzysztof Kozlowski 		 * BWMON_ZONE_MAX() registers to find the highest value.
256b9c2ae6cSKrzysztof Kozlowski 		 * Such case is currently ignored.
257b9c2ae6cSKrzysztof Kozlowski 		 */
258b9c2ae6cSKrzysztof Kozlowski 		return IRQ_NONE;
259b9c2ae6cSKrzysztof Kozlowski 	}
260b9c2ae6cSKrzysztof Kozlowski 
261b9c2ae6cSKrzysztof Kozlowski 	bwmon_disable(bwmon);
262b9c2ae6cSKrzysztof Kozlowski 
263b9c2ae6cSKrzysztof Kozlowski 	zone = get_bitmask_order(status >> BWMON_IRQ_STATUS_ZONE_SHIFT) - 1;
264b9c2ae6cSKrzysztof Kozlowski 	/*
265b9c2ae6cSKrzysztof Kozlowski 	 * Zone max bytes count register returns count units within sampling
266b9c2ae6cSKrzysztof Kozlowski 	 * window.  Downstream kernel for BWMONv4 (called BWMON type 2 in
267b9c2ae6cSKrzysztof Kozlowski 	 * downstream) always increments the max bytes count by one.
268b9c2ae6cSKrzysztof Kozlowski 	 */
269b9c2ae6cSKrzysztof Kozlowski 	max = readl(bwmon->base + BWMON_ZONE_MAX(zone)) + 1;
270b9c2ae6cSKrzysztof Kozlowski 	max *= BWMON_COUNT_UNIT_KB;
271b9c2ae6cSKrzysztof Kozlowski 	bwmon->target_kbps = mult_frac(max, MSEC_PER_SEC, bwmon->sample_ms);
272b9c2ae6cSKrzysztof Kozlowski 
273b9c2ae6cSKrzysztof Kozlowski 	return IRQ_WAKE_THREAD;
274b9c2ae6cSKrzysztof Kozlowski }
275b9c2ae6cSKrzysztof Kozlowski 
276b9c2ae6cSKrzysztof Kozlowski static irqreturn_t bwmon_intr_thread(int irq, void *dev_id)
277b9c2ae6cSKrzysztof Kozlowski {
278b9c2ae6cSKrzysztof Kozlowski 	struct icc_bwmon *bwmon = dev_id;
279b9c2ae6cSKrzysztof Kozlowski 	unsigned int irq_enable = 0;
280b9c2ae6cSKrzysztof Kozlowski 	struct dev_pm_opp *opp, *target_opp;
281b9c2ae6cSKrzysztof Kozlowski 	unsigned int bw_kbps, up_kbps, down_kbps;
282b9c2ae6cSKrzysztof Kozlowski 
283b9c2ae6cSKrzysztof Kozlowski 	bw_kbps = bwmon->target_kbps;
284b9c2ae6cSKrzysztof Kozlowski 
285b9c2ae6cSKrzysztof Kozlowski 	target_opp = dev_pm_opp_find_bw_ceil(bwmon->dev, &bw_kbps, 0);
286b9c2ae6cSKrzysztof Kozlowski 	if (IS_ERR(target_opp) && PTR_ERR(target_opp) == -ERANGE)
287b9c2ae6cSKrzysztof Kozlowski 		target_opp = dev_pm_opp_find_bw_floor(bwmon->dev, &bw_kbps, 0);
288b9c2ae6cSKrzysztof Kozlowski 
289b9c2ae6cSKrzysztof Kozlowski 	bwmon->target_kbps = bw_kbps;
290b9c2ae6cSKrzysztof Kozlowski 
291b9c2ae6cSKrzysztof Kozlowski 	bw_kbps--;
292b9c2ae6cSKrzysztof Kozlowski 	opp = dev_pm_opp_find_bw_floor(bwmon->dev, &bw_kbps, 0);
293b9c2ae6cSKrzysztof Kozlowski 	if (IS_ERR(opp) && PTR_ERR(opp) == -ERANGE)
294b9c2ae6cSKrzysztof Kozlowski 		down_kbps = bwmon->target_kbps;
295b9c2ae6cSKrzysztof Kozlowski 	else
296b9c2ae6cSKrzysztof Kozlowski 		down_kbps = bw_kbps;
297b9c2ae6cSKrzysztof Kozlowski 
298b9c2ae6cSKrzysztof Kozlowski 	up_kbps = bwmon->target_kbps + 1;
299b9c2ae6cSKrzysztof Kozlowski 
300b9c2ae6cSKrzysztof Kozlowski 	if (bwmon->target_kbps >= bwmon->max_bw_kbps)
301b9c2ae6cSKrzysztof Kozlowski 		irq_enable = BIT(BWMON_IRQ_ENABLE_ZONE1_SHIFT);
302b9c2ae6cSKrzysztof Kozlowski 	else if (bwmon->target_kbps <= bwmon->min_bw_kbps)
303b9c2ae6cSKrzysztof Kozlowski 		irq_enable = BIT(BWMON_IRQ_ENABLE_ZONE3_SHIFT);
304b9c2ae6cSKrzysztof Kozlowski 	else
305b9c2ae6cSKrzysztof Kozlowski 		irq_enable = BWMON_IRQ_ENABLE_MASK;
306b9c2ae6cSKrzysztof Kozlowski 
307b9c2ae6cSKrzysztof Kozlowski 	bwmon_set_threshold(bwmon, BWMON_THRESHOLD_HIGH, up_kbps);
308b9c2ae6cSKrzysztof Kozlowski 	bwmon_set_threshold(bwmon, BWMON_THRESHOLD_MED, down_kbps);
309b9c2ae6cSKrzysztof Kozlowski 	/* Write barriers in bwmon_clear_counters() */
310b9c2ae6cSKrzysztof Kozlowski 	bwmon_clear_counters(bwmon);
311b9c2ae6cSKrzysztof Kozlowski 	bwmon_clear_irq(bwmon);
312b9c2ae6cSKrzysztof Kozlowski 	bwmon_enable(bwmon, irq_enable);
313b9c2ae6cSKrzysztof Kozlowski 
314b9c2ae6cSKrzysztof Kozlowski 	if (bwmon->target_kbps == bwmon->current_kbps)
315b9c2ae6cSKrzysztof Kozlowski 		goto out;
316b9c2ae6cSKrzysztof Kozlowski 
317b9c2ae6cSKrzysztof Kozlowski 	dev_pm_opp_set_opp(bwmon->dev, target_opp);
318b9c2ae6cSKrzysztof Kozlowski 	bwmon->current_kbps = bwmon->target_kbps;
319b9c2ae6cSKrzysztof Kozlowski 
320b9c2ae6cSKrzysztof Kozlowski out:
321b9c2ae6cSKrzysztof Kozlowski 	dev_pm_opp_put(target_opp);
322b9c2ae6cSKrzysztof Kozlowski 	if (!IS_ERR(opp))
323b9c2ae6cSKrzysztof Kozlowski 		dev_pm_opp_put(opp);
324b9c2ae6cSKrzysztof Kozlowski 
325b9c2ae6cSKrzysztof Kozlowski 	return IRQ_HANDLED;
326b9c2ae6cSKrzysztof Kozlowski }
327b9c2ae6cSKrzysztof Kozlowski 
328b9c2ae6cSKrzysztof Kozlowski static int bwmon_probe(struct platform_device *pdev)
329b9c2ae6cSKrzysztof Kozlowski {
330b9c2ae6cSKrzysztof Kozlowski 	struct device *dev = &pdev->dev;
331b9c2ae6cSKrzysztof Kozlowski 	struct dev_pm_opp *opp;
332b9c2ae6cSKrzysztof Kozlowski 	struct icc_bwmon *bwmon;
333b9c2ae6cSKrzysztof Kozlowski 	const struct icc_bwmon_data *data;
334b9c2ae6cSKrzysztof Kozlowski 	int ret;
335b9c2ae6cSKrzysztof Kozlowski 
336b9c2ae6cSKrzysztof Kozlowski 	bwmon = devm_kzalloc(dev, sizeof(*bwmon), GFP_KERNEL);
337b9c2ae6cSKrzysztof Kozlowski 	if (!bwmon)
338b9c2ae6cSKrzysztof Kozlowski 		return -ENOMEM;
339b9c2ae6cSKrzysztof Kozlowski 
340b9c2ae6cSKrzysztof Kozlowski 	data = of_device_get_match_data(dev);
341b9c2ae6cSKrzysztof Kozlowski 
342b9c2ae6cSKrzysztof Kozlowski 	bwmon->base = devm_platform_ioremap_resource(pdev, 0);
343b9c2ae6cSKrzysztof Kozlowski 	if (IS_ERR(bwmon->base)) {
344b9c2ae6cSKrzysztof Kozlowski 		dev_err(dev, "failed to map bwmon registers\n");
345b9c2ae6cSKrzysztof Kozlowski 		return PTR_ERR(bwmon->base);
346b9c2ae6cSKrzysztof Kozlowski 	}
347b9c2ae6cSKrzysztof Kozlowski 
348b9c2ae6cSKrzysztof Kozlowski 	bwmon->irq = platform_get_irq(pdev, 0);
349947bb0d1SYang Li 	if (bwmon->irq < 0)
350b9c2ae6cSKrzysztof Kozlowski 		return bwmon->irq;
351b9c2ae6cSKrzysztof Kozlowski 
352b9c2ae6cSKrzysztof Kozlowski 	ret = devm_pm_opp_of_add_table(dev);
353b9c2ae6cSKrzysztof Kozlowski 	if (ret)
354b9c2ae6cSKrzysztof Kozlowski 		return dev_err_probe(dev, ret, "failed to add OPP table\n");
355b9c2ae6cSKrzysztof Kozlowski 
356b9c2ae6cSKrzysztof Kozlowski 	bwmon->max_bw_kbps = UINT_MAX;
357b9c2ae6cSKrzysztof Kozlowski 	opp = dev_pm_opp_find_bw_floor(dev, &bwmon->max_bw_kbps, 0);
358b9c2ae6cSKrzysztof Kozlowski 	if (IS_ERR(opp))
359b9c2ae6cSKrzysztof Kozlowski 		return dev_err_probe(dev, ret, "failed to find max peak bandwidth\n");
360b9c2ae6cSKrzysztof Kozlowski 
361b9c2ae6cSKrzysztof Kozlowski 	bwmon->min_bw_kbps = 0;
362b9c2ae6cSKrzysztof Kozlowski 	opp = dev_pm_opp_find_bw_ceil(dev, &bwmon->min_bw_kbps, 0);
363b9c2ae6cSKrzysztof Kozlowski 	if (IS_ERR(opp))
364b9c2ae6cSKrzysztof Kozlowski 		return dev_err_probe(dev, ret, "failed to find min peak bandwidth\n");
365b9c2ae6cSKrzysztof Kozlowski 
366b9c2ae6cSKrzysztof Kozlowski 	bwmon->sample_ms = data->sample_ms;
367b9c2ae6cSKrzysztof Kozlowski 	bwmon->default_lowbw_kbps = data->default_lowbw_kbps;
368b9c2ae6cSKrzysztof Kozlowski 	bwmon->dev = dev;
369b9c2ae6cSKrzysztof Kozlowski 
370b9c2ae6cSKrzysztof Kozlowski 	bwmon_disable(bwmon);
371b9c2ae6cSKrzysztof Kozlowski 	ret = devm_request_threaded_irq(dev, bwmon->irq, bwmon_intr,
372b9c2ae6cSKrzysztof Kozlowski 					bwmon_intr_thread,
373b9c2ae6cSKrzysztof Kozlowski 					IRQF_ONESHOT, dev_name(dev), bwmon);
374b9c2ae6cSKrzysztof Kozlowski 	if (ret)
375b9c2ae6cSKrzysztof Kozlowski 		return dev_err_probe(dev, ret, "failed to request IRQ\n");
376b9c2ae6cSKrzysztof Kozlowski 
377b9c2ae6cSKrzysztof Kozlowski 	platform_set_drvdata(pdev, bwmon);
378b9c2ae6cSKrzysztof Kozlowski 	bwmon_start(bwmon, data);
379b9c2ae6cSKrzysztof Kozlowski 
380b9c2ae6cSKrzysztof Kozlowski 	return 0;
381b9c2ae6cSKrzysztof Kozlowski }
382b9c2ae6cSKrzysztof Kozlowski 
383b9c2ae6cSKrzysztof Kozlowski static int bwmon_remove(struct platform_device *pdev)
384b9c2ae6cSKrzysztof Kozlowski {
385b9c2ae6cSKrzysztof Kozlowski 	struct icc_bwmon *bwmon = platform_get_drvdata(pdev);
386b9c2ae6cSKrzysztof Kozlowski 
387b9c2ae6cSKrzysztof Kozlowski 	bwmon_disable(bwmon);
388b9c2ae6cSKrzysztof Kozlowski 
389b9c2ae6cSKrzysztof Kozlowski 	return 0;
390b9c2ae6cSKrzysztof Kozlowski }
391b9c2ae6cSKrzysztof Kozlowski 
392b9c2ae6cSKrzysztof Kozlowski /* BWMON v4 */
393b9c2ae6cSKrzysztof Kozlowski static const struct icc_bwmon_data msm8998_bwmon_data = {
394b9c2ae6cSKrzysztof Kozlowski 	.sample_ms = 4,
395b9c2ae6cSKrzysztof Kozlowski 	.default_highbw_kbps = 4800 * 1024, /* 4.8 GBps */
396b9c2ae6cSKrzysztof Kozlowski 	.default_medbw_kbps = 512 * 1024, /* 512 MBps */
397b9c2ae6cSKrzysztof Kozlowski 	.default_lowbw_kbps = 0,
398b9c2ae6cSKrzysztof Kozlowski 	.zone1_thres_count = 16,
399b9c2ae6cSKrzysztof Kozlowski 	.zone3_thres_count = 1,
400b9c2ae6cSKrzysztof Kozlowski };
401b9c2ae6cSKrzysztof Kozlowski 
402b9c2ae6cSKrzysztof Kozlowski static const struct of_device_id bwmon_of_match[] = {
403b9c2ae6cSKrzysztof Kozlowski 	{ .compatible = "qcom,msm8998-bwmon", .data = &msm8998_bwmon_data },
404b9c2ae6cSKrzysztof Kozlowski 	{}
405b9c2ae6cSKrzysztof Kozlowski };
406b9c2ae6cSKrzysztof Kozlowski MODULE_DEVICE_TABLE(of, bwmon_of_match);
407b9c2ae6cSKrzysztof Kozlowski 
408b9c2ae6cSKrzysztof Kozlowski static struct platform_driver bwmon_driver = {
409b9c2ae6cSKrzysztof Kozlowski 	.probe = bwmon_probe,
410b9c2ae6cSKrzysztof Kozlowski 	.remove = bwmon_remove,
411b9c2ae6cSKrzysztof Kozlowski 	.driver = {
412b9c2ae6cSKrzysztof Kozlowski 		.name = "qcom-bwmon",
413b9c2ae6cSKrzysztof Kozlowski 		.of_match_table = bwmon_of_match,
414b9c2ae6cSKrzysztof Kozlowski 	},
415b9c2ae6cSKrzysztof Kozlowski };
416b9c2ae6cSKrzysztof Kozlowski module_platform_driver(bwmon_driver);
417b9c2ae6cSKrzysztof Kozlowski 
418b9c2ae6cSKrzysztof Kozlowski MODULE_AUTHOR("Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>");
419b9c2ae6cSKrzysztof Kozlowski MODULE_DESCRIPTION("QCOM BWMON driver");
420b9c2ae6cSKrzysztof Kozlowski MODULE_LICENSE("GPL");
421