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 EDVD_CORE_VOLT_FREQ(core) (0x20 + (core) * 0x4)
16 #define EDVD_CORE_VOLT_FREQ_F_SHIFT 0
17 #define EDVD_CORE_VOLT_FREQ_V_SHIFT 16
19 struct tegra186_cpufreq_cluster_info
{
22 unsigned int bpmp_cluster_id
;
26 static const struct tegra186_cpufreq_cluster_info tegra186_clusters
[] = {
30 .cpus
= { 1, 2, NO_CPU
, NO_CPU
},
36 .cpus
= { 0, 3, 4, 5 },
41 struct tegra186_cpufreq_cluster
{
42 const struct tegra186_cpufreq_cluster_info
*info
;
43 struct cpufreq_frequency_table
*table
;
46 struct tegra186_cpufreq_data
{
50 struct tegra186_cpufreq_cluster
*clusters
;
53 static int tegra186_cpufreq_init(struct cpufreq_policy
*policy
)
55 struct tegra186_cpufreq_data
*data
= cpufreq_get_driver_data();
58 for (i
= 0; i
< data
->num_clusters
; i
++) {
59 struct tegra186_cpufreq_cluster
*cluster
= &data
->clusters
[i
];
60 const struct tegra186_cpufreq_cluster_info
*info
=
64 for (core
= 0; core
< ARRAY_SIZE(info
->cpus
); core
++) {
65 if (info
->cpus
[core
] == policy
->cpu
)
68 if (core
== ARRAY_SIZE(info
->cpus
))
72 data
->regs
+ info
->offset
+ EDVD_CORE_VOLT_FREQ(core
);
73 policy
->freq_table
= cluster
->table
;
77 policy
->cpuinfo
.transition_latency
= 300 * 1000;
82 static int tegra186_cpufreq_set_target(struct cpufreq_policy
*policy
,
85 struct cpufreq_frequency_table
*tbl
= policy
->freq_table
+ index
;
86 void __iomem
*edvd_reg
= policy
->driver_data
;
87 u32 edvd_val
= tbl
->driver_data
;
89 writel(edvd_val
, edvd_reg
);
94 static struct cpufreq_driver tegra186_cpufreq_driver
= {
96 .flags
= CPUFREQ_STICKY
| CPUFREQ_HAVE_GOVERNOR_PER_POLICY
,
97 .verify
= cpufreq_generic_frequency_table_verify
,
98 .target_index
= tegra186_cpufreq_set_target
,
99 .init
= tegra186_cpufreq_init
,
100 .attr
= cpufreq_generic_attr
,
103 static struct cpufreq_frequency_table
*init_vhint_table(
104 struct platform_device
*pdev
, struct tegra_bpmp
*bpmp
,
105 unsigned int cluster_id
)
107 struct cpufreq_frequency_table
*table
;
108 struct mrq_cpu_vhint_request req
;
109 struct tegra_bpmp_message msg
;
110 struct cpu_vhint_data
*data
;
111 int err
, i
, j
, num_rates
= 0;
115 virt
= dma_alloc_coherent(bpmp
->dev
, sizeof(*data
), &phys
,
118 return ERR_PTR(-ENOMEM
);
120 data
= (struct cpu_vhint_data
*)virt
;
122 memset(&req
, 0, sizeof(req
));
124 req
.cluster_id
= cluster_id
;
126 memset(&msg
, 0, sizeof(msg
));
127 msg
.mrq
= MRQ_CPU_VHINT
;
129 msg
.tx
.size
= sizeof(req
);
131 err
= tegra_bpmp_transfer(bpmp
, &msg
);
133 table
= ERR_PTR(err
);
137 for (i
= data
->vfloor
; i
<= data
->vceil
; i
++) {
138 u16 ndiv
= data
->ndiv
[i
];
140 if (ndiv
< data
->ndiv_min
|| ndiv
> data
->ndiv_max
)
143 /* Only store lowest voltage index for each rate */
144 if (i
> 0 && ndiv
== data
->ndiv
[i
- 1])
150 table
= devm_kcalloc(&pdev
->dev
, num_rates
+ 1, sizeof(*table
),
153 table
= ERR_PTR(-ENOMEM
);
157 for (i
= data
->vfloor
, j
= 0; i
<= data
->vceil
; i
++) {
158 struct cpufreq_frequency_table
*point
;
159 u16 ndiv
= data
->ndiv
[i
];
162 if (ndiv
< data
->ndiv_min
|| ndiv
> data
->ndiv_max
)
165 /* Only store lowest voltage index for each rate */
166 if (i
> 0 && ndiv
== data
->ndiv
[i
- 1])
169 edvd_val
|= i
<< EDVD_CORE_VOLT_FREQ_V_SHIFT
;
170 edvd_val
|= ndiv
<< EDVD_CORE_VOLT_FREQ_F_SHIFT
;
173 point
->driver_data
= edvd_val
;
174 point
->frequency
= data
->ref_clk_hz
* ndiv
/ data
->pdiv
/
178 table
[j
].frequency
= CPUFREQ_TABLE_END
;
181 dma_free_coherent(bpmp
->dev
, sizeof(*data
), virt
, phys
);
186 static int tegra186_cpufreq_probe(struct platform_device
*pdev
)
188 struct tegra186_cpufreq_data
*data
;
189 struct tegra_bpmp
*bpmp
;
190 unsigned int i
= 0, err
;
192 data
= devm_kzalloc(&pdev
->dev
, sizeof(*data
), GFP_KERNEL
);
196 data
->clusters
= devm_kcalloc(&pdev
->dev
, ARRAY_SIZE(tegra186_clusters
),
197 sizeof(*data
->clusters
), GFP_KERNEL
);
201 data
->num_clusters
= ARRAY_SIZE(tegra186_clusters
);
203 bpmp
= tegra_bpmp_get(&pdev
->dev
);
205 return PTR_ERR(bpmp
);
207 data
->regs
= devm_platform_ioremap_resource(pdev
, 0);
208 if (IS_ERR(data
->regs
)) {
209 err
= PTR_ERR(data
->regs
);
213 for (i
= 0; i
< data
->num_clusters
; i
++) {
214 struct tegra186_cpufreq_cluster
*cluster
= &data
->clusters
[i
];
216 cluster
->info
= &tegra186_clusters
[i
];
217 cluster
->table
= init_vhint_table(
218 pdev
, bpmp
, cluster
->info
->bpmp_cluster_id
);
219 if (IS_ERR(cluster
->table
)) {
220 err
= PTR_ERR(cluster
->table
);
225 tegra_bpmp_put(bpmp
);
227 tegra186_cpufreq_driver
.driver_data
= data
;
229 err
= cpufreq_register_driver(&tegra186_cpufreq_driver
);
236 tegra_bpmp_put(bpmp
);
241 static int tegra186_cpufreq_remove(struct platform_device
*pdev
)
243 cpufreq_unregister_driver(&tegra186_cpufreq_driver
);
248 static const struct of_device_id tegra186_cpufreq_of_match
[] = {
249 { .compatible
= "nvidia,tegra186-ccplex-cluster", },
252 MODULE_DEVICE_TABLE(of
, tegra186_cpufreq_of_match
);
254 static struct platform_driver tegra186_cpufreq_platform_driver
= {
256 .name
= "tegra186-cpufreq",
257 .of_match_table
= tegra186_cpufreq_of_match
,
259 .probe
= tegra186_cpufreq_probe
,
260 .remove
= tegra186_cpufreq_remove
,
262 module_platform_driver(tegra186_cpufreq_platform_driver
);
264 MODULE_AUTHOR("Mikko Perttunen <mperttunen@nvidia.com>");
265 MODULE_DESCRIPTION("NVIDIA Tegra186 cpufreq driver");
266 MODULE_LICENSE("GPL v2");