1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved
6 #include <linux/cpufreq.h>
7 #include <linux/dma-mapping.h>
8 #include <linux/module.h>
10 #include <linux/platform_device.h>
12 #include <soc/tegra/bpmp.h>
13 #include <soc/tegra/bpmp-abi.h>
15 #define TEGRA186_NUM_CLUSTERS 2
16 #define EDVD_OFFSET_A57(core) ((SZ_64K * 6) + (0x20 + (core) * 0x4))
17 #define EDVD_OFFSET_DENVER(core) ((SZ_64K * 7) + (0x20 + (core) * 0x4))
18 #define EDVD_CORE_VOLT_FREQ_F_SHIFT 0
19 #define EDVD_CORE_VOLT_FREQ_F_MASK 0xffff
20 #define EDVD_CORE_VOLT_FREQ_V_SHIFT 16
22 struct tegra186_cpufreq_cpu
{
23 unsigned int bpmp_cluster_id
;
24 unsigned int edvd_offset
;
27 static const struct tegra186_cpufreq_cpu tegra186_cpus
[] = {
28 /* CPU0 - A57 Cluster */
31 .edvd_offset
= EDVD_OFFSET_A57(0)
33 /* CPU1 - Denver Cluster */
36 .edvd_offset
= EDVD_OFFSET_DENVER(0)
38 /* CPU2 - Denver Cluster */
41 .edvd_offset
= EDVD_OFFSET_DENVER(1)
43 /* CPU3 - A57 Cluster */
46 .edvd_offset
= EDVD_OFFSET_A57(1)
48 /* CPU4 - A57 Cluster */
51 .edvd_offset
= EDVD_OFFSET_A57(2)
53 /* CPU5 - A57 Cluster */
56 .edvd_offset
= EDVD_OFFSET_A57(3)
60 struct tegra186_cpufreq_cluster
{
61 struct cpufreq_frequency_table
*table
;
66 struct tegra186_cpufreq_data
{
68 struct tegra186_cpufreq_cluster
*clusters
;
69 const struct tegra186_cpufreq_cpu
*cpus
;
72 static int tegra186_cpufreq_init(struct cpufreq_policy
*policy
)
74 struct tegra186_cpufreq_data
*data
= cpufreq_get_driver_data();
75 unsigned int cluster
= data
->cpus
[policy
->cpu
].bpmp_cluster_id
;
77 policy
->freq_table
= data
->clusters
[cluster
].table
;
78 policy
->cpuinfo
.transition_latency
= 300 * 1000;
79 policy
->driver_data
= NULL
;
84 static int tegra186_cpufreq_set_target(struct cpufreq_policy
*policy
,
87 struct tegra186_cpufreq_data
*data
= cpufreq_get_driver_data();
88 struct cpufreq_frequency_table
*tbl
= policy
->freq_table
+ index
;
89 unsigned int edvd_offset
= data
->cpus
[policy
->cpu
].edvd_offset
;
90 u32 edvd_val
= tbl
->driver_data
;
92 writel(edvd_val
, data
->regs
+ edvd_offset
);
97 static unsigned int tegra186_cpufreq_get(unsigned int cpu
)
99 struct tegra186_cpufreq_data
*data
= cpufreq_get_driver_data();
100 struct tegra186_cpufreq_cluster
*cluster
;
101 struct cpufreq_policy
*policy
;
102 unsigned int edvd_offset
, cluster_id
;
105 policy
= cpufreq_cpu_get(cpu
);
109 edvd_offset
= data
->cpus
[policy
->cpu
].edvd_offset
;
110 ndiv
= readl(data
->regs
+ edvd_offset
) & EDVD_CORE_VOLT_FREQ_F_MASK
;
111 cluster_id
= data
->cpus
[policy
->cpu
].bpmp_cluster_id
;
112 cluster
= &data
->clusters
[cluster_id
];
113 cpufreq_cpu_put(policy
);
115 return (cluster
->ref_clk_khz
* ndiv
) / cluster
->div
;
118 static struct cpufreq_driver tegra186_cpufreq_driver
= {
120 .flags
= CPUFREQ_STICKY
| CPUFREQ_HAVE_GOVERNOR_PER_POLICY
|
121 CPUFREQ_NEED_INITIAL_FREQ_CHECK
,
122 .get
= tegra186_cpufreq_get
,
123 .verify
= cpufreq_generic_frequency_table_verify
,
124 .target_index
= tegra186_cpufreq_set_target
,
125 .init
= tegra186_cpufreq_init
,
126 .attr
= cpufreq_generic_attr
,
129 static struct cpufreq_frequency_table
*init_vhint_table(
130 struct platform_device
*pdev
, struct tegra_bpmp
*bpmp
,
131 struct tegra186_cpufreq_cluster
*cluster
, unsigned int cluster_id
)
133 struct cpufreq_frequency_table
*table
;
134 struct mrq_cpu_vhint_request req
;
135 struct tegra_bpmp_message msg
;
136 struct cpu_vhint_data
*data
;
137 int err
, i
, j
, num_rates
= 0;
141 virt
= dma_alloc_coherent(bpmp
->dev
, sizeof(*data
), &phys
,
144 return ERR_PTR(-ENOMEM
);
146 data
= (struct cpu_vhint_data
*)virt
;
148 memset(&req
, 0, sizeof(req
));
150 req
.cluster_id
= cluster_id
;
152 memset(&msg
, 0, sizeof(msg
));
153 msg
.mrq
= MRQ_CPU_VHINT
;
155 msg
.tx
.size
= sizeof(req
);
157 err
= tegra_bpmp_transfer(bpmp
, &msg
);
159 table
= ERR_PTR(err
);
163 for (i
= data
->vfloor
; i
<= data
->vceil
; i
++) {
164 u16 ndiv
= data
->ndiv
[i
];
166 if (ndiv
< data
->ndiv_min
|| ndiv
> data
->ndiv_max
)
169 /* Only store lowest voltage index for each rate */
170 if (i
> 0 && ndiv
== data
->ndiv
[i
- 1])
176 table
= devm_kcalloc(&pdev
->dev
, num_rates
+ 1, sizeof(*table
),
179 table
= ERR_PTR(-ENOMEM
);
183 cluster
->ref_clk_khz
= data
->ref_clk_hz
/ 1000;
184 cluster
->div
= data
->pdiv
* data
->mdiv
;
186 for (i
= data
->vfloor
, j
= 0; i
<= data
->vceil
; i
++) {
187 struct cpufreq_frequency_table
*point
;
188 u16 ndiv
= data
->ndiv
[i
];
191 if (ndiv
< data
->ndiv_min
|| ndiv
> data
->ndiv_max
)
194 /* Only store lowest voltage index for each rate */
195 if (i
> 0 && ndiv
== data
->ndiv
[i
- 1])
198 edvd_val
|= i
<< EDVD_CORE_VOLT_FREQ_V_SHIFT
;
199 edvd_val
|= ndiv
<< EDVD_CORE_VOLT_FREQ_F_SHIFT
;
202 point
->driver_data
= edvd_val
;
203 point
->frequency
= (cluster
->ref_clk_khz
* ndiv
) / cluster
->div
;
206 table
[j
].frequency
= CPUFREQ_TABLE_END
;
209 dma_free_coherent(bpmp
->dev
, sizeof(*data
), virt
, phys
);
214 static int tegra186_cpufreq_probe(struct platform_device
*pdev
)
216 struct tegra186_cpufreq_data
*data
;
217 struct tegra_bpmp
*bpmp
;
218 unsigned int i
= 0, err
;
220 data
= devm_kzalloc(&pdev
->dev
, sizeof(*data
), GFP_KERNEL
);
224 data
->clusters
= devm_kcalloc(&pdev
->dev
, TEGRA186_NUM_CLUSTERS
,
225 sizeof(*data
->clusters
), GFP_KERNEL
);
229 data
->cpus
= tegra186_cpus
;
231 bpmp
= tegra_bpmp_get(&pdev
->dev
);
233 return PTR_ERR(bpmp
);
235 data
->regs
= devm_platform_ioremap_resource(pdev
, 0);
236 if (IS_ERR(data
->regs
)) {
237 err
= PTR_ERR(data
->regs
);
241 for (i
= 0; i
< TEGRA186_NUM_CLUSTERS
; i
++) {
242 struct tegra186_cpufreq_cluster
*cluster
= &data
->clusters
[i
];
244 cluster
->table
= init_vhint_table(pdev
, bpmp
, cluster
, i
);
245 if (IS_ERR(cluster
->table
)) {
246 err
= PTR_ERR(cluster
->table
);
251 tegra186_cpufreq_driver
.driver_data
= data
;
253 err
= cpufreq_register_driver(&tegra186_cpufreq_driver
);
256 tegra_bpmp_put(bpmp
);
261 static int tegra186_cpufreq_remove(struct platform_device
*pdev
)
263 cpufreq_unregister_driver(&tegra186_cpufreq_driver
);
268 static const struct of_device_id tegra186_cpufreq_of_match
[] = {
269 { .compatible
= "nvidia,tegra186-ccplex-cluster", },
272 MODULE_DEVICE_TABLE(of
, tegra186_cpufreq_of_match
);
274 static struct platform_driver tegra186_cpufreq_platform_driver
= {
276 .name
= "tegra186-cpufreq",
277 .of_match_table
= tegra186_cpufreq_of_match
,
279 .probe
= tegra186_cpufreq_probe
,
280 .remove
= tegra186_cpufreq_remove
,
282 module_platform_driver(tegra186_cpufreq_platform_driver
);
284 MODULE_AUTHOR("Mikko Perttunen <mperttunen@nvidia.com>");
285 MODULE_DESCRIPTION("NVIDIA Tegra186 cpufreq driver");
286 MODULE_LICENSE("GPL v2");