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);
484*abd2e711SPortia Stephens struct private_data *priv;
485*abd2e711SPortia Stephens
486b25b64a2SAnastasia Belova if (!policy)
487b25b64a2SAnastasia Belova return 0;
488*abd2e711SPortia Stephens
489*abd2e711SPortia 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