summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMustafa Yigit Bilgen <mbilgen@nvidia.com>2017-08-08 20:34:12 -0400
committermobile promotions <svcmobile_promotions@nvidia.com>2017-08-17 15:18:47 -0400
commit085d4268967d17cf01b3762ccd2efc94c4ab018f (patch)
tree12d91587acb46ec16701a21be81f6c12274940f6
parenta7dc8adc8091d45dc22900affb7a4b73ccf93c01 (diff)
nvpmodel: support clocks from the device tree
* Look for a device tree node to enable the driver. * Support capping arbitrary clocks, provided from the device tree. * Reorder bwmgr registration and sysfs file creation to avoid a potential race where the store/show callback could be called before registration. Bug 1959611 Change-Id: I98fba96f0b2f5dc0f7abbb9df5e7f1f5da8907f0 Signed-off-by: Mustafa Yigit Bilgen <mbilgen@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/1539842 GVS: Gerrit_Virtual_Submit Reviewed-by: Terry Wang <terwang@nvidia.com> Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
-rw-r--r--Documentation/devicetree/bindings/nvpmodel/nvpmodel.txt30
-rw-r--r--drivers/nvpmodel/nvpmodel_emc_cap.c187
2 files changed, 189 insertions, 28 deletions
diff --git a/Documentation/devicetree/bindings/nvpmodel/nvpmodel.txt b/Documentation/devicetree/bindings/nvpmodel/nvpmodel.txt
new file mode 100644
index 000000000..5fd3af2d7
--- /dev/null
+++ b/Documentation/devicetree/bindings/nvpmodel/nvpmodel.txt
@@ -0,0 +1,30 @@
1NVIDIA nvpmodel driver bindings
2
3Nvpmodel is a driver that provides sysfs nodes that allow capping certain clock
4frequencies in order to keep the power consumption under a certain budget.
5
6These caps are designed to be relatively static. They should not be used
7during runtime (under load) to dynamically change the power budget.
8
9Required properties:
10
11- compatible: should be "nvidia,nvpmodel"
12
13Optional properties:
14
15- clocks: Clock IDs that the driver should support capping
16- clock-names: List of strings which identify the relevant clocks
17
18Both properties must have the same number of clocks. The strings that appear
19in the clock-names property will be the names of the sysfs nodes that will be
20provided. The clock IDs and names should appear in the same order.
21
22Sample:
23
24nvpmodel: {
25 compatible = "nvidia,nvpmodel";
26 clocks = <&tegra_car TEGRA186_CLK_NVENC
27 &tegra_car TEGRA186_CLK_NVDEC>;
28 clock-names = "nvenc", "nvdec";
29 status = "okay";
30};
diff --git a/drivers/nvpmodel/nvpmodel_emc_cap.c b/drivers/nvpmodel/nvpmodel_emc_cap.c
index a55562d6b..dbff63947 100644
--- a/drivers/nvpmodel/nvpmodel_emc_cap.c
+++ b/drivers/nvpmodel/nvpmodel_emc_cap.c
@@ -21,13 +21,16 @@
21#include <linux/sysfs.h> 21#include <linux/sysfs.h>
22#include <linux/init.h> 22#include <linux/init.h>
23#include <linux/fs.h> 23#include <linux/fs.h>
24#include <linux/slab.h>
24#include <linux/string.h> 25#include <linux/string.h>
26#include <linux/clk.h>
27#include <linux/of.h>
25#include <linux/platform/tegra/emc_bwmgr.h> 28#include <linux/platform/tegra/emc_bwmgr.h>
26#include <linux/platform/tegra/bwmgr_mc.h> 29#include <linux/platform/tegra/bwmgr_mc.h>
27 30
28#define AUTHOR "Terry Wang <terwang@nvidia.com>" 31#define AUTHOR "Terry Wang <terwang@nvidia.com>"
29#define DESCRIPTION "Nvpmodel EMC cap driver" 32#define DESCRIPTION "Nvpmodel clock cap driver"
30#define MODULE_NAME "Nvpmodel_EMC_cap" 33#define MODULE_NAME "Nvpmodel_clk_cap"
31#define VERSION "1.0" 34#define VERSION "1.0"
32 35
33/* Module information */ 36/* Module information */
@@ -36,12 +39,20 @@ MODULE_DESCRIPTION(DESCRIPTION);
36MODULE_VERSION(VERSION); 39MODULE_VERSION(VERSION);
37MODULE_LICENSE("GPL"); 40MODULE_LICENSE("GPL");
38 41
39static struct kobject *emc_iso_cap_kobject; 42static struct kobject *clk_cap_kobject;
40static unsigned long emc_iso_cap; 43static unsigned long emc_iso_cap;
41 44
42/* bandwidth manager handle */ 45/* bandwidth manager handle */
43struct tegra_bwmgr_client *bwmgr_handle; 46struct tegra_bwmgr_client *bwmgr_handle;
44 47
48struct nvpmodel_clk {
49 struct kobj_attribute attr;
50 struct clk *clk;
51};
52
53static struct nvpmodel_clk *clks;
54static int num_clocks;
55
45static ssize_t emc_iso_cap_show(struct kobject *kobj, 56static ssize_t emc_iso_cap_show(struct kobject *kobj,
46 struct kobj_attribute *attr, char *buf) 57 struct kobj_attribute *attr, char *buf)
47{ 58{
@@ -53,12 +64,16 @@ static ssize_t emc_iso_cap_store(struct kobject *kobj,
53 size_t count) 64 size_t count)
54{ 65{
55 int error = 0; 66 int error = 0;
56 sscanf(buf, "%lu", &emc_iso_cap); 67 if (sscanf(buf, "%lu", &emc_iso_cap) != 1)
68 return -EINVAL;
69
57 error = tegra_bwmgr_set_emc(bwmgr_handle, emc_iso_cap, 70 error = tegra_bwmgr_set_emc(bwmgr_handle, emc_iso_cap,
58 TEGRA_BWMGR_SET_EMC_ISO_CAP); 71 TEGRA_BWMGR_SET_EMC_ISO_CAP);
59 if (error) 72 if (error) {
60 pr_warn("Nvpmodel failed to set EMC hz=%lu errno=%d\n", 73 pr_warn("Nvpmodel failed to set EMC hz=%lu errno=%d\n",
61 emc_iso_cap, error); 74 emc_iso_cap, error);
75 return error;
76 }
62 77
63 return count; 78 return count;
64} 79}
@@ -66,47 +81,163 @@ static ssize_t emc_iso_cap_store(struct kobject *kobj,
66static struct kobj_attribute emc_iso_cap_attribute = 81static struct kobj_attribute emc_iso_cap_attribute =
67 __ATTR(emc_iso_cap, 0660, emc_iso_cap_show, emc_iso_cap_store); 82 __ATTR(emc_iso_cap, 0660, emc_iso_cap_show, emc_iso_cap_store);
68 83
69static int __init nvpmodel_emc_cap_init(void) 84static ssize_t clk_cap_show(struct kobject *kobj,
85 struct kobj_attribute *attr, char *buf)
86{
87 long rate;
88 struct nvpmodel_clk *clk = container_of(attr, struct nvpmodel_clk, attr);
89
90 rate = clk_round_rate(clk->clk, 1UL << 63);
91 if (rate < 0) {
92 pr_err("clk_round_rate failed: %ld\n", rate);
93 return rate;
94 }
95
96 return sprintf(buf, "%ld\n", rate);
97}
98
99static ssize_t clk_cap_store(struct kobject *kobj,
100 struct kobj_attribute *attr, const char *buf,
101 size_t count)
102{
103 unsigned long rate;
104 int ret;
105 struct nvpmodel_clk *clk = container_of(attr, struct nvpmodel_clk, attr);
106
107 if (sscanf(buf, "%lu", &rate) != 1)
108 return -EINVAL;
109
110 ret = clk_set_max_rate(clk->clk, rate);
111 if (ret) {
112 pr_err("setting cap failed: %d\n", ret);
113 return ret;
114 }
115
116 return count;
117}
118
119static void free_resources(void)
120{
121 int i;
122
123 if (clks) {
124 for (i = 0; i < num_clocks; i++) {
125 if (clks[i].attr.attr.name)
126 kfree_const(clks[i].attr.attr.name);
127 if (clks[i].clk)
128 clk_put(clks[i].clk);
129 }
130 kfree(clks);
131 clks = NULL;
132 num_clocks = 0;
133 }
134 if (bwmgr_handle) {
135 tegra_bwmgr_unregister(bwmgr_handle);
136 bwmgr_handle = NULL;
137 }
138 if (clk_cap_kobject) {
139 kobject_put(clk_cap_kobject);
140 clk_cap_kobject = NULL;
141 }
142}
143
144static int __init nvpmodel_clk_cap_init(void)
70{ 145{
71 int error = 0; 146 int error = 0;
147 int i;
148 const char *clk_name;
149 struct device_node *dn;
150
151 dn = of_find_compatible_node(NULL, NULL, "nvidia,nvpmodel");
152 if (!dn || !of_device_is_available(dn)) {
153 of_node_put(dn);
154 return -ENODEV;
155 }
72 156
73 emc_iso_cap_kobject = kobject_create_and_add("nvpmodel_emc_cap", 157 clk_cap_kobject = kobject_create_and_add("nvpmodel_emc_cap",
74 kernel_kobj); 158 kernel_kobj);
75 if (!emc_iso_cap_kobject) 159 if (!clk_cap_kobject) {
76 return -ENOMEM; 160 error = -ENOMEM;
77 error = sysfs_create_file(emc_iso_cap_kobject, 161 goto exit;
78 &emc_iso_cap_attribute.attr);
79 if (error) {
80 pr_err("failed to create emc_iso_cap sysfs: error %d\n", error);
81 kobject_put(emc_iso_cap_kobject);
82 return error;
83 } 162 }
84 163
85 bwmgr_handle = tegra_bwmgr_register(TEGRA_BWMGR_CLIENT_NVPMODEL); 164 bwmgr_handle = tegra_bwmgr_register(TEGRA_BWMGR_CLIENT_NVPMODEL);
86 if (IS_ERR_OR_NULL(bwmgr_handle)) { 165 if (IS_ERR_OR_NULL(bwmgr_handle)) {
87 error = IS_ERR(bwmgr_handle) ? 166 error = IS_ERR(bwmgr_handle) ?
88 PTR_ERR(bwmgr_handle) : -ENODEV; 167 PTR_ERR(bwmgr_handle) : -ENODEV;
89 pr_warn("Nvpmodel can't register EMC bwmgr (%d)\n", error); 168 pr_err("Nvpmodel can't register EMC bwmgr (%d)\n", error);
90 goto put_bwmgr; 169 goto exit;
91 } 170 }
92 171
93 pr_info("Module initialized successfully \n"); 172 error = sysfs_create_file(clk_cap_kobject,
94 return error; 173 &emc_iso_cap_attribute.attr);
174 if (error) {
175 pr_err("failed to create emc_iso_cap sysfs: error %d\n", error);
176 goto exit;
177 }
95 178
96put_bwmgr: 179 num_clocks = of_property_count_strings(dn, "clock-names");
97 if (!IS_ERR_OR_NULL(bwmgr_handle)) 180 if (num_clocks <= 0) {
98 tegra_bwmgr_unregister(bwmgr_handle); 181 num_clocks = 0;
99 kobject_put(emc_iso_cap_kobject); 182 goto exit;
183 }
100 184
185 clks = kzalloc(sizeof(*clks) * num_clocks, GFP_KERNEL);
186 if (!clks) {
187 num_clocks = 0;
188 pr_err("couldn't allocate clks!\n");
189 error = -ENOMEM;
190 goto exit;
191 }
192
193 for (i = 0; i < num_clocks; i++) {
194 if (of_property_read_string_index(dn, "clock-names", i,
195 &clk_name)) {
196 pr_warn("couldn't get clock %d from device tree\n", i);
197 continue;
198 }
199
200 clks[i].clk = of_clk_get(dn, i);
201 if (IS_ERR(clks[i].clk)) {
202 clks[i].clk = NULL;
203 pr_warn("couldn't get clock: %s, error %d\n", clk_name,
204 error);
205 continue;
206 }
207
208 clks[i].attr.attr.name = kstrdup_const(clk_name, GFP_KERNEL);
209 if (!clks[i].attr.attr.name) {
210 pr_warn("couldn't allocate memory for clock %s\n",
211 clk_name);
212 continue;
213 }
214
215 clks[i].attr.attr.mode = 0660;
216 clks[i].attr.show = clk_cap_show;
217 clks[i].attr.store = clk_cap_store;
218 if (sysfs_create_file(clk_cap_kobject,
219 &clks[i].attr.attr)) {
220 pr_warn("failed to create %s cap sysfs file: error %d\n",
221 clk_name, error);
222 continue;
223 }
224 }
225exit:
226 if (error) {
227 free_resources();
228 pr_err("nvpmodel: initialization failed: error %d\n", error);
229 } else {
230 pr_info("nvpmodel: initialized successfully\n");
231 }
232 of_node_put(dn);
101 return error; 233 return error;
102} 234}
103 235
104static void __exit nvpmodel_emc_cap_exit(void) 236static void __exit nvpmodel_clk_cap_exit(void)
105{ 237{
106 tegra_bwmgr_unregister(bwmgr_handle); 238 free_resources();
107 kobject_put(emc_iso_cap_kobject);
108 pr_info("Module exit successfully \n"); 239 pr_info("Module exit successfully \n");
109} 240}
110 241
111module_init(nvpmodel_emc_cap_init); 242module_init(nvpmodel_clk_cap_init);
112module_exit(nvpmodel_emc_cap_exit); 243module_exit(nvpmodel_clk_cap_exit);