1de322e08SMarkus Mayer /*
2de322e08SMarkus Mayer  * CPU frequency scaling for Broadcom SoCs with AVS firmware that
3de322e08SMarkus Mayer  * supports DVS or DVFS
4de322e08SMarkus Mayer  *
5de322e08SMarkus Mayer  * Copyright (c) 2016 Broadcom
6de322e08SMarkus Mayer  *
7de322e08SMarkus Mayer  * This program is free software; you can redistribute it and/or
8de322e08SMarkus Mayer  * modify it under the terms of the GNU General Public License as
9de322e08SMarkus Mayer  * published by the Free Software Foundation version 2.
10de322e08SMarkus Mayer  *
11de322e08SMarkus Mayer  * This program is distributed "as is" WITHOUT ANY WARRANTY of any
12de322e08SMarkus Mayer  * kind, whether express or implied; without even the implied warranty
13de322e08SMarkus Mayer  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14de322e08SMarkus Mayer  * GNU General Public License for more details.
15de322e08SMarkus Mayer  */
16de322e08SMarkus Mayer 
17de322e08SMarkus Mayer /*
18de322e08SMarkus Mayer  * "AVS" is the name of a firmware developed at Broadcom. It derives
19de322e08SMarkus Mayer  * its name from the technique called "Adaptive Voltage Scaling".
20de322e08SMarkus Mayer  * Adaptive voltage scaling was the original purpose of this firmware.
21de322e08SMarkus Mayer  * The AVS firmware still supports "AVS mode", where all it does is
22de322e08SMarkus Mayer  * adaptive voltage scaling. However, on some newer Broadcom SoCs, the
23de322e08SMarkus Mayer  * AVS Firmware, despite its unchanged name, also supports DFS mode and
24de322e08SMarkus Mayer  * DVFS mode.
25de322e08SMarkus Mayer  *
26de322e08SMarkus Mayer  * In the context of this document and the related driver, "AVS" by
27de322e08SMarkus Mayer  * itself always means the Broadcom firmware and never refers to the
28de322e08SMarkus Mayer  * technique called "Adaptive Voltage Scaling".
29de322e08SMarkus Mayer  *
30de322e08SMarkus Mayer  * The Broadcom STB AVS CPUfreq driver provides voltage and frequency
31de322e08SMarkus Mayer  * scaling on Broadcom SoCs using AVS firmware with support for DFS and
32de322e08SMarkus Mayer  * DVFS. The AVS firmware is running on its own co-processor. The
33de322e08SMarkus Mayer  * driver supports both uniprocessor (UP) and symmetric multiprocessor
34de322e08SMarkus Mayer  * (SMP) systems which share clock and voltage across all CPUs.
35de322e08SMarkus Mayer  *
36de322e08SMarkus Mayer  * Actual voltage and frequency scaling is done solely by the AVS
37de322e08SMarkus Mayer  * firmware. This driver does not change frequency or voltage itself.
38de322e08SMarkus Mayer  * It provides a standard CPUfreq interface to the rest of the kernel
39de322e08SMarkus Mayer  * and to userland. It interfaces with the AVS firmware to effect the
40de322e08SMarkus Mayer  * requested changes and to report back the current system status in a
41de322e08SMarkus Mayer  * way that is expected by existing tools.
42de322e08SMarkus Mayer  */
43de322e08SMarkus Mayer 
44de322e08SMarkus Mayer #include <linux/cpufreq.h>
4508535ccdSFlorian Fainelli #include <linux/delay.h>
46de322e08SMarkus Mayer #include <linux/interrupt.h>
47de322e08SMarkus Mayer #include <linux/io.h>
48de322e08SMarkus Mayer #include <linux/module.h>
49de322e08SMarkus Mayer #include <linux/of_address.h>
50de322e08SMarkus Mayer #include <linux/platform_device.h>
51de322e08SMarkus Mayer #include <linux/semaphore.h>
52de322e08SMarkus Mayer 
53de322e08SMarkus Mayer /* Max number of arguments AVS calls take */
54de322e08SMarkus Mayer #define AVS_MAX_CMD_ARGS	4
55de322e08SMarkus Mayer /*
56de322e08SMarkus Mayer  * This macro is used to generate AVS parameter register offsets. For
57de322e08SMarkus Mayer  * x >= AVS_MAX_CMD_ARGS, it returns 0 to protect against accidental memory
58de322e08SMarkus Mayer  * access outside of the parameter range. (Offset 0 is the first parameter.)
59de322e08SMarkus Mayer  */
60de322e08SMarkus Mayer #define AVS_PARAM_MULT(x)	((x) < AVS_MAX_CMD_ARGS ? (x) : 0)
61de322e08SMarkus Mayer 
62de322e08SMarkus Mayer /* AVS Mailbox Register offsets */
63de322e08SMarkus Mayer #define AVS_MBOX_COMMAND	0x00
64de322e08SMarkus Mayer #define AVS_MBOX_STATUS		0x04
65de322e08SMarkus Mayer #define AVS_MBOX_VOLTAGE0	0x08
66de322e08SMarkus Mayer #define AVS_MBOX_TEMP0		0x0c
67de322e08SMarkus Mayer #define AVS_MBOX_PV0		0x10
68de322e08SMarkus Mayer #define AVS_MBOX_MV0		0x14
69de322e08SMarkus Mayer #define AVS_MBOX_PARAM(x)	(0x18 + AVS_PARAM_MULT(x) * sizeof(u32))
70de322e08SMarkus Mayer #define AVS_MBOX_REVISION	0x28
71de322e08SMarkus Mayer #define AVS_MBOX_PSTATE		0x2c
72de322e08SMarkus Mayer #define AVS_MBOX_HEARTBEAT	0x30
73de322e08SMarkus Mayer #define AVS_MBOX_MAGIC		0x34
74de322e08SMarkus Mayer #define AVS_MBOX_SIGMA_HVT	0x38
75de322e08SMarkus Mayer #define AVS_MBOX_SIGMA_SVT	0x3c
76de322e08SMarkus Mayer #define AVS_MBOX_VOLTAGE1	0x40
77de322e08SMarkus Mayer #define AVS_MBOX_TEMP1		0x44
78de322e08SMarkus Mayer #define AVS_MBOX_PV1		0x48
79de322e08SMarkus Mayer #define AVS_MBOX_MV1		0x4c
80de322e08SMarkus Mayer #define AVS_MBOX_FREQUENCY	0x50
81de322e08SMarkus Mayer 
82de322e08SMarkus Mayer /* AVS Commands */
83de322e08SMarkus Mayer #define AVS_CMD_AVAILABLE	0x00
84de322e08SMarkus Mayer #define AVS_CMD_DISABLE		0x10
85de322e08SMarkus Mayer #define AVS_CMD_ENABLE		0x11
86de322e08SMarkus Mayer #define AVS_CMD_S2_ENTER	0x12
87de322e08SMarkus Mayer #define AVS_CMD_S2_EXIT		0x13
88de322e08SMarkus Mayer #define AVS_CMD_BBM_ENTER	0x14
89de322e08SMarkus Mayer #define AVS_CMD_BBM_EXIT	0x15
90de322e08SMarkus Mayer #define AVS_CMD_S3_ENTER	0x16
91de322e08SMarkus Mayer #define AVS_CMD_S3_EXIT		0x17
92de322e08SMarkus Mayer #define AVS_CMD_BALANCE		0x18
93de322e08SMarkus Mayer /* PMAP and P-STATE commands */
94de322e08SMarkus Mayer #define AVS_CMD_GET_PMAP	0x30
95de322e08SMarkus Mayer #define AVS_CMD_SET_PMAP	0x31
96de322e08SMarkus Mayer #define AVS_CMD_GET_PSTATE	0x40
97de322e08SMarkus Mayer #define AVS_CMD_SET_PSTATE	0x41
98de322e08SMarkus Mayer 
99de322e08SMarkus Mayer /* Different modes AVS supports (for GET_PMAP/SET_PMAP) */
100de322e08SMarkus Mayer #define AVS_MODE_AVS		0x0
101de322e08SMarkus Mayer #define AVS_MODE_DFS		0x1
102de322e08SMarkus Mayer #define AVS_MODE_DVS		0x2
103de322e08SMarkus Mayer #define AVS_MODE_DVFS		0x3
104de322e08SMarkus Mayer 
105de322e08SMarkus Mayer /*
106de322e08SMarkus Mayer  * PMAP parameter p1
107de322e08SMarkus Mayer  * unused:31-24, mdiv_p0:23-16, unused:15-14, pdiv:13-10 , ndiv_int:9-0
108de322e08SMarkus Mayer  */
109de322e08SMarkus Mayer #define NDIV_INT_SHIFT		0
110de322e08SMarkus Mayer #define NDIV_INT_MASK		0x3ff
111de322e08SMarkus Mayer #define PDIV_SHIFT		10
112de322e08SMarkus Mayer #define PDIV_MASK		0xf
113de322e08SMarkus Mayer #define MDIV_P0_SHIFT		16
114de322e08SMarkus Mayer #define MDIV_P0_MASK		0xff
115de322e08SMarkus Mayer /*
116de322e08SMarkus Mayer  * PMAP parameter p2
117de322e08SMarkus Mayer  * mdiv_p4:31-24, mdiv_p3:23-16, mdiv_p2:15:8, mdiv_p1:7:0
118de322e08SMarkus Mayer  */
119de322e08SMarkus Mayer #define MDIV_P1_SHIFT		0
120de322e08SMarkus Mayer #define MDIV_P1_MASK		0xff
121de322e08SMarkus Mayer #define MDIV_P2_SHIFT		8
122de322e08SMarkus Mayer #define MDIV_P2_MASK		0xff
123de322e08SMarkus Mayer #define MDIV_P3_SHIFT		16
124de322e08SMarkus Mayer #define MDIV_P3_MASK		0xff
125de322e08SMarkus Mayer #define MDIV_P4_SHIFT		24
126de322e08SMarkus Mayer #define MDIV_P4_MASK		0xff
127de322e08SMarkus Mayer 
128de322e08SMarkus Mayer /* Different P-STATES AVS supports (for GET_PSTATE/SET_PSTATE) */
129de322e08SMarkus Mayer #define AVS_PSTATE_P0		0x0
130de322e08SMarkus Mayer #define AVS_PSTATE_P1		0x1
131de322e08SMarkus Mayer #define AVS_PSTATE_P2		0x2
132de322e08SMarkus Mayer #define AVS_PSTATE_P3		0x3
133de322e08SMarkus Mayer #define AVS_PSTATE_P4		0x4
134de322e08SMarkus Mayer #define AVS_PSTATE_MAX		AVS_PSTATE_P4
135de322e08SMarkus Mayer 
136de322e08SMarkus Mayer /* CPU L2 Interrupt Controller Registers */
137de322e08SMarkus Mayer #define AVS_CPU_L2_SET0		0x04
138de322e08SMarkus Mayer #define AVS_CPU_L2_INT_MASK	BIT(31)
139de322e08SMarkus Mayer 
140de322e08SMarkus Mayer /* AVS Command Status Values */
141de322e08SMarkus Mayer #define AVS_STATUS_CLEAR	0x00
142de322e08SMarkus Mayer /* Command/notification accepted */
143de322e08SMarkus Mayer #define AVS_STATUS_SUCCESS	0xf0
144de322e08SMarkus Mayer /* Command/notification rejected */
145de322e08SMarkus Mayer #define AVS_STATUS_FAILURE	0xff
146de322e08SMarkus Mayer /* Invalid command/notification (unknown) */
147de322e08SMarkus Mayer #define AVS_STATUS_INVALID	0xf1
148de322e08SMarkus Mayer /* Non-AVS modes are not supported */
149de322e08SMarkus Mayer #define AVS_STATUS_NO_SUPP	0xf2
150de322e08SMarkus Mayer /* Cannot set P-State until P-Map supplied */
151de322e08SMarkus Mayer #define AVS_STATUS_NO_MAP	0xf3
152de322e08SMarkus Mayer /* Cannot change P-Map after initial P-Map set */
153de322e08SMarkus Mayer #define AVS_STATUS_MAP_SET	0xf4
154de322e08SMarkus Mayer /* Max AVS status; higher numbers are used for debugging */
155de322e08SMarkus Mayer #define AVS_STATUS_MAX		0xff
156de322e08SMarkus Mayer 
157de322e08SMarkus Mayer /* Other AVS related constants */
158de322e08SMarkus Mayer #define AVS_LOOP_LIMIT		10000
159de322e08SMarkus Mayer #define AVS_TIMEOUT		300 /* in ms; expected completion is < 10ms */
160de322e08SMarkus Mayer #define AVS_FIRMWARE_MAGIC	0xa11600d1
161de322e08SMarkus Mayer 
162de322e08SMarkus Mayer #define BRCM_AVS_CPUFREQ_PREFIX	"brcmstb-avs"
163de322e08SMarkus Mayer #define BRCM_AVS_CPUFREQ_NAME	BRCM_AVS_CPUFREQ_PREFIX "-cpufreq"
164de322e08SMarkus Mayer #define BRCM_AVS_CPU_DATA	"brcm,avs-cpu-data-mem"
165de322e08SMarkus Mayer #define BRCM_AVS_CPU_INTR	"brcm,avs-cpu-l2-intr"
166de322e08SMarkus Mayer #define BRCM_AVS_HOST_INTR	"sw_intr"
167de322e08SMarkus Mayer 
168de322e08SMarkus Mayer struct pmap {
169de322e08SMarkus Mayer 	unsigned int mode;
170de322e08SMarkus Mayer 	unsigned int p1;
171de322e08SMarkus Mayer 	unsigned int p2;
172de322e08SMarkus Mayer 	unsigned int state;
173de322e08SMarkus Mayer };
174de322e08SMarkus Mayer 
175de322e08SMarkus Mayer struct private_data {
176de322e08SMarkus Mayer 	void __iomem *base;
177de322e08SMarkus Mayer 	void __iomem *avs_intr_base;
178de322e08SMarkus Mayer 	struct device *dev;
179de322e08SMarkus Mayer 	struct completion done;
180de322e08SMarkus Mayer 	struct semaphore sem;
181de322e08SMarkus Mayer 	struct pmap pmap;
18208535ccdSFlorian Fainelli 	int host_irq;
183de322e08SMarkus Mayer };
184de322e08SMarkus Mayer 
__map_region(const char * name)185de322e08SMarkus Mayer static void __iomem *__map_region(const char *name)
186de322e08SMarkus Mayer {
187de322e08SMarkus Mayer 	struct device_node *np;
188de322e08SMarkus Mayer 	void __iomem *ptr;
189de322e08SMarkus Mayer 
190de322e08SMarkus Mayer 	np = of_find_compatible_node(NULL, NULL, name);
191de322e08SMarkus Mayer 	if (!np)
192de322e08SMarkus Mayer 		return NULL;
193de322e08SMarkus Mayer 
194de322e08SMarkus Mayer 	ptr = of_iomap(np, 0);
195de322e08SMarkus Mayer 	of_node_put(np);
196de322e08SMarkus Mayer 
197de322e08SMarkus Mayer 	return ptr;
198de322e08SMarkus Mayer }
199de322e08SMarkus Mayer 
wait_for_avs_command(struct private_data * priv,unsigned long timeout)20008535ccdSFlorian Fainelli static unsigned long wait_for_avs_command(struct private_data *priv,
20108535ccdSFlorian Fainelli 					  unsigned long timeout)
20208535ccdSFlorian Fainelli {
20308535ccdSFlorian Fainelli 	unsigned long time_left = 0;
20408535ccdSFlorian Fainelli 	u32 val;
20508535ccdSFlorian Fainelli 
20608535ccdSFlorian Fainelli 	/* Event driven, wait for the command interrupt */
20708535ccdSFlorian Fainelli 	if (priv->host_irq >= 0)
20808535ccdSFlorian Fainelli 		return wait_for_completion_timeout(&priv->done,
20908535ccdSFlorian Fainelli 						   msecs_to_jiffies(timeout));
21008535ccdSFlorian Fainelli 
21108535ccdSFlorian Fainelli 	/* Polling for command completion */
21208535ccdSFlorian Fainelli 	do {
21308535ccdSFlorian Fainelli 		time_left = timeout;
21408535ccdSFlorian Fainelli 		val = readl(priv->base + AVS_MBOX_STATUS);
21508535ccdSFlorian Fainelli 		if (val)
21608535ccdSFlorian Fainelli 			break;
21708535ccdSFlorian Fainelli 
21808535ccdSFlorian Fainelli 		usleep_range(1000, 2000);
21908535ccdSFlorian Fainelli 	} while (--timeout);
22008535ccdSFlorian Fainelli 
22108535ccdSFlorian Fainelli 	return time_left;
22208535ccdSFlorian Fainelli }
22308535ccdSFlorian Fainelli 
__issue_avs_command(struct private_data * priv,unsigned int cmd,unsigned int num_in,unsigned int num_out,u32 args[])224b75acfb4SMarkus Mayer static int __issue_avs_command(struct private_data *priv, unsigned int cmd,
225b75acfb4SMarkus Mayer 			       unsigned int num_in, unsigned int num_out,
226de322e08SMarkus Mayer 			       u32 args[])
227de322e08SMarkus Mayer {
228de322e08SMarkus Mayer 	void __iomem *base = priv->base;
22908535ccdSFlorian Fainelli 	unsigned long time_left;
230de322e08SMarkus Mayer 	unsigned int i;
231de322e08SMarkus Mayer 	int ret;
232de322e08SMarkus Mayer 	u32 val;
233de322e08SMarkus Mayer 
234de322e08SMarkus Mayer 	ret = down_interruptible(&priv->sem);
235de322e08SMarkus Mayer 	if (ret)
236de322e08SMarkus Mayer 		return ret;
237de322e08SMarkus Mayer 
238de322e08SMarkus Mayer 	/*
239de322e08SMarkus Mayer 	 * Make sure no other command is currently running: cmd is 0 if AVS
240de322e08SMarkus Mayer 	 * co-processor is idle. Due to the guard above, we should almost never
241de322e08SMarkus Mayer 	 * have to wait here.
242de322e08SMarkus Mayer 	 */
243de322e08SMarkus Mayer 	for (i = 0, val = 1; val != 0 && i < AVS_LOOP_LIMIT; i++)
244de322e08SMarkus Mayer 		val = readl(base + AVS_MBOX_COMMAND);
245de322e08SMarkus Mayer 
246de322e08SMarkus Mayer 	/* Give the caller a chance to retry if AVS is busy. */
247de322e08SMarkus Mayer 	if (i == AVS_LOOP_LIMIT) {
248de322e08SMarkus Mayer 		ret = -EAGAIN;
249de322e08SMarkus Mayer 		goto out;
250de322e08SMarkus Mayer 	}
251de322e08SMarkus Mayer 
252de322e08SMarkus Mayer 	/* Clear status before we begin. */
253de322e08SMarkus Mayer 	writel(AVS_STATUS_CLEAR, base + AVS_MBOX_STATUS);
254de322e08SMarkus Mayer 
255b75acfb4SMarkus Mayer 	/* Provide input parameters */
256b75acfb4SMarkus Mayer 	for (i = 0; i < num_in; i++)
257de322e08SMarkus Mayer 		writel(args[i], base + AVS_MBOX_PARAM(i));
258de322e08SMarkus Mayer 
259de322e08SMarkus Mayer 	/* Protect from spurious interrupts. */
260de322e08SMarkus Mayer 	reinit_completion(&priv->done);
261de322e08SMarkus Mayer 
262de322e08SMarkus Mayer 	/* Now issue the command & tell firmware to wake up to process it. */
263de322e08SMarkus Mayer 	writel(cmd, base + AVS_MBOX_COMMAND);
264de322e08SMarkus Mayer 	writel(AVS_CPU_L2_INT_MASK, priv->avs_intr_base + AVS_CPU_L2_SET0);
265de322e08SMarkus Mayer 
266de322e08SMarkus Mayer 	/* Wait for AVS co-processor to finish processing the command. */
26708535ccdSFlorian Fainelli 	time_left = wait_for_avs_command(priv, AVS_TIMEOUT);
268de322e08SMarkus Mayer 
269de322e08SMarkus Mayer 	/*
270de322e08SMarkus Mayer 	 * If the AVS status is not in the expected range, it means AVS didn't
271de322e08SMarkus Mayer 	 * complete our command in time, and we return an error. Also, if there
272de322e08SMarkus Mayer 	 * is no "time left", we timed out waiting for the interrupt.
273de322e08SMarkus Mayer 	 */
274de322e08SMarkus Mayer 	val = readl(base + AVS_MBOX_STATUS);
275de322e08SMarkus Mayer 	if (time_left == 0 || val == 0 || val > AVS_STATUS_MAX) {
276de322e08SMarkus Mayer 		dev_err(priv->dev, "AVS command %#x didn't complete in time\n",
277de322e08SMarkus Mayer 			cmd);
278de322e08SMarkus Mayer 		dev_err(priv->dev, "    Time left: %u ms, AVS status: %#x\n",
279de322e08SMarkus Mayer 			jiffies_to_msecs(time_left), val);
280de322e08SMarkus Mayer 		ret = -ETIMEDOUT;
281de322e08SMarkus Mayer 		goto out;
282de322e08SMarkus Mayer 	}
283de322e08SMarkus Mayer 
284b75acfb4SMarkus Mayer 	/* Process returned values */
285b75acfb4SMarkus Mayer 	for (i = 0; i < num_out; i++)
286de322e08SMarkus Mayer 		args[i] = readl(base + AVS_MBOX_PARAM(i));
287de322e08SMarkus Mayer 
288de322e08SMarkus Mayer 	/* Clear status to tell AVS co-processor we are done. */
289de322e08SMarkus Mayer 	writel(AVS_STATUS_CLEAR, base + AVS_MBOX_STATUS);
290de322e08SMarkus Mayer 
291de322e08SMarkus Mayer 	/* Convert firmware errors to errno's as much as possible. */
292de322e08SMarkus Mayer 	switch (val) {
293de322e08SMarkus Mayer 	case AVS_STATUS_INVALID:
294de322e08SMarkus Mayer 		ret = -EINVAL;
295de322e08SMarkus Mayer 		break;
296de322e08SMarkus Mayer 	case AVS_STATUS_NO_SUPP:
297de322e08SMarkus Mayer 		ret = -ENOTSUPP;
298de322e08SMarkus Mayer 		break;
299de322e08SMarkus Mayer 	case AVS_STATUS_NO_MAP:
300de322e08SMarkus Mayer 		ret = -ENOENT;
301de322e08SMarkus Mayer 		break;
302de322e08SMarkus Mayer 	case AVS_STATUS_MAP_SET:
303de322e08SMarkus Mayer 		ret = -EEXIST;
304de322e08SMarkus Mayer 		break;
305de322e08SMarkus Mayer 	case AVS_STATUS_FAILURE:
306de322e08SMarkus Mayer 		ret = -EIO;
307de322e08SMarkus Mayer 		break;
308de322e08SMarkus Mayer 	}
309de322e08SMarkus Mayer 
310de322e08SMarkus Mayer out:
311de322e08SMarkus Mayer 	up(&priv->sem);
312de322e08SMarkus Mayer 
313de322e08SMarkus Mayer 	return ret;
314de322e08SMarkus Mayer }
315de322e08SMarkus Mayer 
irq_handler(int irq,void * data)316de322e08SMarkus Mayer static irqreturn_t irq_handler(int irq, void *data)
317de322e08SMarkus Mayer {
318de322e08SMarkus Mayer 	struct private_data *priv = data;
319de322e08SMarkus Mayer 
320de322e08SMarkus Mayer 	/* AVS command completed execution. Wake up __issue_avs_command(). */
321de322e08SMarkus Mayer 	complete(&priv->done);
322de322e08SMarkus Mayer 
323de322e08SMarkus Mayer 	return IRQ_HANDLED;
324de322e08SMarkus Mayer }
325de322e08SMarkus Mayer 
brcm_avs_mode_to_string(unsigned int mode)326de322e08SMarkus Mayer static char *brcm_avs_mode_to_string(unsigned int mode)
327de322e08SMarkus Mayer {
328de322e08SMarkus Mayer 	switch (mode) {
329de322e08SMarkus Mayer 	case AVS_MODE_AVS:
330de322e08SMarkus Mayer 		return "AVS";
331de322e08SMarkus Mayer 	case AVS_MODE_DFS:
332de322e08SMarkus Mayer 		return "DFS";
333de322e08SMarkus Mayer 	case AVS_MODE_DVS:
334de322e08SMarkus Mayer 		return "DVS";
335de322e08SMarkus Mayer 	case AVS_MODE_DVFS:
336de322e08SMarkus Mayer 		return "DVFS";
337de322e08SMarkus Mayer 	}
338de322e08SMarkus Mayer 	return NULL;
339de322e08SMarkus Mayer }
340de322e08SMarkus Mayer 
brcm_avs_parse_p1(u32 p1,unsigned int * mdiv_p0,unsigned int * pdiv,unsigned int * ndiv)341de322e08SMarkus Mayer static void brcm_avs_parse_p1(u32 p1, unsigned int *mdiv_p0, unsigned int *pdiv,
342de322e08SMarkus Mayer 			      unsigned int *ndiv)
343de322e08SMarkus Mayer {
344de322e08SMarkus Mayer 	*mdiv_p0 = (p1 >> MDIV_P0_SHIFT) & MDIV_P0_MASK;
345de322e08SMarkus Mayer 	*pdiv = (p1 >> PDIV_SHIFT) & PDIV_MASK;
346de322e08SMarkus Mayer 	*ndiv = (p1 >> NDIV_INT_SHIFT) & NDIV_INT_MASK;
347de322e08SMarkus Mayer }
348de322e08SMarkus Mayer 
brcm_avs_parse_p2(u32 p2,unsigned int * mdiv_p1,unsigned int * mdiv_p2,unsigned int * mdiv_p3,unsigned int * mdiv_p4)349de322e08SMarkus Mayer static void brcm_avs_parse_p2(u32 p2, unsigned int *mdiv_p1,
350de322e08SMarkus Mayer 			      unsigned int *mdiv_p2, unsigned int *mdiv_p3,
351de322e08SMarkus Mayer 			      unsigned int *mdiv_p4)
352de322e08SMarkus Mayer {
353de322e08SMarkus Mayer 	*mdiv_p4 = (p2 >> MDIV_P4_SHIFT) & MDIV_P4_MASK;
354de322e08SMarkus Mayer 	*mdiv_p3 = (p2 >> MDIV_P3_SHIFT) & MDIV_P3_MASK;
355de322e08SMarkus Mayer 	*mdiv_p2 = (p2 >> MDIV_P2_SHIFT) & MDIV_P2_MASK;
356de322e08SMarkus Mayer 	*mdiv_p1 = (p2 >> MDIV_P1_SHIFT) & MDIV_P1_MASK;
357de322e08SMarkus Mayer }
358de322e08SMarkus Mayer 
brcm_avs_get_pmap(struct private_data * priv,struct pmap * pmap)359de322e08SMarkus Mayer static int brcm_avs_get_pmap(struct private_data *priv, struct pmap *pmap)
360de322e08SMarkus Mayer {
361de322e08SMarkus Mayer 	u32 args[AVS_MAX_CMD_ARGS];
362de322e08SMarkus Mayer 	int ret;
363de322e08SMarkus Mayer 
364b75acfb4SMarkus Mayer 	ret = __issue_avs_command(priv, AVS_CMD_GET_PMAP, 0, 4, args);
365de322e08SMarkus Mayer 	if (ret || !pmap)
366de322e08SMarkus Mayer 		return ret;
367de322e08SMarkus Mayer 
368de322e08SMarkus Mayer 	pmap->mode = args[0];
369de322e08SMarkus Mayer 	pmap->p1 = args[1];
370de322e08SMarkus Mayer 	pmap->p2 = args[2];
371de322e08SMarkus Mayer 	pmap->state = args[3];
372de322e08SMarkus Mayer 
373de322e08SMarkus Mayer 	return 0;
374de322e08SMarkus Mayer }
375de322e08SMarkus Mayer 
brcm_avs_set_pmap(struct private_data * priv,struct pmap * pmap)376de322e08SMarkus Mayer static int brcm_avs_set_pmap(struct private_data *priv, struct pmap *pmap)
377de322e08SMarkus Mayer {
378de322e08SMarkus Mayer 	u32 args[AVS_MAX_CMD_ARGS];
379de322e08SMarkus Mayer 
380de322e08SMarkus Mayer 	args[0] = pmap->mode;
381de322e08SMarkus Mayer 	args[1] = pmap->p1;
382de322e08SMarkus Mayer 	args[2] = pmap->p2;
383de322e08SMarkus Mayer 	args[3] = pmap->state;
384de322e08SMarkus Mayer 
385b75acfb4SMarkus Mayer 	return __issue_avs_command(priv, AVS_CMD_SET_PMAP, 4, 0, args);
386de322e08SMarkus Mayer }
387de322e08SMarkus Mayer 
brcm_avs_get_pstate(struct private_data * priv,unsigned int * pstate)388de322e08SMarkus Mayer static int brcm_avs_get_pstate(struct private_data *priv, unsigned int *pstate)
389de322e08SMarkus Mayer {
390de322e08SMarkus Mayer 	u32 args[AVS_MAX_CMD_ARGS];
391de322e08SMarkus Mayer 	int ret;
392de322e08SMarkus Mayer 
393b75acfb4SMarkus Mayer 	ret = __issue_avs_command(priv, AVS_CMD_GET_PSTATE, 0, 1, args);
394de322e08SMarkus Mayer 	if (ret)
395de322e08SMarkus Mayer 		return ret;
396de322e08SMarkus Mayer 	*pstate = args[0];
397de322e08SMarkus Mayer 
398de322e08SMarkus Mayer 	return 0;
399de322e08SMarkus Mayer }
400de322e08SMarkus Mayer 
brcm_avs_set_pstate(struct private_data * priv,unsigned int pstate)401de322e08SMarkus Mayer static int brcm_avs_set_pstate(struct private_data *priv, unsigned int pstate)
402de322e08SMarkus Mayer {
403de322e08SMarkus Mayer 	u32 args[AVS_MAX_CMD_ARGS];
404de322e08SMarkus Mayer 
405de322e08SMarkus Mayer 	args[0] = pstate;
406de322e08SMarkus Mayer 
407b75acfb4SMarkus Mayer 	return __issue_avs_command(priv, AVS_CMD_SET_PSTATE, 1, 0, args);
408b75acfb4SMarkus Mayer 
409de322e08SMarkus Mayer }
410de322e08SMarkus Mayer 
brcm_avs_get_voltage(void __iomem * base)4114c5681fcSFlorian Fainelli static u32 brcm_avs_get_voltage(void __iomem *base)
412de322e08SMarkus Mayer {
413de322e08SMarkus Mayer 	return readl(base + AVS_MBOX_VOLTAGE1);
414de322e08SMarkus Mayer }
415de322e08SMarkus Mayer 
brcm_avs_get_frequency(void __iomem * base)4164c5681fcSFlorian Fainelli static u32 brcm_avs_get_frequency(void __iomem *base)
417de322e08SMarkus Mayer {
418de322e08SMarkus Mayer 	return readl(base + AVS_MBOX_FREQUENCY) * 1000;	/* in kHz */
419de322e08SMarkus Mayer }
420de322e08SMarkus Mayer 
421de322e08SMarkus Mayer /*
422de322e08SMarkus Mayer  * We determine which frequencies are supported by cycling through all P-states
423de322e08SMarkus Mayer  * and reading back what frequency we are running at for each P-state.
424de322e08SMarkus Mayer  */
425de322e08SMarkus Mayer static struct cpufreq_frequency_table *
brcm_avs_get_freq_table(struct device * dev,struct private_data * priv)426de322e08SMarkus Mayer brcm_avs_get_freq_table(struct device *dev, struct private_data *priv)
427de322e08SMarkus Mayer {
428de322e08SMarkus Mayer 	struct cpufreq_frequency_table *table;
429de322e08SMarkus Mayer 	unsigned int pstate;
430de322e08SMarkus Mayer 	int i, ret;
431de322e08SMarkus Mayer 
432de322e08SMarkus Mayer 	/* Remember P-state for later */
433de322e08SMarkus Mayer 	ret = brcm_avs_get_pstate(priv, &pstate);
434de322e08SMarkus Mayer 	if (ret)
435de322e08SMarkus Mayer 		return ERR_PTR(ret);
436de322e08SMarkus Mayer 
437e520d0b6SGustavo A. R. Silva 	/*
438e520d0b6SGustavo A. R. Silva 	 * We allocate space for the 5 different P-STATES AVS,
439e520d0b6SGustavo A. R. Silva 	 * plus extra space for a terminating element.
440e520d0b6SGustavo A. R. Silva 	 */
441e520d0b6SGustavo A. R. Silva 	table = devm_kcalloc(dev, AVS_PSTATE_MAX + 1 + 1, sizeof(*table),
442de322e08SMarkus Mayer 			     GFP_KERNEL);
443de322e08SMarkus Mayer 	if (!table)
444de322e08SMarkus Mayer 		return ERR_PTR(-ENOMEM);
445de322e08SMarkus Mayer 
446de322e08SMarkus Mayer 	for (i = AVS_PSTATE_P0; i <= AVS_PSTATE_MAX; i++) {
447de322e08SMarkus Mayer 		ret = brcm_avs_set_pstate(priv, i);
448de322e08SMarkus Mayer 		if (ret)
449de322e08SMarkus Mayer 			return ERR_PTR(ret);
450de322e08SMarkus Mayer 		table[i].frequency = brcm_avs_get_frequency(priv->base);
451de322e08SMarkus Mayer 		table[i].driver_data = i;
452de322e08SMarkus Mayer 	}
453de322e08SMarkus Mayer 	table[i].frequency = CPUFREQ_TABLE_END;
454de322e08SMarkus Mayer 
455de322e08SMarkus Mayer 	/* Restore P-state */
456de322e08SMarkus Mayer 	ret = brcm_avs_set_pstate(priv, pstate);
457de322e08SMarkus Mayer 	if (ret)
458de322e08SMarkus Mayer 		return ERR_PTR(ret);
459de322e08SMarkus Mayer 
460de322e08SMarkus Mayer 	return table;
461de322e08SMarkus Mayer }
462de322e08SMarkus Mayer 
463de322e08SMarkus Mayer /*
464de322e08SMarkus Mayer  * To ensure the right firmware is running we need to
465de322e08SMarkus Mayer  *    - check the MAGIC matches what we expect
466de322e08SMarkus Mayer  *    - brcm_avs_get_pmap() doesn't return -ENOTSUPP or -EINVAL
467de322e08SMarkus Mayer  * We need to set up our interrupt handling before calling brcm_avs_get_pmap()!
468de322e08SMarkus Mayer  */
brcm_avs_is_firmware_loaded(struct private_data * priv)469de322e08SMarkus Mayer static bool brcm_avs_is_firmware_loaded(struct private_data *priv)
470de322e08SMarkus Mayer {
471de322e08SMarkus Mayer 	u32 magic;
472de322e08SMarkus Mayer 	int rc;
473de322e08SMarkus Mayer 
474de322e08SMarkus Mayer 	rc = brcm_avs_get_pmap(priv, NULL);
475de322e08SMarkus Mayer 	magic = readl(priv->base + AVS_MBOX_MAGIC);
476de322e08SMarkus Mayer 
47722a26cc6SFlorian Fainelli 	return (magic == AVS_FIRMWARE_MAGIC) && ((rc != -ENOTSUPP) ||
47822a26cc6SFlorian Fainelli 		(rc != -EINVAL));
479de322e08SMarkus Mayer }
480de322e08SMarkus Mayer 
brcm_avs_cpufreq_get(unsigned int cpu)481de322e08SMarkus Mayer static unsigned int brcm_avs_cpufreq_get(unsigned int cpu)
482de322e08SMarkus Mayer {
483de322e08SMarkus Mayer 	struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
484abd2e711SPortia Stephens 	struct private_data *priv;
485abd2e711SPortia Stephens 
486b25b64a2SAnastasia Belova 	if (!policy)
487b25b64a2SAnastasia Belova 		return 0;
488abd2e711SPortia Stephens 
489abd2e711SPortia Stephens 	priv = policy->driver_data;
490de322e08SMarkus Mayer 
491a48ac1c9Schenqiwu 	cpufreq_cpu_put(policy);
492a48ac1c9Schenqiwu 
493de322e08SMarkus Mayer 	return brcm_avs_get_frequency(priv->base);
494de322e08SMarkus Mayer }
495de322e08SMarkus Mayer 
brcm_avs_target_index(struct cpufreq_policy * policy,unsigned int index)496de322e08SMarkus Mayer static int brcm_avs_target_index(struct cpufreq_policy *policy,
497de322e08SMarkus Mayer 				 unsigned int index)
498de322e08SMarkus Mayer {
499de322e08SMarkus Mayer 	return brcm_avs_set_pstate(policy->driver_data,
500de322e08SMarkus Mayer 				  policy->freq_table[index].driver_data);
501de322e08SMarkus Mayer }
502de322e08SMarkus Mayer 
brcm_avs_suspend(struct cpufreq_policy * policy)503de322e08SMarkus Mayer static int brcm_avs_suspend(struct cpufreq_policy *policy)
504de322e08SMarkus Mayer {
505de322e08SMarkus Mayer 	struct private_data *priv = policy->driver_data;
5063c223c19SMarkus Mayer 	int ret;
507de322e08SMarkus Mayer 
5083c223c19SMarkus Mayer 	ret = brcm_avs_get_pmap(priv, &priv->pmap);
5093c223c19SMarkus Mayer 	if (ret)
5103c223c19SMarkus Mayer 		return ret;
5113c223c19SMarkus Mayer 
5123c223c19SMarkus Mayer 	/*
5133c223c19SMarkus Mayer 	 * We can't use the P-state returned by brcm_avs_get_pmap(), since
5143c223c19SMarkus Mayer 	 * that's the initial P-state from when the P-map was downloaded to the
5153c223c19SMarkus Mayer 	 * AVS co-processor, not necessarily the P-state we are running at now.
5163c223c19SMarkus Mayer 	 * So, we get the current P-state explicitly.
5173c223c19SMarkus Mayer 	 */
518d48461b2SMarkus Mayer 	ret = brcm_avs_get_pstate(priv, &priv->pmap.state);
519d48461b2SMarkus Mayer 	if (ret)
520d48461b2SMarkus Mayer 		return ret;
521d48461b2SMarkus Mayer 
522d48461b2SMarkus Mayer 	/* This is best effort. Nothing to do if it fails. */
523d48461b2SMarkus Mayer 	(void)__issue_avs_command(priv, AVS_CMD_S2_ENTER, 0, 0, NULL);
524d48461b2SMarkus Mayer 
525d48461b2SMarkus Mayer 	return 0;
526de322e08SMarkus Mayer }
527de322e08SMarkus Mayer 
brcm_avs_resume(struct cpufreq_policy * policy)528de322e08SMarkus Mayer static int brcm_avs_resume(struct cpufreq_policy *policy)
529de322e08SMarkus Mayer {
530de322e08SMarkus Mayer 	struct private_data *priv = policy->driver_data;
531de322e08SMarkus Mayer 	int ret;
532de322e08SMarkus Mayer 
533d48461b2SMarkus Mayer 	/* This is best effort. Nothing to do if it fails. */
534d48461b2SMarkus Mayer 	(void)__issue_avs_command(priv, AVS_CMD_S2_EXIT, 0, 0, NULL);
535d48461b2SMarkus Mayer 
536de322e08SMarkus Mayer 	ret = brcm_avs_set_pmap(priv, &priv->pmap);
537de322e08SMarkus Mayer 	if (ret == -EEXIST) {
538de322e08SMarkus Mayer 		struct platform_device *pdev  = cpufreq_get_driver_data();
539de322e08SMarkus Mayer 		struct device *dev = &pdev->dev;
540de322e08SMarkus Mayer 
541de322e08SMarkus Mayer 		dev_warn(dev, "PMAP was already set\n");
542de322e08SMarkus Mayer 		ret = 0;
543de322e08SMarkus Mayer 	}
544de322e08SMarkus Mayer 
545de322e08SMarkus Mayer 	return ret;
546de322e08SMarkus Mayer }
547de322e08SMarkus Mayer 
548de322e08SMarkus Mayer /*
549de322e08SMarkus Mayer  * All initialization code that we only want to execute once goes here. Setup
550de322e08SMarkus Mayer  * code that can be re-tried on every core (if it failed before) can go into
551de322e08SMarkus Mayer  * brcm_avs_cpufreq_init().
552de322e08SMarkus Mayer  */
brcm_avs_prepare_init(struct platform_device * pdev)553de322e08SMarkus Mayer static int brcm_avs_prepare_init(struct platform_device *pdev)
554de322e08SMarkus Mayer {
555de322e08SMarkus Mayer 	struct private_data *priv;
556de322e08SMarkus Mayer 	struct device *dev;
55708535ccdSFlorian Fainelli 	int ret;
558de322e08SMarkus Mayer 
559de322e08SMarkus Mayer 	dev = &pdev->dev;
560de322e08SMarkus Mayer 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
561de322e08SMarkus Mayer 	if (!priv)
562de322e08SMarkus Mayer 		return -ENOMEM;
563de322e08SMarkus Mayer 
564de322e08SMarkus Mayer 	priv->dev = dev;
565de322e08SMarkus Mayer 	sema_init(&priv->sem, 1);
566de322e08SMarkus Mayer 	init_completion(&priv->done);
567de322e08SMarkus Mayer 	platform_set_drvdata(pdev, priv);
568de322e08SMarkus Mayer 
569de322e08SMarkus Mayer 	priv->base = __map_region(BRCM_AVS_CPU_DATA);
570de322e08SMarkus Mayer 	if (!priv->base) {
571de322e08SMarkus Mayer 		dev_err(dev, "Couldn't find property %s in device tree.\n",
572de322e08SMarkus Mayer 			BRCM_AVS_CPU_DATA);
573de322e08SMarkus Mayer 		return -ENOENT;
574de322e08SMarkus Mayer 	}
575de322e08SMarkus Mayer 
576de322e08SMarkus Mayer 	priv->avs_intr_base = __map_region(BRCM_AVS_CPU_INTR);
577de322e08SMarkus Mayer 	if (!priv->avs_intr_base) {
578de322e08SMarkus Mayer 		dev_err(dev, "Couldn't find property %s in device tree.\n",
579de322e08SMarkus Mayer 			BRCM_AVS_CPU_INTR);
580de322e08SMarkus Mayer 		ret = -ENOENT;
581de322e08SMarkus Mayer 		goto unmap_base;
582de322e08SMarkus Mayer 	}
583de322e08SMarkus Mayer 
58408535ccdSFlorian Fainelli 	priv->host_irq = platform_get_irq_byname(pdev, BRCM_AVS_HOST_INTR);
585de322e08SMarkus Mayer 
58608535ccdSFlorian Fainelli 	ret = devm_request_irq(dev, priv->host_irq, irq_handler,
58708535ccdSFlorian Fainelli 			       IRQF_TRIGGER_RISING,
588de322e08SMarkus Mayer 			       BRCM_AVS_HOST_INTR, priv);
58908535ccdSFlorian Fainelli 	if (ret && priv->host_irq >= 0) {
590de322e08SMarkus Mayer 		dev_err(dev, "IRQ request failed: %s (%d) -- %d\n",
59108535ccdSFlorian Fainelli 			BRCM_AVS_HOST_INTR, priv->host_irq, ret);
592de322e08SMarkus Mayer 		goto unmap_intr_base;
593de322e08SMarkus Mayer 	}
594de322e08SMarkus Mayer 
595de322e08SMarkus Mayer 	if (brcm_avs_is_firmware_loaded(priv))
596de322e08SMarkus Mayer 		return 0;
597de322e08SMarkus Mayer 
598de322e08SMarkus Mayer 	dev_err(dev, "AVS firmware is not loaded or doesn't support DVFS\n");
599de322e08SMarkus Mayer 	ret = -ENODEV;
600de322e08SMarkus Mayer 
601de322e08SMarkus Mayer unmap_intr_base:
602de322e08SMarkus Mayer 	iounmap(priv->avs_intr_base);
603de322e08SMarkus Mayer unmap_base:
604de322e08SMarkus Mayer 	iounmap(priv->base);
605de322e08SMarkus Mayer 
606de322e08SMarkus Mayer 	return ret;
607de322e08SMarkus Mayer }
608de322e08SMarkus Mayer 
brcm_avs_prepare_uninit(struct platform_device * pdev)60905f45628SChristophe JAILLET static void brcm_avs_prepare_uninit(struct platform_device *pdev)
61005f45628SChristophe JAILLET {
61105f45628SChristophe JAILLET 	struct private_data *priv;
61205f45628SChristophe JAILLET 
61305f45628SChristophe JAILLET 	priv = platform_get_drvdata(pdev);
61405f45628SChristophe JAILLET 
61505f45628SChristophe JAILLET 	iounmap(priv->avs_intr_base);
61605f45628SChristophe JAILLET 	iounmap(priv->base);
61705f45628SChristophe JAILLET }
61805f45628SChristophe JAILLET 
brcm_avs_cpufreq_init(struct cpufreq_policy * policy)619de322e08SMarkus Mayer static int brcm_avs_cpufreq_init(struct cpufreq_policy *policy)
620de322e08SMarkus Mayer {
621de322e08SMarkus Mayer 	struct cpufreq_frequency_table *freq_table;
622de322e08SMarkus Mayer 	struct platform_device *pdev;
623de322e08SMarkus Mayer 	struct private_data *priv;
624de322e08SMarkus Mayer 	struct device *dev;
625de322e08SMarkus Mayer 	int ret;
626de322e08SMarkus Mayer 
627de322e08SMarkus Mayer 	pdev = cpufreq_get_driver_data();
628de322e08SMarkus Mayer 	priv = platform_get_drvdata(pdev);
629de322e08SMarkus Mayer 	policy->driver_data = priv;
630de322e08SMarkus Mayer 	dev = &pdev->dev;
631de322e08SMarkus Mayer 
632de322e08SMarkus Mayer 	freq_table = brcm_avs_get_freq_table(dev, priv);
633de322e08SMarkus Mayer 	if (IS_ERR(freq_table)) {
634de322e08SMarkus Mayer 		ret = PTR_ERR(freq_table);
635de322e08SMarkus Mayer 		dev_err(dev, "Couldn't determine frequency table (%d).\n", ret);
636de322e08SMarkus Mayer 		return ret;
637de322e08SMarkus Mayer 	}
638de322e08SMarkus Mayer 
6395d8d4f92SViresh Kumar 	policy->freq_table = freq_table;
640de322e08SMarkus Mayer 
641de322e08SMarkus Mayer 	/* All cores share the same clock and thus the same policy. */
642de322e08SMarkus Mayer 	cpumask_setall(policy->cpus);
643de322e08SMarkus Mayer 
644b75acfb4SMarkus Mayer 	ret = __issue_avs_command(priv, AVS_CMD_ENABLE, 0, 0, NULL);
645de322e08SMarkus Mayer 	if (!ret) {
646de322e08SMarkus Mayer 		unsigned int pstate;
647de322e08SMarkus Mayer 
648de322e08SMarkus Mayer 		ret = brcm_avs_get_pstate(priv, &pstate);
649de322e08SMarkus Mayer 		if (!ret) {
650de322e08SMarkus Mayer 			policy->cur = freq_table[pstate].frequency;
651de322e08SMarkus Mayer 			dev_info(dev, "registered\n");
652de322e08SMarkus Mayer 			return 0;
653de322e08SMarkus Mayer 		}
654de322e08SMarkus Mayer 	}
655de322e08SMarkus Mayer 
656de322e08SMarkus Mayer 	dev_err(dev, "couldn't initialize driver (%d)\n", ret);
657de322e08SMarkus Mayer 
658de322e08SMarkus Mayer 	return ret;
659de322e08SMarkus Mayer }
660de322e08SMarkus Mayer 
show_brcm_avs_pstate(struct cpufreq_policy * policy,char * buf)661de322e08SMarkus Mayer static ssize_t show_brcm_avs_pstate(struct cpufreq_policy *policy, char *buf)
662de322e08SMarkus Mayer {
663de322e08SMarkus Mayer 	struct private_data *priv = policy->driver_data;
664de322e08SMarkus Mayer 	unsigned int pstate;
665de322e08SMarkus Mayer 
666de322e08SMarkus Mayer 	if (brcm_avs_get_pstate(priv, &pstate))
667de322e08SMarkus Mayer 		return sprintf(buf, "<unknown>\n");
668de322e08SMarkus Mayer 
669de322e08SMarkus Mayer 	return sprintf(buf, "%u\n", pstate);
670de322e08SMarkus Mayer }
671de322e08SMarkus Mayer 
show_brcm_avs_mode(struct cpufreq_policy * policy,char * buf)672de322e08SMarkus Mayer static ssize_t show_brcm_avs_mode(struct cpufreq_policy *policy, char *buf)
673de322e08SMarkus Mayer {
674de322e08SMarkus Mayer 	struct private_data *priv = policy->driver_data;
675de322e08SMarkus Mayer 	struct pmap pmap;
676de322e08SMarkus Mayer 
677de322e08SMarkus Mayer 	if (brcm_avs_get_pmap(priv, &pmap))
678de322e08SMarkus Mayer 		return sprintf(buf, "<unknown>\n");
679de322e08SMarkus Mayer 
680de322e08SMarkus Mayer 	return sprintf(buf, "%s %u\n", brcm_avs_mode_to_string(pmap.mode),
681de322e08SMarkus Mayer 		pmap.mode);
682de322e08SMarkus Mayer }
683de322e08SMarkus Mayer 
show_brcm_avs_pmap(struct cpufreq_policy * policy,char * buf)684de322e08SMarkus Mayer static ssize_t show_brcm_avs_pmap(struct cpufreq_policy *policy, char *buf)
685de322e08SMarkus Mayer {
686de322e08SMarkus Mayer 	unsigned int mdiv_p0, mdiv_p1, mdiv_p2, mdiv_p3, mdiv_p4;
687de322e08SMarkus Mayer 	struct private_data *priv = policy->driver_data;
688de322e08SMarkus Mayer 	unsigned int ndiv, pdiv;
689de322e08SMarkus Mayer 	struct pmap pmap;
690de322e08SMarkus Mayer 
691de322e08SMarkus Mayer 	if (brcm_avs_get_pmap(priv, &pmap))
692de322e08SMarkus Mayer 		return sprintf(buf, "<unknown>\n");
693de322e08SMarkus Mayer 
694de322e08SMarkus Mayer 	brcm_avs_parse_p1(pmap.p1, &mdiv_p0, &pdiv, &ndiv);
695de322e08SMarkus Mayer 	brcm_avs_parse_p2(pmap.p2, &mdiv_p1, &mdiv_p2, &mdiv_p3, &mdiv_p4);
696de322e08SMarkus Mayer 
6979b02c54bSMarkus Mayer 	return sprintf(buf, "0x%08x 0x%08x %u %u %u %u %u %u %u %u %u\n",
698de322e08SMarkus Mayer 		pmap.p1, pmap.p2, ndiv, pdiv, mdiv_p0, mdiv_p1, mdiv_p2,
6999b02c54bSMarkus Mayer 		mdiv_p3, mdiv_p4, pmap.mode, pmap.state);
700de322e08SMarkus Mayer }
701de322e08SMarkus Mayer 
show_brcm_avs_voltage(struct cpufreq_policy * policy,char * buf)702de322e08SMarkus Mayer static ssize_t show_brcm_avs_voltage(struct cpufreq_policy *policy, char *buf)
703de322e08SMarkus Mayer {
704de322e08SMarkus Mayer 	struct private_data *priv = policy->driver_data;
705de322e08SMarkus Mayer 
7064c5681fcSFlorian Fainelli 	return sprintf(buf, "0x%08x\n", brcm_avs_get_voltage(priv->base));
707de322e08SMarkus Mayer }
708de322e08SMarkus Mayer 
show_brcm_avs_frequency(struct cpufreq_policy * policy,char * buf)709de322e08SMarkus Mayer static ssize_t show_brcm_avs_frequency(struct cpufreq_policy *policy, char *buf)
710de322e08SMarkus Mayer {
711de322e08SMarkus Mayer 	struct private_data *priv = policy->driver_data;
712de322e08SMarkus Mayer 
7134c5681fcSFlorian Fainelli 	return sprintf(buf, "0x%08x\n", brcm_avs_get_frequency(priv->base));
714de322e08SMarkus Mayer }
715de322e08SMarkus Mayer 
716de322e08SMarkus Mayer cpufreq_freq_attr_ro(brcm_avs_pstate);
717de322e08SMarkus Mayer cpufreq_freq_attr_ro(brcm_avs_mode);
718de322e08SMarkus Mayer cpufreq_freq_attr_ro(brcm_avs_pmap);
719de322e08SMarkus Mayer cpufreq_freq_attr_ro(brcm_avs_voltage);
720de322e08SMarkus Mayer cpufreq_freq_attr_ro(brcm_avs_frequency);
721de322e08SMarkus Mayer 
722e7d040b8SWei Yongjun static struct freq_attr *brcm_avs_cpufreq_attr[] = {
723de322e08SMarkus Mayer 	&cpufreq_freq_attr_scaling_available_freqs,
724de322e08SMarkus Mayer 	&brcm_avs_pstate,
725de322e08SMarkus Mayer 	&brcm_avs_mode,
726de322e08SMarkus Mayer 	&brcm_avs_pmap,
727de322e08SMarkus Mayer 	&brcm_avs_voltage,
728de322e08SMarkus Mayer 	&brcm_avs_frequency,
729de322e08SMarkus Mayer 	NULL
730de322e08SMarkus Mayer };
731de322e08SMarkus Mayer 
732de322e08SMarkus Mayer static struct cpufreq_driver brcm_avs_driver = {
733de322e08SMarkus Mayer 	.flags		= CPUFREQ_NEED_INITIAL_FREQ_CHECK,
734de322e08SMarkus Mayer 	.verify		= cpufreq_generic_frequency_table_verify,
735de322e08SMarkus Mayer 	.target_index	= brcm_avs_target_index,
736de322e08SMarkus Mayer 	.get		= brcm_avs_cpufreq_get,
737de322e08SMarkus Mayer 	.suspend	= brcm_avs_suspend,
738de322e08SMarkus Mayer 	.resume		= brcm_avs_resume,
739de322e08SMarkus Mayer 	.init		= brcm_avs_cpufreq_init,
740de322e08SMarkus Mayer 	.attr		= brcm_avs_cpufreq_attr,
741de322e08SMarkus Mayer 	.name		= BRCM_AVS_CPUFREQ_PREFIX,
742de322e08SMarkus Mayer };
743de322e08SMarkus Mayer 
brcm_avs_cpufreq_probe(struct platform_device * pdev)744de322e08SMarkus Mayer static int brcm_avs_cpufreq_probe(struct platform_device *pdev)
745de322e08SMarkus Mayer {
746de322e08SMarkus Mayer 	int ret;
747de322e08SMarkus Mayer 
748de322e08SMarkus Mayer 	ret = brcm_avs_prepare_init(pdev);
749de322e08SMarkus Mayer 	if (ret)
750de322e08SMarkus Mayer 		return ret;
751de322e08SMarkus Mayer 
752de322e08SMarkus Mayer 	brcm_avs_driver.driver_data = pdev;
753de322e08SMarkus Mayer 
75405f45628SChristophe JAILLET 	ret = cpufreq_register_driver(&brcm_avs_driver);
75505f45628SChristophe JAILLET 	if (ret)
75605f45628SChristophe JAILLET 		brcm_avs_prepare_uninit(pdev);
75705f45628SChristophe JAILLET 
75805f45628SChristophe JAILLET 	return ret;
759de322e08SMarkus Mayer }
760de322e08SMarkus Mayer 
brcm_avs_cpufreq_remove(struct platform_device * pdev)761f1154d65SYangtao Li static void brcm_avs_cpufreq_remove(struct platform_device *pdev)
762de322e08SMarkus Mayer {
763dd329e1eSUwe Kleine-König 	cpufreq_unregister_driver(&brcm_avs_driver);
764de322e08SMarkus Mayer 
76505f45628SChristophe JAILLET 	brcm_avs_prepare_uninit(pdev);
766de322e08SMarkus Mayer }
767de322e08SMarkus Mayer 
768de322e08SMarkus Mayer static const struct of_device_id brcm_avs_cpufreq_match[] = {
769de322e08SMarkus Mayer 	{ .compatible = BRCM_AVS_CPU_DATA },
770de322e08SMarkus Mayer 	{ }
771de322e08SMarkus Mayer };
772de322e08SMarkus Mayer MODULE_DEVICE_TABLE(of, brcm_avs_cpufreq_match);
773de322e08SMarkus Mayer 
774de322e08SMarkus Mayer static struct platform_driver brcm_avs_cpufreq_platdrv = {
775de322e08SMarkus Mayer 	.driver = {
776de322e08SMarkus Mayer 		.name	= BRCM_AVS_CPUFREQ_NAME,
777de322e08SMarkus Mayer 		.of_match_table = brcm_avs_cpufreq_match,
778de322e08SMarkus Mayer 	},
779de322e08SMarkus Mayer 	.probe		= brcm_avs_cpufreq_probe,
780f1154d65SYangtao Li 	.remove_new	= brcm_avs_cpufreq_remove,
781de322e08SMarkus Mayer };
782de322e08SMarkus Mayer module_platform_driver(brcm_avs_cpufreq_platdrv);
783de322e08SMarkus Mayer 
784de322e08SMarkus Mayer MODULE_AUTHOR("Markus Mayer <mmayer@broadcom.com>");
785de322e08SMarkus Mayer MODULE_DESCRIPTION("CPUfreq driver for Broadcom STB AVS");
786de322e08SMarkus Mayer MODULE_LICENSE("GPL");
787