xref: /openbmc/linux/drivers/soc/qcom/icc-bwmon.c (revision cdad59c2)
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  */
8ec63dcd3SKrzysztof Kozlowski 
9ec63dcd3SKrzysztof Kozlowski #include <linux/err.h>
10b9c2ae6cSKrzysztof Kozlowski #include <linux/interconnect.h>
11b9c2ae6cSKrzysztof Kozlowski #include <linux/interrupt.h>
12b9c2ae6cSKrzysztof Kozlowski #include <linux/io.h>
13b9c2ae6cSKrzysztof Kozlowski #include <linux/kernel.h>
14b9c2ae6cSKrzysztof Kozlowski #include <linux/module.h>
15b9c2ae6cSKrzysztof Kozlowski #include <linux/of_device.h>
16b9c2ae6cSKrzysztof Kozlowski #include <linux/platform_device.h>
17b9c2ae6cSKrzysztof Kozlowski #include <linux/pm_opp.h>
18ec63dcd3SKrzysztof Kozlowski #include <linux/regmap.h>
19b9c2ae6cSKrzysztof Kozlowski #include <linux/sizes.h>
20b9c2ae6cSKrzysztof Kozlowski 
21b9c2ae6cSKrzysztof Kozlowski /*
22b9c2ae6cSKrzysztof Kozlowski  * The BWMON samples data throughput within 'sample_ms' time. With three
23b9c2ae6cSKrzysztof Kozlowski  * configurable thresholds (Low, Medium and High) gives four windows (called
24b9c2ae6cSKrzysztof Kozlowski  * zones) of current bandwidth:
25b9c2ae6cSKrzysztof Kozlowski  *
26b9c2ae6cSKrzysztof Kozlowski  * Zone 0: byte count < THRES_LO
27b9c2ae6cSKrzysztof Kozlowski  * Zone 1: THRES_LO < byte count < THRES_MED
28b9c2ae6cSKrzysztof Kozlowski  * Zone 2: THRES_MED < byte count < THRES_HIGH
29b9c2ae6cSKrzysztof Kozlowski  * Zone 3: THRES_HIGH < byte count
30b9c2ae6cSKrzysztof Kozlowski  *
31b9c2ae6cSKrzysztof Kozlowski  * Zones 0 and 2 are not used by this driver.
32b9c2ae6cSKrzysztof Kozlowski  */
33b9c2ae6cSKrzysztof Kozlowski 
34b9c2ae6cSKrzysztof Kozlowski /* Internal sampling clock frequency */
35b9c2ae6cSKrzysztof Kozlowski #define HW_TIMER_HZ				19200000
36b9c2ae6cSKrzysztof Kozlowski 
37ec63dcd3SKrzysztof Kozlowski #define BWMON_V4_GLOBAL_IRQ_CLEAR		0x008
38ec63dcd3SKrzysztof Kozlowski #define BWMON_V4_GLOBAL_IRQ_ENABLE		0x00c
39ec63dcd3SKrzysztof Kozlowski /*
40ec63dcd3SKrzysztof Kozlowski  * All values here and further are matching regmap fields, so without absolute
41ec63dcd3SKrzysztof Kozlowski  * register offsets.
42ec63dcd3SKrzysztof Kozlowski  */
43ec63dcd3SKrzysztof Kozlowski #define BWMON_V4_GLOBAL_IRQ_ENABLE_ENABLE	BIT(0)
44b9c2ae6cSKrzysztof Kozlowski 
45ec63dcd3SKrzysztof Kozlowski #define BWMON_V4_IRQ_STATUS			0x100
46ec63dcd3SKrzysztof Kozlowski #define BWMON_V4_IRQ_CLEAR			0x108
47b9c2ae6cSKrzysztof Kozlowski 
48ec63dcd3SKrzysztof Kozlowski #define BWMON_V4_IRQ_ENABLE			0x10c
49ec63dcd3SKrzysztof Kozlowski #define BWMON_IRQ_ENABLE_MASK			(BIT(1) | BIT(3))
5014af4ce0SKrzysztof Kozlowski #define BWMON_V5_IRQ_STATUS			0x000
5114af4ce0SKrzysztof Kozlowski #define BWMON_V5_IRQ_CLEAR			0x008
5214af4ce0SKrzysztof Kozlowski #define BWMON_V5_IRQ_ENABLE			0x00c
53ec63dcd3SKrzysztof Kozlowski 
54ec63dcd3SKrzysztof Kozlowski #define BWMON_V4_ENABLE				0x2a0
5514af4ce0SKrzysztof Kozlowski #define BWMON_V5_ENABLE				0x010
56b9c2ae6cSKrzysztof Kozlowski #define BWMON_ENABLE_ENABLE			BIT(0)
57b9c2ae6cSKrzysztof Kozlowski 
58ec63dcd3SKrzysztof Kozlowski #define BWMON_V4_CLEAR				0x2a4
5914af4ce0SKrzysztof Kozlowski #define BWMON_V5_CLEAR				0x014
60b9c2ae6cSKrzysztof Kozlowski #define BWMON_CLEAR_CLEAR			BIT(0)
61956deab5SKrzysztof Kozlowski #define BWMON_CLEAR_CLEAR_ALL			BIT(1)
62b9c2ae6cSKrzysztof Kozlowski 
63ec63dcd3SKrzysztof Kozlowski #define BWMON_V4_SAMPLE_WINDOW			0x2a8
6414af4ce0SKrzysztof Kozlowski #define BWMON_V5_SAMPLE_WINDOW			0x020
6514af4ce0SKrzysztof Kozlowski 
66ec63dcd3SKrzysztof Kozlowski #define BWMON_V4_THRESHOLD_HIGH			0x2ac
67ec63dcd3SKrzysztof Kozlowski #define BWMON_V4_THRESHOLD_MED			0x2b0
68ec63dcd3SKrzysztof Kozlowski #define BWMON_V4_THRESHOLD_LOW			0x2b4
6914af4ce0SKrzysztof Kozlowski #define BWMON_V5_THRESHOLD_HIGH			0x024
7014af4ce0SKrzysztof Kozlowski #define BWMON_V5_THRESHOLD_MED			0x028
7114af4ce0SKrzysztof Kozlowski #define BWMON_V5_THRESHOLD_LOW			0x02c
72b9c2ae6cSKrzysztof Kozlowski 
73ec63dcd3SKrzysztof Kozlowski #define BWMON_V4_ZONE_ACTIONS			0x2b8
7414af4ce0SKrzysztof Kozlowski #define BWMON_V5_ZONE_ACTIONS			0x030
75b9c2ae6cSKrzysztof Kozlowski /*
76b9c2ae6cSKrzysztof Kozlowski  * Actions to perform on some zone 'z' when current zone hits the threshold:
77b9c2ae6cSKrzysztof Kozlowski  * Increment counter of zone 'z'
78b9c2ae6cSKrzysztof Kozlowski  */
79b9c2ae6cSKrzysztof Kozlowski #define BWMON_ZONE_ACTIONS_INCREMENT(z)		(0x2 << ((z) * 2))
80b9c2ae6cSKrzysztof Kozlowski /* Clear counter of zone 'z' */
81b9c2ae6cSKrzysztof Kozlowski #define BWMON_ZONE_ACTIONS_CLEAR(z)		(0x1 << ((z) * 2))
82b9c2ae6cSKrzysztof Kozlowski 
83b9c2ae6cSKrzysztof Kozlowski /* Zone 0 threshold hit: Clear zone count */
84b9c2ae6cSKrzysztof Kozlowski #define BWMON_ZONE_ACTIONS_ZONE0		(BWMON_ZONE_ACTIONS_CLEAR(0))
85b9c2ae6cSKrzysztof Kozlowski 
86b9c2ae6cSKrzysztof Kozlowski /* Zone 1 threshold hit: Increment zone count & clear lower zones */
87b9c2ae6cSKrzysztof Kozlowski #define BWMON_ZONE_ACTIONS_ZONE1		(BWMON_ZONE_ACTIONS_INCREMENT(1) | \
88b9c2ae6cSKrzysztof Kozlowski 						 BWMON_ZONE_ACTIONS_CLEAR(0))
89b9c2ae6cSKrzysztof Kozlowski 
90b9c2ae6cSKrzysztof Kozlowski /* Zone 2 threshold hit: Increment zone count & clear lower zones */
91b9c2ae6cSKrzysztof Kozlowski #define BWMON_ZONE_ACTIONS_ZONE2		(BWMON_ZONE_ACTIONS_INCREMENT(2) | \
92b9c2ae6cSKrzysztof Kozlowski 						 BWMON_ZONE_ACTIONS_CLEAR(1) | \
93b9c2ae6cSKrzysztof Kozlowski 						 BWMON_ZONE_ACTIONS_CLEAR(0))
94b9c2ae6cSKrzysztof Kozlowski 
95b9c2ae6cSKrzysztof Kozlowski /* Zone 3 threshold hit: Increment zone count & clear lower zones */
96b9c2ae6cSKrzysztof Kozlowski #define BWMON_ZONE_ACTIONS_ZONE3		(BWMON_ZONE_ACTIONS_INCREMENT(3) | \
97b9c2ae6cSKrzysztof Kozlowski 						 BWMON_ZONE_ACTIONS_CLEAR(2) | \
98b9c2ae6cSKrzysztof Kozlowski 						 BWMON_ZONE_ACTIONS_CLEAR(1) | \
99b9c2ae6cSKrzysztof Kozlowski 						 BWMON_ZONE_ACTIONS_CLEAR(0))
100b9c2ae6cSKrzysztof Kozlowski 
101b9c2ae6cSKrzysztof Kozlowski /*
102ec63dcd3SKrzysztof Kozlowski  * There is no clear documentation/explanation of BWMON_V4_THRESHOLD_COUNT
103b9c2ae6cSKrzysztof Kozlowski  * register. Based on observations, this is number of times one threshold has to
104b9c2ae6cSKrzysztof Kozlowski  * be reached, to trigger interrupt in given zone.
105b9c2ae6cSKrzysztof Kozlowski  *
106b9c2ae6cSKrzysztof Kozlowski  * 0xff are maximum values meant to ignore the zones 0 and 2.
107b9c2ae6cSKrzysztof Kozlowski  */
108ec63dcd3SKrzysztof Kozlowski #define BWMON_V4_THRESHOLD_COUNT		0x2bc
10914af4ce0SKrzysztof Kozlowski #define BWMON_V5_THRESHOLD_COUNT		0x034
110b9c2ae6cSKrzysztof Kozlowski #define BWMON_THRESHOLD_COUNT_ZONE0_DEFAULT	0xff
111b9c2ae6cSKrzysztof Kozlowski #define BWMON_THRESHOLD_COUNT_ZONE2_DEFAULT	0xff
112b9c2ae6cSKrzysztof Kozlowski 
113ec63dcd3SKrzysztof Kozlowski #define BWMON_V4_ZONE_MAX(zone)			(0x2e0 + 4 * (zone))
11414af4ce0SKrzysztof Kozlowski #define BWMON_V5_ZONE_MAX(zone)			(0x044 + 4 * (zone))
115ec63dcd3SKrzysztof Kozlowski 
116e6f34184SKrzysztof Kozlowski /* Quirks for specific BWMON types */
117e6f34184SKrzysztof Kozlowski #define BWMON_HAS_GLOBAL_IRQ			BIT(0)
118*cdad59c2SRajendra Nayak #define BWMON_NEEDS_FORCE_CLEAR			BIT(1)
119e6f34184SKrzysztof Kozlowski 
120ec63dcd3SKrzysztof Kozlowski enum bwmon_fields {
121ec63dcd3SKrzysztof Kozlowski 	F_GLOBAL_IRQ_CLEAR,
122ec63dcd3SKrzysztof Kozlowski 	F_GLOBAL_IRQ_ENABLE,
123ec63dcd3SKrzysztof Kozlowski 	F_IRQ_STATUS,
124ec63dcd3SKrzysztof Kozlowski 	F_IRQ_CLEAR,
125ec63dcd3SKrzysztof Kozlowski 	F_IRQ_ENABLE,
126ec63dcd3SKrzysztof Kozlowski 	F_ENABLE,
127ec63dcd3SKrzysztof Kozlowski 	F_CLEAR,
128ec63dcd3SKrzysztof Kozlowski 	F_SAMPLE_WINDOW,
129ec63dcd3SKrzysztof Kozlowski 	F_THRESHOLD_HIGH,
130ec63dcd3SKrzysztof Kozlowski 	F_THRESHOLD_MED,
131ec63dcd3SKrzysztof Kozlowski 	F_THRESHOLD_LOW,
132ec63dcd3SKrzysztof Kozlowski 	F_ZONE_ACTIONS_ZONE0,
133ec63dcd3SKrzysztof Kozlowski 	F_ZONE_ACTIONS_ZONE1,
134ec63dcd3SKrzysztof Kozlowski 	F_ZONE_ACTIONS_ZONE2,
135ec63dcd3SKrzysztof Kozlowski 	F_ZONE_ACTIONS_ZONE3,
136ec63dcd3SKrzysztof Kozlowski 	F_THRESHOLD_COUNT_ZONE0,
137ec63dcd3SKrzysztof Kozlowski 	F_THRESHOLD_COUNT_ZONE1,
138ec63dcd3SKrzysztof Kozlowski 	F_THRESHOLD_COUNT_ZONE2,
139ec63dcd3SKrzysztof Kozlowski 	F_THRESHOLD_COUNT_ZONE3,
140ec63dcd3SKrzysztof Kozlowski 	F_ZONE0_MAX,
141ec63dcd3SKrzysztof Kozlowski 	F_ZONE1_MAX,
142ec63dcd3SKrzysztof Kozlowski 	F_ZONE2_MAX,
143ec63dcd3SKrzysztof Kozlowski 	F_ZONE3_MAX,
144ec63dcd3SKrzysztof Kozlowski 
145ec63dcd3SKrzysztof Kozlowski 	F_NUM_FIELDS
146ec63dcd3SKrzysztof Kozlowski };
147b9c2ae6cSKrzysztof Kozlowski 
148b9c2ae6cSKrzysztof Kozlowski struct icc_bwmon_data {
149b9c2ae6cSKrzysztof Kozlowski 	unsigned int sample_ms;
150650db9faSKrzysztof Kozlowski 	unsigned int count_unit_kb; /* kbytes */
151b9c2ae6cSKrzysztof Kozlowski 	unsigned int default_highbw_kbps;
152b9c2ae6cSKrzysztof Kozlowski 	unsigned int default_medbw_kbps;
153b9c2ae6cSKrzysztof Kozlowski 	unsigned int default_lowbw_kbps;
154b9c2ae6cSKrzysztof Kozlowski 	u8 zone1_thres_count;
155b9c2ae6cSKrzysztof Kozlowski 	u8 zone3_thres_count;
156e6f34184SKrzysztof Kozlowski 	unsigned int quirks;
157ec63dcd3SKrzysztof Kozlowski 
158ec63dcd3SKrzysztof Kozlowski 	const struct regmap_config *regmap_cfg;
159ec63dcd3SKrzysztof Kozlowski 	const struct reg_field *regmap_fields;
160b9c2ae6cSKrzysztof Kozlowski };
161b9c2ae6cSKrzysztof Kozlowski 
162b9c2ae6cSKrzysztof Kozlowski struct icc_bwmon {
163b9c2ae6cSKrzysztof Kozlowski 	struct device *dev;
1641dd5246eSKrzysztof Kozlowski 	const struct icc_bwmon_data *data;
165b9c2ae6cSKrzysztof Kozlowski 	int irq;
166b9c2ae6cSKrzysztof Kozlowski 
167ec63dcd3SKrzysztof Kozlowski 	struct regmap *regmap;
168ec63dcd3SKrzysztof Kozlowski 	struct regmap_field *regs[F_NUM_FIELDS];
169ec63dcd3SKrzysztof Kozlowski 
170b9c2ae6cSKrzysztof Kozlowski 	unsigned int max_bw_kbps;
171b9c2ae6cSKrzysztof Kozlowski 	unsigned int min_bw_kbps;
172b9c2ae6cSKrzysztof Kozlowski 	unsigned int target_kbps;
173b9c2ae6cSKrzysztof Kozlowski 	unsigned int current_kbps;
174b9c2ae6cSKrzysztof Kozlowski };
175b9c2ae6cSKrzysztof Kozlowski 
176ec63dcd3SKrzysztof Kozlowski /* BWMON v4 */
177ec63dcd3SKrzysztof Kozlowski static const struct reg_field msm8998_bwmon_reg_fields[] = {
178ec63dcd3SKrzysztof Kozlowski 	[F_GLOBAL_IRQ_CLEAR]	= REG_FIELD(BWMON_V4_GLOBAL_IRQ_CLEAR, 0, 0),
179ec63dcd3SKrzysztof Kozlowski 	[F_GLOBAL_IRQ_ENABLE]	= REG_FIELD(BWMON_V4_GLOBAL_IRQ_ENABLE, 0, 0),
180ec63dcd3SKrzysztof Kozlowski 	[F_IRQ_STATUS]		= REG_FIELD(BWMON_V4_IRQ_STATUS, 4, 7),
181ec63dcd3SKrzysztof Kozlowski 	[F_IRQ_CLEAR]		= REG_FIELD(BWMON_V4_IRQ_CLEAR, 4, 7),
182ec63dcd3SKrzysztof Kozlowski 	[F_IRQ_ENABLE]		= REG_FIELD(BWMON_V4_IRQ_ENABLE, 4, 7),
183ec63dcd3SKrzysztof Kozlowski 	/* F_ENABLE covers entire register to disable other features */
184ec63dcd3SKrzysztof Kozlowski 	[F_ENABLE]		= REG_FIELD(BWMON_V4_ENABLE, 0, 31),
185ec63dcd3SKrzysztof Kozlowski 	[F_CLEAR]		= REG_FIELD(BWMON_V4_CLEAR, 0, 1),
186ec63dcd3SKrzysztof Kozlowski 	[F_SAMPLE_WINDOW]	= REG_FIELD(BWMON_V4_SAMPLE_WINDOW, 0, 23),
187ec63dcd3SKrzysztof Kozlowski 	[F_THRESHOLD_HIGH]	= REG_FIELD(BWMON_V4_THRESHOLD_HIGH, 0, 11),
188ec63dcd3SKrzysztof Kozlowski 	[F_THRESHOLD_MED]	= REG_FIELD(BWMON_V4_THRESHOLD_MED, 0, 11),
189ec63dcd3SKrzysztof Kozlowski 	[F_THRESHOLD_LOW]	= REG_FIELD(BWMON_V4_THRESHOLD_LOW, 0, 11),
190ec63dcd3SKrzysztof Kozlowski 	[F_ZONE_ACTIONS_ZONE0]	= REG_FIELD(BWMON_V4_ZONE_ACTIONS, 0, 7),
191ec63dcd3SKrzysztof Kozlowski 	[F_ZONE_ACTIONS_ZONE1]	= REG_FIELD(BWMON_V4_ZONE_ACTIONS, 8, 15),
192ec63dcd3SKrzysztof Kozlowski 	[F_ZONE_ACTIONS_ZONE2]	= REG_FIELD(BWMON_V4_ZONE_ACTIONS, 16, 23),
193ec63dcd3SKrzysztof Kozlowski 	[F_ZONE_ACTIONS_ZONE3]	= REG_FIELD(BWMON_V4_ZONE_ACTIONS, 24, 31),
194ec63dcd3SKrzysztof Kozlowski 	[F_THRESHOLD_COUNT_ZONE0]	= REG_FIELD(BWMON_V4_THRESHOLD_COUNT, 0, 7),
195ec63dcd3SKrzysztof Kozlowski 	[F_THRESHOLD_COUNT_ZONE1]	= REG_FIELD(BWMON_V4_THRESHOLD_COUNT, 8, 15),
196ec63dcd3SKrzysztof Kozlowski 	[F_THRESHOLD_COUNT_ZONE2]	= REG_FIELD(BWMON_V4_THRESHOLD_COUNT, 16, 23),
197ec63dcd3SKrzysztof Kozlowski 	[F_THRESHOLD_COUNT_ZONE3]	= REG_FIELD(BWMON_V4_THRESHOLD_COUNT, 24, 31),
198ec63dcd3SKrzysztof Kozlowski 	[F_ZONE0_MAX]		= REG_FIELD(BWMON_V4_ZONE_MAX(0), 0, 11),
199ec63dcd3SKrzysztof Kozlowski 	[F_ZONE1_MAX]		= REG_FIELD(BWMON_V4_ZONE_MAX(1), 0, 11),
200ec63dcd3SKrzysztof Kozlowski 	[F_ZONE2_MAX]		= REG_FIELD(BWMON_V4_ZONE_MAX(2), 0, 11),
201ec63dcd3SKrzysztof Kozlowski 	[F_ZONE3_MAX]		= REG_FIELD(BWMON_V4_ZONE_MAX(3), 0, 11),
202ec63dcd3SKrzysztof Kozlowski };
203ec63dcd3SKrzysztof Kozlowski 
204ec63dcd3SKrzysztof Kozlowski static const struct regmap_range msm8998_bwmon_reg_noread_ranges[] = {
205ec63dcd3SKrzysztof Kozlowski 	regmap_reg_range(BWMON_V4_GLOBAL_IRQ_CLEAR, BWMON_V4_GLOBAL_IRQ_CLEAR),
206ec63dcd3SKrzysztof Kozlowski 	regmap_reg_range(BWMON_V4_IRQ_CLEAR, BWMON_V4_IRQ_CLEAR),
207ec63dcd3SKrzysztof Kozlowski 	regmap_reg_range(BWMON_V4_CLEAR, BWMON_V4_CLEAR),
208ec63dcd3SKrzysztof Kozlowski };
209ec63dcd3SKrzysztof Kozlowski 
210ec63dcd3SKrzysztof Kozlowski static const struct regmap_access_table msm8998_bwmon_reg_read_table = {
211ec63dcd3SKrzysztof Kozlowski 	.no_ranges	= msm8998_bwmon_reg_noread_ranges,
212ec63dcd3SKrzysztof Kozlowski 	.n_no_ranges	= ARRAY_SIZE(msm8998_bwmon_reg_noread_ranges),
213ec63dcd3SKrzysztof Kozlowski };
214ec63dcd3SKrzysztof Kozlowski 
215ec63dcd3SKrzysztof Kozlowski static const struct regmap_range msm8998_bwmon_reg_volatile_ranges[] = {
216ec63dcd3SKrzysztof Kozlowski 	regmap_reg_range(BWMON_V4_IRQ_STATUS, BWMON_V4_IRQ_STATUS),
217ec63dcd3SKrzysztof Kozlowski 	regmap_reg_range(BWMON_V4_ZONE_MAX(0), BWMON_V4_ZONE_MAX(3)),
218ec63dcd3SKrzysztof Kozlowski };
219ec63dcd3SKrzysztof Kozlowski 
220ec63dcd3SKrzysztof Kozlowski static const struct regmap_access_table msm8998_bwmon_reg_volatile_table = {
221ec63dcd3SKrzysztof Kozlowski 	.yes_ranges	= msm8998_bwmon_reg_volatile_ranges,
222ec63dcd3SKrzysztof Kozlowski 	.n_yes_ranges	= ARRAY_SIZE(msm8998_bwmon_reg_volatile_ranges),
223ec63dcd3SKrzysztof Kozlowski };
224ec63dcd3SKrzysztof Kozlowski 
225ec63dcd3SKrzysztof Kozlowski /*
226ec63dcd3SKrzysztof Kozlowski  * Fill the cache for non-readable registers only as rest does not really
227ec63dcd3SKrzysztof Kozlowski  * matter and can be read from the device.
228ec63dcd3SKrzysztof Kozlowski  */
229ec63dcd3SKrzysztof Kozlowski static const struct reg_default msm8998_bwmon_reg_defaults[] = {
230ec63dcd3SKrzysztof Kozlowski 	{ BWMON_V4_GLOBAL_IRQ_CLEAR, 0x0 },
231ec63dcd3SKrzysztof Kozlowski 	{ BWMON_V4_IRQ_CLEAR, 0x0 },
232ec63dcd3SKrzysztof Kozlowski 	{ BWMON_V4_CLEAR, 0x0 },
233ec63dcd3SKrzysztof Kozlowski };
234ec63dcd3SKrzysztof Kozlowski 
235ec63dcd3SKrzysztof Kozlowski static const struct regmap_config msm8998_bwmon_regmap_cfg = {
236ec63dcd3SKrzysztof Kozlowski 	.reg_bits		= 32,
237ec63dcd3SKrzysztof Kozlowski 	.reg_stride		= 4,
238ec63dcd3SKrzysztof Kozlowski 	.val_bits		= 32,
239ec63dcd3SKrzysztof Kozlowski 	/*
240ec63dcd3SKrzysztof Kozlowski 	 * No concurrent access expected - driver has one interrupt handler,
241ec63dcd3SKrzysztof Kozlowski 	 * regmap is not shared, no driver or user-space API.
242ec63dcd3SKrzysztof Kozlowski 	 */
243ec63dcd3SKrzysztof Kozlowski 	.disable_locking	= true,
244ec63dcd3SKrzysztof Kozlowski 	.rd_table		= &msm8998_bwmon_reg_read_table,
245ec63dcd3SKrzysztof Kozlowski 	.volatile_table		= &msm8998_bwmon_reg_volatile_table,
246ec63dcd3SKrzysztof Kozlowski 	.reg_defaults		= msm8998_bwmon_reg_defaults,
247ec63dcd3SKrzysztof Kozlowski 	.num_reg_defaults	= ARRAY_SIZE(msm8998_bwmon_reg_defaults),
248ec63dcd3SKrzysztof Kozlowski 	/*
249ec63dcd3SKrzysztof Kozlowski 	 * Cache is necessary for using regmap fields with non-readable
250ec63dcd3SKrzysztof Kozlowski 	 * registers.
251ec63dcd3SKrzysztof Kozlowski 	 */
252ec63dcd3SKrzysztof Kozlowski 	.cache_type		= REGCACHE_RBTREE,
253ec63dcd3SKrzysztof Kozlowski };
254ec63dcd3SKrzysztof Kozlowski 
25514af4ce0SKrzysztof Kozlowski /* BWMON v5 */
25614af4ce0SKrzysztof Kozlowski static const struct reg_field sdm845_llcc_bwmon_reg_fields[] = {
25714af4ce0SKrzysztof Kozlowski 	[F_GLOBAL_IRQ_CLEAR]	= {},
25814af4ce0SKrzysztof Kozlowski 	[F_GLOBAL_IRQ_ENABLE]	= {},
25914af4ce0SKrzysztof Kozlowski 	[F_IRQ_STATUS]		= REG_FIELD(BWMON_V5_IRQ_STATUS, 0, 3),
26014af4ce0SKrzysztof Kozlowski 	[F_IRQ_CLEAR]		= REG_FIELD(BWMON_V5_IRQ_CLEAR, 0, 3),
26114af4ce0SKrzysztof Kozlowski 	[F_IRQ_ENABLE]		= REG_FIELD(BWMON_V5_IRQ_ENABLE, 0, 3),
26214af4ce0SKrzysztof Kozlowski 	/* F_ENABLE covers entire register to disable other features */
26314af4ce0SKrzysztof Kozlowski 	[F_ENABLE]		= REG_FIELD(BWMON_V5_ENABLE, 0, 31),
26414af4ce0SKrzysztof Kozlowski 	[F_CLEAR]		= REG_FIELD(BWMON_V5_CLEAR, 0, 1),
26514af4ce0SKrzysztof Kozlowski 	[F_SAMPLE_WINDOW]	= REG_FIELD(BWMON_V5_SAMPLE_WINDOW, 0, 19),
26614af4ce0SKrzysztof Kozlowski 	[F_THRESHOLD_HIGH]	= REG_FIELD(BWMON_V5_THRESHOLD_HIGH, 0, 11),
26714af4ce0SKrzysztof Kozlowski 	[F_THRESHOLD_MED]	= REG_FIELD(BWMON_V5_THRESHOLD_MED, 0, 11),
26814af4ce0SKrzysztof Kozlowski 	[F_THRESHOLD_LOW]	= REG_FIELD(BWMON_V5_THRESHOLD_LOW, 0, 11),
26914af4ce0SKrzysztof Kozlowski 	[F_ZONE_ACTIONS_ZONE0]	= REG_FIELD(BWMON_V5_ZONE_ACTIONS, 0, 7),
27014af4ce0SKrzysztof Kozlowski 	[F_ZONE_ACTIONS_ZONE1]	= REG_FIELD(BWMON_V5_ZONE_ACTIONS, 8, 15),
27114af4ce0SKrzysztof Kozlowski 	[F_ZONE_ACTIONS_ZONE2]	= REG_FIELD(BWMON_V5_ZONE_ACTIONS, 16, 23),
27214af4ce0SKrzysztof Kozlowski 	[F_ZONE_ACTIONS_ZONE3]	= REG_FIELD(BWMON_V5_ZONE_ACTIONS, 24, 31),
27314af4ce0SKrzysztof Kozlowski 	[F_THRESHOLD_COUNT_ZONE0]	= REG_FIELD(BWMON_V5_THRESHOLD_COUNT, 0, 7),
27414af4ce0SKrzysztof Kozlowski 	[F_THRESHOLD_COUNT_ZONE1]	= REG_FIELD(BWMON_V5_THRESHOLD_COUNT, 8, 15),
27514af4ce0SKrzysztof Kozlowski 	[F_THRESHOLD_COUNT_ZONE2]	= REG_FIELD(BWMON_V5_THRESHOLD_COUNT, 16, 23),
27614af4ce0SKrzysztof Kozlowski 	[F_THRESHOLD_COUNT_ZONE3]	= REG_FIELD(BWMON_V5_THRESHOLD_COUNT, 24, 31),
27714af4ce0SKrzysztof Kozlowski 	[F_ZONE0_MAX]		= REG_FIELD(BWMON_V5_ZONE_MAX(0), 0, 11),
27814af4ce0SKrzysztof Kozlowski 	[F_ZONE1_MAX]		= REG_FIELD(BWMON_V5_ZONE_MAX(1), 0, 11),
27914af4ce0SKrzysztof Kozlowski 	[F_ZONE2_MAX]		= REG_FIELD(BWMON_V5_ZONE_MAX(2), 0, 11),
28014af4ce0SKrzysztof Kozlowski 	[F_ZONE3_MAX]		= REG_FIELD(BWMON_V5_ZONE_MAX(3), 0, 11),
28114af4ce0SKrzysztof Kozlowski };
28214af4ce0SKrzysztof Kozlowski 
28314af4ce0SKrzysztof Kozlowski static const struct regmap_range sdm845_llcc_bwmon_reg_noread_ranges[] = {
28414af4ce0SKrzysztof Kozlowski 	regmap_reg_range(BWMON_V5_IRQ_CLEAR, BWMON_V5_IRQ_CLEAR),
28514af4ce0SKrzysztof Kozlowski 	regmap_reg_range(BWMON_V5_CLEAR, BWMON_V5_CLEAR),
28614af4ce0SKrzysztof Kozlowski };
28714af4ce0SKrzysztof Kozlowski 
28814af4ce0SKrzysztof Kozlowski static const struct regmap_access_table sdm845_llcc_bwmon_reg_read_table = {
28914af4ce0SKrzysztof Kozlowski 	.no_ranges	= sdm845_llcc_bwmon_reg_noread_ranges,
29014af4ce0SKrzysztof Kozlowski 	.n_no_ranges	= ARRAY_SIZE(sdm845_llcc_bwmon_reg_noread_ranges),
29114af4ce0SKrzysztof Kozlowski };
29214af4ce0SKrzysztof Kozlowski 
29314af4ce0SKrzysztof Kozlowski static const struct regmap_range sdm845_llcc_bwmon_reg_volatile_ranges[] = {
29414af4ce0SKrzysztof Kozlowski 	regmap_reg_range(BWMON_V5_IRQ_STATUS, BWMON_V5_IRQ_STATUS),
29514af4ce0SKrzysztof Kozlowski 	regmap_reg_range(BWMON_V5_ZONE_MAX(0), BWMON_V5_ZONE_MAX(3)),
29614af4ce0SKrzysztof Kozlowski };
29714af4ce0SKrzysztof Kozlowski 
29814af4ce0SKrzysztof Kozlowski static const struct regmap_access_table sdm845_llcc_bwmon_reg_volatile_table = {
29914af4ce0SKrzysztof Kozlowski 	.yes_ranges	= sdm845_llcc_bwmon_reg_volatile_ranges,
30014af4ce0SKrzysztof Kozlowski 	.n_yes_ranges	= ARRAY_SIZE(sdm845_llcc_bwmon_reg_volatile_ranges),
30114af4ce0SKrzysztof Kozlowski };
30214af4ce0SKrzysztof Kozlowski 
30314af4ce0SKrzysztof Kozlowski /*
30414af4ce0SKrzysztof Kozlowski  * Fill the cache for non-readable registers only as rest does not really
30514af4ce0SKrzysztof Kozlowski  * matter and can be read from the device.
30614af4ce0SKrzysztof Kozlowski  */
30714af4ce0SKrzysztof Kozlowski static const struct reg_default sdm845_llcc_bwmon_reg_defaults[] = {
30814af4ce0SKrzysztof Kozlowski 	{ BWMON_V5_IRQ_CLEAR, 0x0 },
30914af4ce0SKrzysztof Kozlowski 	{ BWMON_V5_CLEAR, 0x0 },
31014af4ce0SKrzysztof Kozlowski };
31114af4ce0SKrzysztof Kozlowski 
31214af4ce0SKrzysztof Kozlowski static const struct regmap_config sdm845_llcc_bwmon_regmap_cfg = {
31314af4ce0SKrzysztof Kozlowski 	.reg_bits		= 32,
31414af4ce0SKrzysztof Kozlowski 	.reg_stride		= 4,
31514af4ce0SKrzysztof Kozlowski 	.val_bits		= 32,
31614af4ce0SKrzysztof Kozlowski 	/*
31714af4ce0SKrzysztof Kozlowski 	 * No concurrent access expected - driver has one interrupt handler,
31814af4ce0SKrzysztof Kozlowski 	 * regmap is not shared, no driver or user-space API.
31914af4ce0SKrzysztof Kozlowski 	 */
32014af4ce0SKrzysztof Kozlowski 	.disable_locking	= true,
32114af4ce0SKrzysztof Kozlowski 	.rd_table		= &sdm845_llcc_bwmon_reg_read_table,
32214af4ce0SKrzysztof Kozlowski 	.volatile_table		= &sdm845_llcc_bwmon_reg_volatile_table,
32314af4ce0SKrzysztof Kozlowski 	.reg_defaults		= sdm845_llcc_bwmon_reg_defaults,
32414af4ce0SKrzysztof Kozlowski 	.num_reg_defaults	= ARRAY_SIZE(sdm845_llcc_bwmon_reg_defaults),
32514af4ce0SKrzysztof Kozlowski 	/*
32614af4ce0SKrzysztof Kozlowski 	 * Cache is necessary for using regmap fields with non-readable
32714af4ce0SKrzysztof Kozlowski 	 * registers.
32814af4ce0SKrzysztof Kozlowski 	 */
32914af4ce0SKrzysztof Kozlowski 	.cache_type		= REGCACHE_RBTREE,
33014af4ce0SKrzysztof Kozlowski };
33114af4ce0SKrzysztof Kozlowski 
332956deab5SKrzysztof Kozlowski static void bwmon_clear_counters(struct icc_bwmon *bwmon, bool clear_all)
333b9c2ae6cSKrzysztof Kozlowski {
334956deab5SKrzysztof Kozlowski 	unsigned int val = BWMON_CLEAR_CLEAR;
335956deab5SKrzysztof Kozlowski 
336956deab5SKrzysztof Kozlowski 	if (clear_all)
337956deab5SKrzysztof Kozlowski 		val |= BWMON_CLEAR_CLEAR_ALL;
338b9c2ae6cSKrzysztof Kozlowski 	/*
339b9c2ae6cSKrzysztof Kozlowski 	 * Clear counters. The order and barriers are
340b9c2ae6cSKrzysztof Kozlowski 	 * important. Quoting downstream Qualcomm msm-4.9 tree:
341b9c2ae6cSKrzysztof Kozlowski 	 *
342b9c2ae6cSKrzysztof Kozlowski 	 * The counter clear and IRQ clear bits are not in the same 4KB
343b9c2ae6cSKrzysztof Kozlowski 	 * region. So, we need to make sure the counter clear is completed
344b9c2ae6cSKrzysztof Kozlowski 	 * before we try to clear the IRQ or do any other counter operations.
345b9c2ae6cSKrzysztof Kozlowski 	 */
346ec63dcd3SKrzysztof Kozlowski 	regmap_field_force_write(bwmon->regs[F_CLEAR], val);
347*cdad59c2SRajendra Nayak 	if (bwmon->data->quirks & BWMON_NEEDS_FORCE_CLEAR)
348*cdad59c2SRajendra Nayak 		regmap_field_force_write(bwmon->regs[F_CLEAR], 0);
349b9c2ae6cSKrzysztof Kozlowski }
350b9c2ae6cSKrzysztof Kozlowski 
351b9c2ae6cSKrzysztof Kozlowski static void bwmon_clear_irq(struct icc_bwmon *bwmon)
352b9c2ae6cSKrzysztof Kozlowski {
353b9c2ae6cSKrzysztof Kozlowski 	/*
354b9c2ae6cSKrzysztof Kozlowski 	 * Clear zone and global interrupts. The order and barriers are
355b9c2ae6cSKrzysztof Kozlowski 	 * important. Quoting downstream Qualcomm msm-4.9 tree:
356b9c2ae6cSKrzysztof Kozlowski 	 *
357b9c2ae6cSKrzysztof Kozlowski 	 * Synchronize the local interrupt clear in mon_irq_clear()
358b9c2ae6cSKrzysztof Kozlowski 	 * with the global interrupt clear here. Otherwise, the CPU
359b9c2ae6cSKrzysztof Kozlowski 	 * may reorder the two writes and clear the global interrupt
360b9c2ae6cSKrzysztof Kozlowski 	 * before the local interrupt, causing the global interrupt
361b9c2ae6cSKrzysztof Kozlowski 	 * to be retriggered by the local interrupt still being high.
362b9c2ae6cSKrzysztof Kozlowski 	 *
363b9c2ae6cSKrzysztof Kozlowski 	 * Similarly, because the global registers are in a different
364b9c2ae6cSKrzysztof Kozlowski 	 * region than the local registers, we need to ensure any register
365b9c2ae6cSKrzysztof Kozlowski 	 * writes to enable the monitor after this call are ordered with the
366b9c2ae6cSKrzysztof Kozlowski 	 * clearing here so that local writes don't happen before the
367b9c2ae6cSKrzysztof Kozlowski 	 * interrupt is cleared.
368b9c2ae6cSKrzysztof Kozlowski 	 */
369ec63dcd3SKrzysztof Kozlowski 	regmap_field_force_write(bwmon->regs[F_IRQ_CLEAR], BWMON_IRQ_ENABLE_MASK);
370*cdad59c2SRajendra Nayak 	if (bwmon->data->quirks & BWMON_NEEDS_FORCE_CLEAR)
371*cdad59c2SRajendra Nayak 		regmap_field_force_write(bwmon->regs[F_IRQ_CLEAR], 0);
372e6f34184SKrzysztof Kozlowski 	if (bwmon->data->quirks & BWMON_HAS_GLOBAL_IRQ)
373ec63dcd3SKrzysztof Kozlowski 		regmap_field_force_write(bwmon->regs[F_GLOBAL_IRQ_CLEAR],
374ec63dcd3SKrzysztof Kozlowski 					 BWMON_V4_GLOBAL_IRQ_ENABLE_ENABLE);
375b9c2ae6cSKrzysztof Kozlowski }
376b9c2ae6cSKrzysztof Kozlowski 
377b9c2ae6cSKrzysztof Kozlowski static void bwmon_disable(struct icc_bwmon *bwmon)
378b9c2ae6cSKrzysztof Kozlowski {
379b9c2ae6cSKrzysztof Kozlowski 	/* Disable interrupts. Strict ordering, see bwmon_clear_irq(). */
380e6f34184SKrzysztof Kozlowski 	if (bwmon->data->quirks & BWMON_HAS_GLOBAL_IRQ)
381ec63dcd3SKrzysztof Kozlowski 		regmap_field_write(bwmon->regs[F_GLOBAL_IRQ_ENABLE], 0x0);
382ec63dcd3SKrzysztof Kozlowski 	regmap_field_write(bwmon->regs[F_IRQ_ENABLE], 0x0);
383b9c2ae6cSKrzysztof Kozlowski 
384b9c2ae6cSKrzysztof Kozlowski 	/*
385b9c2ae6cSKrzysztof Kozlowski 	 * Disable bwmon. Must happen before bwmon_clear_irq() to avoid spurious
386b9c2ae6cSKrzysztof Kozlowski 	 * IRQ.
387b9c2ae6cSKrzysztof Kozlowski 	 */
388ec63dcd3SKrzysztof Kozlowski 	regmap_field_write(bwmon->regs[F_ENABLE], 0x0);
389b9c2ae6cSKrzysztof Kozlowski }
390b9c2ae6cSKrzysztof Kozlowski 
391b9c2ae6cSKrzysztof Kozlowski static void bwmon_enable(struct icc_bwmon *bwmon, unsigned int irq_enable)
392b9c2ae6cSKrzysztof Kozlowski {
393b9c2ae6cSKrzysztof Kozlowski 	/* Enable interrupts */
394e6f34184SKrzysztof Kozlowski 	if (bwmon->data->quirks & BWMON_HAS_GLOBAL_IRQ)
395ec63dcd3SKrzysztof Kozlowski 		regmap_field_write(bwmon->regs[F_GLOBAL_IRQ_ENABLE],
396ec63dcd3SKrzysztof Kozlowski 				   BWMON_V4_GLOBAL_IRQ_ENABLE_ENABLE);
397ec63dcd3SKrzysztof Kozlowski 	regmap_field_write(bwmon->regs[F_IRQ_ENABLE], irq_enable);
398b9c2ae6cSKrzysztof Kozlowski 
399b9c2ae6cSKrzysztof Kozlowski 	/* Enable bwmon */
400ec63dcd3SKrzysztof Kozlowski 	regmap_field_write(bwmon->regs[F_ENABLE], BWMON_ENABLE_ENABLE);
401b9c2ae6cSKrzysztof Kozlowski }
402b9c2ae6cSKrzysztof Kozlowski 
403650db9faSKrzysztof Kozlowski static unsigned int bwmon_kbps_to_count(struct icc_bwmon *bwmon,
404650db9faSKrzysztof Kozlowski 					unsigned int kbps)
405b9c2ae6cSKrzysztof Kozlowski {
406650db9faSKrzysztof Kozlowski 	return kbps / bwmon->data->count_unit_kb;
407b9c2ae6cSKrzysztof Kozlowski }
408b9c2ae6cSKrzysztof Kozlowski 
409ec63dcd3SKrzysztof Kozlowski static void bwmon_set_threshold(struct icc_bwmon *bwmon,
410ec63dcd3SKrzysztof Kozlowski 				struct regmap_field *reg, unsigned int kbps)
411b9c2ae6cSKrzysztof Kozlowski {
412b9c2ae6cSKrzysztof Kozlowski 	unsigned int thres;
413b9c2ae6cSKrzysztof Kozlowski 
414650db9faSKrzysztof Kozlowski 	thres = mult_frac(bwmon_kbps_to_count(bwmon, kbps),
415650db9faSKrzysztof Kozlowski 			  bwmon->data->sample_ms, MSEC_PER_SEC);
416ec63dcd3SKrzysztof Kozlowski 	regmap_field_write(reg, thres);
417b9c2ae6cSKrzysztof Kozlowski }
418b9c2ae6cSKrzysztof Kozlowski 
4191dd5246eSKrzysztof Kozlowski static void bwmon_start(struct icc_bwmon *bwmon)
420b9c2ae6cSKrzysztof Kozlowski {
4211dd5246eSKrzysztof Kozlowski 	const struct icc_bwmon_data *data = bwmon->data;
422b9c2ae6cSKrzysztof Kozlowski 	int window;
423b9c2ae6cSKrzysztof Kozlowski 
424956deab5SKrzysztof Kozlowski 	bwmon_clear_counters(bwmon, true);
425b9c2ae6cSKrzysztof Kozlowski 
4261dd5246eSKrzysztof Kozlowski 	window = mult_frac(bwmon->data->sample_ms, HW_TIMER_HZ, MSEC_PER_SEC);
42714af4ce0SKrzysztof Kozlowski 	/* Maximum sampling window: 0xffffff for v4 and 0xfffff for v5 */
428ec63dcd3SKrzysztof Kozlowski 	regmap_field_write(bwmon->regs[F_SAMPLE_WINDOW], window);
429b9c2ae6cSKrzysztof Kozlowski 
430ec63dcd3SKrzysztof Kozlowski 	bwmon_set_threshold(bwmon, bwmon->regs[F_THRESHOLD_HIGH],
431b9c2ae6cSKrzysztof Kozlowski 			    data->default_highbw_kbps);
432ec63dcd3SKrzysztof Kozlowski 	bwmon_set_threshold(bwmon, bwmon->regs[F_THRESHOLD_MED],
433b9c2ae6cSKrzysztof Kozlowski 			    data->default_medbw_kbps);
434ec63dcd3SKrzysztof Kozlowski 	bwmon_set_threshold(bwmon, bwmon->regs[F_THRESHOLD_LOW],
435b9c2ae6cSKrzysztof Kozlowski 			    data->default_lowbw_kbps);
436b9c2ae6cSKrzysztof Kozlowski 
437ec63dcd3SKrzysztof Kozlowski 	regmap_field_write(bwmon->regs[F_THRESHOLD_COUNT_ZONE0],
438ec63dcd3SKrzysztof Kozlowski 			   BWMON_THRESHOLD_COUNT_ZONE0_DEFAULT);
439ec63dcd3SKrzysztof Kozlowski 	regmap_field_write(bwmon->regs[F_THRESHOLD_COUNT_ZONE1],
440ec63dcd3SKrzysztof Kozlowski 			   data->zone1_thres_count);
441ec63dcd3SKrzysztof Kozlowski 	regmap_field_write(bwmon->regs[F_THRESHOLD_COUNT_ZONE2],
442ec63dcd3SKrzysztof Kozlowski 			   BWMON_THRESHOLD_COUNT_ZONE2_DEFAULT);
443ec63dcd3SKrzysztof Kozlowski 	regmap_field_write(bwmon->regs[F_THRESHOLD_COUNT_ZONE3],
444ec63dcd3SKrzysztof Kozlowski 			   data->zone3_thres_count);
445ec63dcd3SKrzysztof Kozlowski 
446ec63dcd3SKrzysztof Kozlowski 	regmap_field_write(bwmon->regs[F_ZONE_ACTIONS_ZONE0],
447ec63dcd3SKrzysztof Kozlowski 			   BWMON_ZONE_ACTIONS_ZONE0);
448ec63dcd3SKrzysztof Kozlowski 	regmap_field_write(bwmon->regs[F_ZONE_ACTIONS_ZONE1],
449ec63dcd3SKrzysztof Kozlowski 			   BWMON_ZONE_ACTIONS_ZONE1);
450ec63dcd3SKrzysztof Kozlowski 	regmap_field_write(bwmon->regs[F_ZONE_ACTIONS_ZONE2],
451ec63dcd3SKrzysztof Kozlowski 			   BWMON_ZONE_ACTIONS_ZONE2);
452ec63dcd3SKrzysztof Kozlowski 	regmap_field_write(bwmon->regs[F_ZONE_ACTIONS_ZONE3],
453ec63dcd3SKrzysztof Kozlowski 			   BWMON_ZONE_ACTIONS_ZONE3);
454b9c2ae6cSKrzysztof Kozlowski 
455b9c2ae6cSKrzysztof Kozlowski 	bwmon_clear_irq(bwmon);
456b9c2ae6cSKrzysztof Kozlowski 	bwmon_enable(bwmon, BWMON_IRQ_ENABLE_MASK);
457b9c2ae6cSKrzysztof Kozlowski }
458b9c2ae6cSKrzysztof Kozlowski 
459b9c2ae6cSKrzysztof Kozlowski static irqreturn_t bwmon_intr(int irq, void *dev_id)
460b9c2ae6cSKrzysztof Kozlowski {
461b9c2ae6cSKrzysztof Kozlowski 	struct icc_bwmon *bwmon = dev_id;
462b9c2ae6cSKrzysztof Kozlowski 	unsigned int status, max;
463b9c2ae6cSKrzysztof Kozlowski 	int zone;
464b9c2ae6cSKrzysztof Kozlowski 
465ec63dcd3SKrzysztof Kozlowski 	if (regmap_field_read(bwmon->regs[F_IRQ_STATUS], &status))
466ec63dcd3SKrzysztof Kozlowski 		return IRQ_NONE;
467ec63dcd3SKrzysztof Kozlowski 
468b9c2ae6cSKrzysztof Kozlowski 	status &= BWMON_IRQ_ENABLE_MASK;
469b9c2ae6cSKrzysztof Kozlowski 	if (!status) {
470b9c2ae6cSKrzysztof Kozlowski 		/*
471b9c2ae6cSKrzysztof Kozlowski 		 * Only zone 1 and zone 3 interrupts are enabled but zone 2
472b9c2ae6cSKrzysztof Kozlowski 		 * threshold could be hit and trigger interrupt even if not
473b9c2ae6cSKrzysztof Kozlowski 		 * enabled.
474b9c2ae6cSKrzysztof Kozlowski 		 * Such spurious interrupt might come with valuable max count or
475b9c2ae6cSKrzysztof Kozlowski 		 * not, so solution would be to always check all
476b9c2ae6cSKrzysztof Kozlowski 		 * BWMON_ZONE_MAX() registers to find the highest value.
477b9c2ae6cSKrzysztof Kozlowski 		 * Such case is currently ignored.
478b9c2ae6cSKrzysztof Kozlowski 		 */
479b9c2ae6cSKrzysztof Kozlowski 		return IRQ_NONE;
480b9c2ae6cSKrzysztof Kozlowski 	}
481b9c2ae6cSKrzysztof Kozlowski 
482b9c2ae6cSKrzysztof Kozlowski 	bwmon_disable(bwmon);
483b9c2ae6cSKrzysztof Kozlowski 
484ec63dcd3SKrzysztof Kozlowski 	zone = get_bitmask_order(status) - 1;
485b9c2ae6cSKrzysztof Kozlowski 	/*
486b9c2ae6cSKrzysztof Kozlowski 	 * Zone max bytes count register returns count units within sampling
487b9c2ae6cSKrzysztof Kozlowski 	 * window.  Downstream kernel for BWMONv4 (called BWMON type 2 in
488b9c2ae6cSKrzysztof Kozlowski 	 * downstream) always increments the max bytes count by one.
489b9c2ae6cSKrzysztof Kozlowski 	 */
490ec63dcd3SKrzysztof Kozlowski 	if (regmap_field_read(bwmon->regs[F_ZONE0_MAX + zone], &max))
491ec63dcd3SKrzysztof Kozlowski 		return IRQ_NONE;
492ec63dcd3SKrzysztof Kozlowski 
493ec63dcd3SKrzysztof Kozlowski 	max += 1;
494650db9faSKrzysztof Kozlowski 	max *= bwmon->data->count_unit_kb;
4951dd5246eSKrzysztof Kozlowski 	bwmon->target_kbps = mult_frac(max, MSEC_PER_SEC, bwmon->data->sample_ms);
496b9c2ae6cSKrzysztof Kozlowski 
497b9c2ae6cSKrzysztof Kozlowski 	return IRQ_WAKE_THREAD;
498b9c2ae6cSKrzysztof Kozlowski }
499b9c2ae6cSKrzysztof Kozlowski 
500b9c2ae6cSKrzysztof Kozlowski static irqreturn_t bwmon_intr_thread(int irq, void *dev_id)
501b9c2ae6cSKrzysztof Kozlowski {
502b9c2ae6cSKrzysztof Kozlowski 	struct icc_bwmon *bwmon = dev_id;
503b9c2ae6cSKrzysztof Kozlowski 	unsigned int irq_enable = 0;
504b9c2ae6cSKrzysztof Kozlowski 	struct dev_pm_opp *opp, *target_opp;
505b9c2ae6cSKrzysztof Kozlowski 	unsigned int bw_kbps, up_kbps, down_kbps;
506b9c2ae6cSKrzysztof Kozlowski 
507b9c2ae6cSKrzysztof Kozlowski 	bw_kbps = bwmon->target_kbps;
508b9c2ae6cSKrzysztof Kozlowski 
509b9c2ae6cSKrzysztof Kozlowski 	target_opp = dev_pm_opp_find_bw_ceil(bwmon->dev, &bw_kbps, 0);
510b9c2ae6cSKrzysztof Kozlowski 	if (IS_ERR(target_opp) && PTR_ERR(target_opp) == -ERANGE)
511b9c2ae6cSKrzysztof Kozlowski 		target_opp = dev_pm_opp_find_bw_floor(bwmon->dev, &bw_kbps, 0);
512b9c2ae6cSKrzysztof Kozlowski 
513b9c2ae6cSKrzysztof Kozlowski 	bwmon->target_kbps = bw_kbps;
514b9c2ae6cSKrzysztof Kozlowski 
515b9c2ae6cSKrzysztof Kozlowski 	bw_kbps--;
516b9c2ae6cSKrzysztof Kozlowski 	opp = dev_pm_opp_find_bw_floor(bwmon->dev, &bw_kbps, 0);
517b9c2ae6cSKrzysztof Kozlowski 	if (IS_ERR(opp) && PTR_ERR(opp) == -ERANGE)
518b9c2ae6cSKrzysztof Kozlowski 		down_kbps = bwmon->target_kbps;
519b9c2ae6cSKrzysztof Kozlowski 	else
520b9c2ae6cSKrzysztof Kozlowski 		down_kbps = bw_kbps;
521b9c2ae6cSKrzysztof Kozlowski 
522b9c2ae6cSKrzysztof Kozlowski 	up_kbps = bwmon->target_kbps + 1;
523b9c2ae6cSKrzysztof Kozlowski 
524b9c2ae6cSKrzysztof Kozlowski 	if (bwmon->target_kbps >= bwmon->max_bw_kbps)
525ec63dcd3SKrzysztof Kozlowski 		irq_enable = BIT(1);
526b9c2ae6cSKrzysztof Kozlowski 	else if (bwmon->target_kbps <= bwmon->min_bw_kbps)
527ec63dcd3SKrzysztof Kozlowski 		irq_enable = BIT(3);
528b9c2ae6cSKrzysztof Kozlowski 	else
529b9c2ae6cSKrzysztof Kozlowski 		irq_enable = BWMON_IRQ_ENABLE_MASK;
530b9c2ae6cSKrzysztof Kozlowski 
531ec63dcd3SKrzysztof Kozlowski 	bwmon_set_threshold(bwmon, bwmon->regs[F_THRESHOLD_HIGH],
532ec63dcd3SKrzysztof Kozlowski 			    up_kbps);
533ec63dcd3SKrzysztof Kozlowski 	bwmon_set_threshold(bwmon, bwmon->regs[F_THRESHOLD_MED],
534ec63dcd3SKrzysztof Kozlowski 			    down_kbps);
535956deab5SKrzysztof Kozlowski 	bwmon_clear_counters(bwmon, false);
536b9c2ae6cSKrzysztof Kozlowski 	bwmon_clear_irq(bwmon);
537b9c2ae6cSKrzysztof Kozlowski 	bwmon_enable(bwmon, irq_enable);
538b9c2ae6cSKrzysztof Kozlowski 
539b9c2ae6cSKrzysztof Kozlowski 	if (bwmon->target_kbps == bwmon->current_kbps)
540b9c2ae6cSKrzysztof Kozlowski 		goto out;
541b9c2ae6cSKrzysztof Kozlowski 
542b9c2ae6cSKrzysztof Kozlowski 	dev_pm_opp_set_opp(bwmon->dev, target_opp);
543b9c2ae6cSKrzysztof Kozlowski 	bwmon->current_kbps = bwmon->target_kbps;
544b9c2ae6cSKrzysztof Kozlowski 
545b9c2ae6cSKrzysztof Kozlowski out:
546b9c2ae6cSKrzysztof Kozlowski 	dev_pm_opp_put(target_opp);
547b9c2ae6cSKrzysztof Kozlowski 	if (!IS_ERR(opp))
548b9c2ae6cSKrzysztof Kozlowski 		dev_pm_opp_put(opp);
549b9c2ae6cSKrzysztof Kozlowski 
550b9c2ae6cSKrzysztof Kozlowski 	return IRQ_HANDLED;
551b9c2ae6cSKrzysztof Kozlowski }
552b9c2ae6cSKrzysztof Kozlowski 
553ec63dcd3SKrzysztof Kozlowski static int bwmon_init_regmap(struct platform_device *pdev,
554ec63dcd3SKrzysztof Kozlowski 			     struct icc_bwmon *bwmon)
555ec63dcd3SKrzysztof Kozlowski {
556ec63dcd3SKrzysztof Kozlowski 	struct device *dev = &pdev->dev;
557ec63dcd3SKrzysztof Kozlowski 	void __iomem *base;
558ec63dcd3SKrzysztof Kozlowski 	struct regmap *map;
559ec63dcd3SKrzysztof Kozlowski 
560ec63dcd3SKrzysztof Kozlowski 	base = devm_platform_ioremap_resource(pdev, 0);
561ec63dcd3SKrzysztof Kozlowski 	if (IS_ERR(base))
562ec63dcd3SKrzysztof Kozlowski 		return dev_err_probe(dev, PTR_ERR(base),
563ec63dcd3SKrzysztof Kozlowski 				     "failed to map bwmon registers\n");
564ec63dcd3SKrzysztof Kozlowski 
565ec63dcd3SKrzysztof Kozlowski 	map = devm_regmap_init_mmio(dev, base, bwmon->data->regmap_cfg);
566ec63dcd3SKrzysztof Kozlowski 	if (IS_ERR(map))
567ec63dcd3SKrzysztof Kozlowski 		return dev_err_probe(dev, PTR_ERR(map),
568ec63dcd3SKrzysztof Kozlowski 				     "failed to initialize regmap\n");
569ec63dcd3SKrzysztof Kozlowski 
570ec63dcd3SKrzysztof Kozlowski 	BUILD_BUG_ON(ARRAY_SIZE(msm8998_bwmon_reg_fields) != F_NUM_FIELDS);
57114af4ce0SKrzysztof Kozlowski 	BUILD_BUG_ON(ARRAY_SIZE(sdm845_llcc_bwmon_reg_fields) != F_NUM_FIELDS);
5727eb89c17SJinpeng Cui 
5737eb89c17SJinpeng Cui 	return devm_regmap_field_bulk_alloc(dev, map, bwmon->regs,
574ec63dcd3SKrzysztof Kozlowski 					   bwmon->data->regmap_fields,
575ec63dcd3SKrzysztof Kozlowski 					   F_NUM_FIELDS);
576ec63dcd3SKrzysztof Kozlowski }
577ec63dcd3SKrzysztof Kozlowski 
578b9c2ae6cSKrzysztof Kozlowski static int bwmon_probe(struct platform_device *pdev)
579b9c2ae6cSKrzysztof Kozlowski {
580b9c2ae6cSKrzysztof Kozlowski 	struct device *dev = &pdev->dev;
581b9c2ae6cSKrzysztof Kozlowski 	struct dev_pm_opp *opp;
582b9c2ae6cSKrzysztof Kozlowski 	struct icc_bwmon *bwmon;
583b9c2ae6cSKrzysztof Kozlowski 	int ret;
584b9c2ae6cSKrzysztof Kozlowski 
585b9c2ae6cSKrzysztof Kozlowski 	bwmon = devm_kzalloc(dev, sizeof(*bwmon), GFP_KERNEL);
586b9c2ae6cSKrzysztof Kozlowski 	if (!bwmon)
587b9c2ae6cSKrzysztof Kozlowski 		return -ENOMEM;
588b9c2ae6cSKrzysztof Kozlowski 
5891dd5246eSKrzysztof Kozlowski 	bwmon->data = of_device_get_match_data(dev);
590b9c2ae6cSKrzysztof Kozlowski 
591ec63dcd3SKrzysztof Kozlowski 	ret = bwmon_init_regmap(pdev, bwmon);
592ec63dcd3SKrzysztof Kozlowski 	if (ret)
593ec63dcd3SKrzysztof Kozlowski 		return ret;
594b9c2ae6cSKrzysztof Kozlowski 
595b9c2ae6cSKrzysztof Kozlowski 	bwmon->irq = platform_get_irq(pdev, 0);
596947bb0d1SYang Li 	if (bwmon->irq < 0)
597b9c2ae6cSKrzysztof Kozlowski 		return bwmon->irq;
598b9c2ae6cSKrzysztof Kozlowski 
599b9c2ae6cSKrzysztof Kozlowski 	ret = devm_pm_opp_of_add_table(dev);
600b9c2ae6cSKrzysztof Kozlowski 	if (ret)
601b9c2ae6cSKrzysztof Kozlowski 		return dev_err_probe(dev, ret, "failed to add OPP table\n");
602b9c2ae6cSKrzysztof Kozlowski 
603b9c2ae6cSKrzysztof Kozlowski 	bwmon->max_bw_kbps = UINT_MAX;
604b9c2ae6cSKrzysztof Kozlowski 	opp = dev_pm_opp_find_bw_floor(dev, &bwmon->max_bw_kbps, 0);
605b9c2ae6cSKrzysztof Kozlowski 	if (IS_ERR(opp))
606b9c2ae6cSKrzysztof Kozlowski 		return dev_err_probe(dev, ret, "failed to find max peak bandwidth\n");
607b9c2ae6cSKrzysztof Kozlowski 
608b9c2ae6cSKrzysztof Kozlowski 	bwmon->min_bw_kbps = 0;
609b9c2ae6cSKrzysztof Kozlowski 	opp = dev_pm_opp_find_bw_ceil(dev, &bwmon->min_bw_kbps, 0);
610b9c2ae6cSKrzysztof Kozlowski 	if (IS_ERR(opp))
611b9c2ae6cSKrzysztof Kozlowski 		return dev_err_probe(dev, ret, "failed to find min peak bandwidth\n");
612b9c2ae6cSKrzysztof Kozlowski 
613b9c2ae6cSKrzysztof Kozlowski 	bwmon->dev = dev;
614b9c2ae6cSKrzysztof Kozlowski 
615b9c2ae6cSKrzysztof Kozlowski 	bwmon_disable(bwmon);
616b9c2ae6cSKrzysztof Kozlowski 	ret = devm_request_threaded_irq(dev, bwmon->irq, bwmon_intr,
617b9c2ae6cSKrzysztof Kozlowski 					bwmon_intr_thread,
618b9c2ae6cSKrzysztof Kozlowski 					IRQF_ONESHOT, dev_name(dev), bwmon);
619b9c2ae6cSKrzysztof Kozlowski 	if (ret)
620b9c2ae6cSKrzysztof Kozlowski 		return dev_err_probe(dev, ret, "failed to request IRQ\n");
621b9c2ae6cSKrzysztof Kozlowski 
622b9c2ae6cSKrzysztof Kozlowski 	platform_set_drvdata(pdev, bwmon);
6231dd5246eSKrzysztof Kozlowski 	bwmon_start(bwmon);
624b9c2ae6cSKrzysztof Kozlowski 
625b9c2ae6cSKrzysztof Kozlowski 	return 0;
626b9c2ae6cSKrzysztof Kozlowski }
627b9c2ae6cSKrzysztof Kozlowski 
628b9c2ae6cSKrzysztof Kozlowski static int bwmon_remove(struct platform_device *pdev)
629b9c2ae6cSKrzysztof Kozlowski {
630b9c2ae6cSKrzysztof Kozlowski 	struct icc_bwmon *bwmon = platform_get_drvdata(pdev);
631b9c2ae6cSKrzysztof Kozlowski 
632b9c2ae6cSKrzysztof Kozlowski 	bwmon_disable(bwmon);
633b9c2ae6cSKrzysztof Kozlowski 
634b9c2ae6cSKrzysztof Kozlowski 	return 0;
635b9c2ae6cSKrzysztof Kozlowski }
636b9c2ae6cSKrzysztof Kozlowski 
637b9c2ae6cSKrzysztof Kozlowski static const struct icc_bwmon_data msm8998_bwmon_data = {
638b9c2ae6cSKrzysztof Kozlowski 	.sample_ms = 4,
639650db9faSKrzysztof Kozlowski 	.count_unit_kb = 64,
640b9c2ae6cSKrzysztof Kozlowski 	.default_highbw_kbps = 4800 * 1024, /* 4.8 GBps */
641b9c2ae6cSKrzysztof Kozlowski 	.default_medbw_kbps = 512 * 1024, /* 512 MBps */
642b9c2ae6cSKrzysztof Kozlowski 	.default_lowbw_kbps = 0,
643b9c2ae6cSKrzysztof Kozlowski 	.zone1_thres_count = 16,
644b9c2ae6cSKrzysztof Kozlowski 	.zone3_thres_count = 1,
645e6f34184SKrzysztof Kozlowski 	.quirks = BWMON_HAS_GLOBAL_IRQ,
646ec63dcd3SKrzysztof Kozlowski 	.regmap_fields = msm8998_bwmon_reg_fields,
647ec63dcd3SKrzysztof Kozlowski 	.regmap_cfg = &msm8998_bwmon_regmap_cfg,
648b9c2ae6cSKrzysztof Kozlowski };
649b9c2ae6cSKrzysztof Kozlowski 
65014af4ce0SKrzysztof Kozlowski static const struct icc_bwmon_data sdm845_llcc_bwmon_data = {
65114af4ce0SKrzysztof Kozlowski 	.sample_ms = 4,
65214af4ce0SKrzysztof Kozlowski 	.count_unit_kb = 1024,
65314af4ce0SKrzysztof Kozlowski 	.default_highbw_kbps = 800 * 1024, /* 800 MBps */
65414af4ce0SKrzysztof Kozlowski 	.default_medbw_kbps = 256 * 1024, /* 256 MBps */
65514af4ce0SKrzysztof Kozlowski 	.default_lowbw_kbps = 0,
65614af4ce0SKrzysztof Kozlowski 	.zone1_thres_count = 16,
65714af4ce0SKrzysztof Kozlowski 	.zone3_thres_count = 1,
65814af4ce0SKrzysztof Kozlowski 	.regmap_fields = sdm845_llcc_bwmon_reg_fields,
65914af4ce0SKrzysztof Kozlowski 	.regmap_cfg = &sdm845_llcc_bwmon_regmap_cfg,
66014af4ce0SKrzysztof Kozlowski };
66114af4ce0SKrzysztof Kozlowski 
6621335fc5bSRajendra Nayak static const struct icc_bwmon_data sc7280_llcc_bwmon_data = {
6631335fc5bSRajendra Nayak 	.sample_ms = 4,
6641335fc5bSRajendra Nayak 	.count_unit_kb = 64,
6651335fc5bSRajendra Nayak 	.default_highbw_kbps = 800 * 1024, /* 800 MBps */
6661335fc5bSRajendra Nayak 	.default_medbw_kbps = 256 * 1024, /* 256 MBps */
6671335fc5bSRajendra Nayak 	.default_lowbw_kbps = 0,
6681335fc5bSRajendra Nayak 	.zone1_thres_count = 16,
6691335fc5bSRajendra Nayak 	.zone3_thres_count = 1,
670*cdad59c2SRajendra Nayak 	.quirks = BWMON_NEEDS_FORCE_CLEAR,
6711335fc5bSRajendra Nayak 	.regmap_fields = sdm845_llcc_bwmon_reg_fields,
6721335fc5bSRajendra Nayak 	.regmap_cfg = &sdm845_llcc_bwmon_regmap_cfg,
6731335fc5bSRajendra Nayak };
6741335fc5bSRajendra Nayak 
675b9c2ae6cSKrzysztof Kozlowski static const struct of_device_id bwmon_of_match[] = {
67614af4ce0SKrzysztof Kozlowski 	{
67714af4ce0SKrzysztof Kozlowski 		.compatible = "qcom,msm8998-bwmon",
67814af4ce0SKrzysztof Kozlowski 		.data = &msm8998_bwmon_data
67914af4ce0SKrzysztof Kozlowski 	}, {
68014af4ce0SKrzysztof Kozlowski 		.compatible = "qcom,sdm845-llcc-bwmon",
68114af4ce0SKrzysztof Kozlowski 		.data = &sdm845_llcc_bwmon_data
6821335fc5bSRajendra Nayak 	}, {
6831335fc5bSRajendra Nayak 		.compatible = "qcom,sc7280-llcc-bwmon",
6841335fc5bSRajendra Nayak 		.data = &sc7280_llcc_bwmon_data
68514af4ce0SKrzysztof Kozlowski 	},
686b9c2ae6cSKrzysztof Kozlowski 	{}
687b9c2ae6cSKrzysztof Kozlowski };
688b9c2ae6cSKrzysztof Kozlowski MODULE_DEVICE_TABLE(of, bwmon_of_match);
689b9c2ae6cSKrzysztof Kozlowski 
690b9c2ae6cSKrzysztof Kozlowski static struct platform_driver bwmon_driver = {
691b9c2ae6cSKrzysztof Kozlowski 	.probe = bwmon_probe,
692b9c2ae6cSKrzysztof Kozlowski 	.remove = bwmon_remove,
693b9c2ae6cSKrzysztof Kozlowski 	.driver = {
694b9c2ae6cSKrzysztof Kozlowski 		.name = "qcom-bwmon",
695b9c2ae6cSKrzysztof Kozlowski 		.of_match_table = bwmon_of_match,
696b9c2ae6cSKrzysztof Kozlowski 	},
697b9c2ae6cSKrzysztof Kozlowski };
698b9c2ae6cSKrzysztof Kozlowski module_platform_driver(bwmon_driver);
699b9c2ae6cSKrzysztof Kozlowski 
700b9c2ae6cSKrzysztof Kozlowski MODULE_AUTHOR("Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>");
701b9c2ae6cSKrzysztof Kozlowski MODULE_DESCRIPTION("QCOM BWMON driver");
702b9c2ae6cSKrzysztof Kozlowski MODULE_LICENSE("GPL");
703