ARM: tegra: clock: Support variable iso share calculator
[linux-3.10.git] / arch / arm / mach-tegra / tegra_emc.c
1 /*
2  * arch/arm/mach-tegra/tegra_emc.c
3  *
4  * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13  * more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18  *
19  */
20
21 #include <linux/kernel.h>
22 #include <linux/err.h>
23 #include <linux/io.h>
24 #include <linux/module.h>
25 #include <linux/delay.h>
26 #include <linux/debugfs.h>
27 #include <linux/seq_file.h>
28
29 #include "tegra_emc.h"
30
31 static u8 emc_iso_share = 100;
32 static unsigned long emc_iso_allocation;
33 static unsigned long last_iso_bw;
34
35 static struct emc_iso_usage emc_usage_table[TEGRA_EMC_ISO_USE_CASES_MAX_NUM];
36
37
38 void __init tegra_emc_iso_usage_table_init(struct emc_iso_usage *table,
39                                            int size)
40 {
41         size = min(size, TEGRA_EMC_ISO_USE_CASES_MAX_NUM);
42
43         if (size && table)
44                 memcpy(emc_usage_table, table,
45                        size * sizeof(struct emc_iso_usage));
46 }
47
48 static u8 tegra_emc_get_iso_share(u32 usage_flags, unsigned long iso_bw)
49 {
50         int i;
51         u8 iso_share = 100;
52
53         if (usage_flags) {
54                 for (i = 0; i < TEGRA_EMC_ISO_USE_CASES_MAX_NUM; i++) {
55                         struct emc_iso_usage *iso_usage = &emc_usage_table[i];
56                         u32 flags = iso_usage->emc_usage_flags;
57                         u8 share = iso_usage->iso_usage_share;
58
59                         if (!flags)
60                                 continue;
61
62                         if (iso_usage->iso_share_calculator)
63                                 share = iso_usage->iso_share_calculator(iso_bw);
64                         if (!share) {
65                                 WARN(1, "%s: entry %d: iso_share 0\n",
66                                      __func__, i);
67                                 continue;
68                         }
69
70                         if ((flags & usage_flags) == flags)
71                                 iso_share = min(iso_share, share);
72                 }
73         }
74         last_iso_bw = iso_bw;
75         emc_iso_share = iso_share;
76         return iso_share;
77 }
78
79 unsigned long tegra_emc_apply_efficiency(unsigned long total_bw,
80         unsigned long iso_bw, unsigned long max_rate, u32 usage_flags,
81         unsigned long *iso_bw_min)
82 {
83         u8 efficiency = tegra_emc_get_iso_share(usage_flags, iso_bw);
84         if (iso_bw && efficiency && (efficiency < 100)) {
85                 iso_bw /= efficiency;
86                 iso_bw = (iso_bw < max_rate / 100) ?
87                                 (iso_bw * 100) : max_rate;
88         }
89         emc_iso_allocation = iso_bw;
90         if (iso_bw_min)
91                 *iso_bw_min = iso_bw;
92
93         efficiency = tegra_emc_bw_efficiency;
94         if (total_bw && efficiency && (efficiency < 100)) {
95                 total_bw = total_bw / efficiency;
96                 total_bw = (total_bw < max_rate / 100) ?
97                                 (total_bw * 100) : max_rate;
98         }
99         return max(total_bw, iso_bw);
100 }
101
102 #ifdef CONFIG_DEBUG_FS
103
104 #define USER_NAME(module) \
105 [EMC_USER_##module] = #module
106
107 static const char *emc_user_names[EMC_USER_NUM] = {
108         USER_NAME(DC),
109         USER_NAME(VI),
110         USER_NAME(MSENC),
111         USER_NAME(2D),
112         USER_NAME(3D),
113         USER_NAME(BB),
114 };
115
116 static int emc_usage_table_show(struct seq_file *s, void *data)
117 {
118         int i, j;
119
120         seq_printf(s, "EMC USAGE\t\tISO SHARE %% @ last bw %lu\n", last_iso_bw);
121
122         for (i = 0; i < TEGRA_EMC_ISO_USE_CASES_MAX_NUM; i++) {
123                 u32 flags = emc_usage_table[i].emc_usage_flags;
124                 u8 share = emc_usage_table[i].iso_usage_share;
125                 bool fixed_share = true;
126                 bool first = false;
127
128                 if (emc_usage_table[i].iso_share_calculator) {
129                         share = emc_usage_table[i].iso_share_calculator(
130                                 last_iso_bw);
131                         fixed_share = false;
132                 }
133
134                 seq_printf(s, "[%d]: ", i);
135                 if (!flags) {
136                         seq_printf(s, "reserved\n");
137                         continue;
138                 }
139
140                 for (j = 0; j < EMC_USER_NUM; j++) {
141                         u32 mask = 0x1 << j;
142                         if (!(flags & mask))
143                                 continue;
144                         seq_printf(s, "%s%s", first ? "+" : "",
145                                    emc_user_names[j]);
146                         first = true;
147                 }
148                 seq_printf(s, "\r\t\t\t= %d(%s across bw)\n",
149                            share, fixed_share ? "fixed" : "vary");
150         }
151         return 0;
152 }
153
154 static int emc_usage_table_open(struct inode *inode, struct file *file)
155 {
156         return single_open(file, emc_usage_table_show, inode->i_private);
157 }
158
159 static const struct file_operations emc_usage_table_fops = {
160         .open           = emc_usage_table_open,
161         .read           = seq_read,
162         .llseek         = seq_lseek,
163         .release        = single_release,
164 };
165
166 int __init tegra_emc_iso_usage_debugfs_init(struct dentry *emc_debugfs_root)
167 {
168         struct dentry *d;
169
170         d = debugfs_create_file("emc_usage_table", S_IRUGO, emc_debugfs_root,
171                 NULL, &emc_usage_table_fops);
172         if (!d)
173                 return -ENOMEM;
174
175         d = debugfs_create_u8("emc_iso_share", S_IRUGO, emc_debugfs_root,
176                               &emc_iso_share);
177         if (!d)
178                 return -ENOMEM;
179
180         d = debugfs_create_u32("emc_iso_allocation", S_IRUGO, emc_debugfs_root,
181                               (u32 *)&emc_iso_allocation);
182         if (!d)
183                 return -ENOMEM;
184
185         return 0;
186 }
187 #endif