2 * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms and conditions of the GNU General Public License,
6 * version 2, as published by the Free Software Foundation.
8 * This program is distributed in the hope it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 #include <linux/cpufreq.h>
15 #include <linux/dma-mapping.h>
16 #include <linux/module.h>
18 #include <linux/platform_device.h>
20 #include <soc/tegra/bpmp.h>
21 #include <soc/tegra/bpmp-abi.h>
23 #define EDVD_CORE_VOLT_FREQ(core) (0x20 + (core) * 0x4)
24 #define EDVD_CORE_VOLT_FREQ_F_SHIFT 0
25 #define EDVD_CORE_VOLT_FREQ_V_SHIFT 16
27 struct tegra186_cpufreq_cluster_info
{
30 unsigned int bpmp_cluster_id
;
34 static const struct tegra186_cpufreq_cluster_info tegra186_clusters
[] = {
38 .cpus
= { 1, 2, NO_CPU
, NO_CPU
},
44 .cpus
= { 0, 3, 4, 5 },
49 struct tegra186_cpufreq_cluster
{
50 const struct tegra186_cpufreq_cluster_info
*info
;
51 struct cpufreq_frequency_table
*table
;
54 struct tegra186_cpufreq_data
{
58 struct tegra186_cpufreq_cluster
*clusters
;
61 static int tegra186_cpufreq_init(struct cpufreq_policy
*policy
)
63 struct tegra186_cpufreq_data
*data
= cpufreq_get_driver_data();
66 for (i
= 0; i
< data
->num_clusters
; i
++) {
67 struct tegra186_cpufreq_cluster
*cluster
= &data
->clusters
[i
];
68 const struct tegra186_cpufreq_cluster_info
*info
=
72 for (core
= 0; core
< ARRAY_SIZE(info
->cpus
); core
++) {
73 if (info
->cpus
[core
] == policy
->cpu
)
76 if (core
== ARRAY_SIZE(info
->cpus
))
80 data
->regs
+ info
->offset
+ EDVD_CORE_VOLT_FREQ(core
);
81 policy
->freq_table
= cluster
->table
;
85 policy
->cpuinfo
.transition_latency
= 300 * 1000;
90 static int tegra186_cpufreq_set_target(struct cpufreq_policy
*policy
,
93 struct cpufreq_frequency_table
*tbl
= policy
->freq_table
+ index
;
94 void __iomem
*edvd_reg
= policy
->driver_data
;
95 u32 edvd_val
= tbl
->driver_data
;
97 writel(edvd_val
, edvd_reg
);
102 static struct cpufreq_driver tegra186_cpufreq_driver
= {
104 .flags
= CPUFREQ_STICKY
| CPUFREQ_HAVE_GOVERNOR_PER_POLICY
,
105 .verify
= cpufreq_generic_frequency_table_verify
,
106 .target_index
= tegra186_cpufreq_set_target
,
107 .init
= tegra186_cpufreq_init
,
108 .attr
= cpufreq_generic_attr
,
111 static struct cpufreq_frequency_table
*init_vhint_table(
112 struct platform_device
*pdev
, struct tegra_bpmp
*bpmp
,
113 unsigned int cluster_id
)
115 struct cpufreq_frequency_table
*table
;
116 struct mrq_cpu_vhint_request req
;
117 struct tegra_bpmp_message msg
;
118 struct cpu_vhint_data
*data
;
119 int err
, i
, j
, num_rates
= 0;
123 virt
= dma_alloc_coherent(bpmp
->dev
, sizeof(*data
), &phys
,
124 GFP_KERNEL
| GFP_DMA32
);
126 return ERR_PTR(-ENOMEM
);
128 data
= (struct cpu_vhint_data
*)virt
;
130 memset(&req
, 0, sizeof(req
));
132 req
.cluster_id
= cluster_id
;
134 memset(&msg
, 0, sizeof(msg
));
135 msg
.mrq
= MRQ_CPU_VHINT
;
137 msg
.tx
.size
= sizeof(req
);
139 err
= tegra_bpmp_transfer(bpmp
, &msg
);
141 table
= ERR_PTR(err
);
145 for (i
= data
->vfloor
; i
<= data
->vceil
; i
++) {
146 u16 ndiv
= data
->ndiv
[i
];
148 if (ndiv
< data
->ndiv_min
|| ndiv
> data
->ndiv_max
)
151 /* Only store lowest voltage index for each rate */
152 if (i
> 0 && ndiv
== data
->ndiv
[i
- 1])
158 table
= devm_kcalloc(&pdev
->dev
, num_rates
+ 1, sizeof(*table
),
161 table
= ERR_PTR(-ENOMEM
);
165 for (i
= data
->vfloor
, j
= 0; i
<= data
->vceil
; i
++) {
166 struct cpufreq_frequency_table
*point
;
167 u16 ndiv
= data
->ndiv
[i
];
170 if (ndiv
< data
->ndiv_min
|| ndiv
> data
->ndiv_max
)
173 /* Only store lowest voltage index for each rate */
174 if (i
> 0 && ndiv
== data
->ndiv
[i
- 1])
177 edvd_val
|= i
<< EDVD_CORE_VOLT_FREQ_V_SHIFT
;
178 edvd_val
|= ndiv
<< EDVD_CORE_VOLT_FREQ_F_SHIFT
;
181 point
->driver_data
= edvd_val
;
182 point
->frequency
= data
->ref_clk_hz
* ndiv
/ data
->pdiv
/
186 table
[j
].frequency
= CPUFREQ_TABLE_END
;
189 dma_free_coherent(bpmp
->dev
, sizeof(*data
), virt
, phys
);
194 static int tegra186_cpufreq_probe(struct platform_device
*pdev
)
196 struct tegra186_cpufreq_data
*data
;
197 struct tegra_bpmp
*bpmp
;
198 struct resource
*res
;
199 unsigned int i
= 0, err
;
201 data
= devm_kzalloc(&pdev
->dev
, sizeof(*data
), GFP_KERNEL
);
205 data
->clusters
= devm_kcalloc(&pdev
->dev
, ARRAY_SIZE(tegra186_clusters
),
206 sizeof(*data
->clusters
), GFP_KERNEL
);
210 data
->num_clusters
= ARRAY_SIZE(tegra186_clusters
);
212 bpmp
= tegra_bpmp_get(&pdev
->dev
);
214 return PTR_ERR(bpmp
);
216 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
217 data
->regs
= devm_ioremap_resource(&pdev
->dev
, res
);
218 if (IS_ERR(data
->regs
)) {
219 err
= PTR_ERR(data
->regs
);
223 for (i
= 0; i
< data
->num_clusters
; i
++) {
224 struct tegra186_cpufreq_cluster
*cluster
= &data
->clusters
[i
];
226 cluster
->info
= &tegra186_clusters
[i
];
227 cluster
->table
= init_vhint_table(
228 pdev
, bpmp
, cluster
->info
->bpmp_cluster_id
);
229 if (IS_ERR(cluster
->table
)) {
230 err
= PTR_ERR(cluster
->table
);
235 tegra_bpmp_put(bpmp
);
237 tegra186_cpufreq_driver
.driver_data
= data
;
239 err
= cpufreq_register_driver(&tegra186_cpufreq_driver
);
246 tegra_bpmp_put(bpmp
);
251 static int tegra186_cpufreq_remove(struct platform_device
*pdev
)
253 cpufreq_unregister_driver(&tegra186_cpufreq_driver
);
258 static const struct of_device_id tegra186_cpufreq_of_match
[] = {
259 { .compatible
= "nvidia,tegra186-ccplex-cluster", },
262 MODULE_DEVICE_TABLE(of
, tegra186_cpufreq_of_match
);
264 static struct platform_driver tegra186_cpufreq_platform_driver
= {
266 .name
= "tegra186-cpufreq",
267 .of_match_table
= tegra186_cpufreq_of_match
,
269 .probe
= tegra186_cpufreq_probe
,
270 .remove
= tegra186_cpufreq_remove
,
272 module_platform_driver(tegra186_cpufreq_platform_driver
);
274 MODULE_AUTHOR("Mikko Perttunen <mperttunen@nvidia.com>");
275 MODULE_DESCRIPTION("NVIDIA Tegra186 cpufreq driver");
276 MODULE_LICENSE("GPL v2");