summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Frid <afrid@nvidia.com>2013-07-16 22:47:55 -0700
committerHarshada Kale <hkale@nvidia.com>2013-09-12 02:37:55 -0700
commit51ef87ecd0e7c769276c4b2ec1418e35e2ea20dd (patch)
treea75dc74d92c34b62cee5825ec5641540dc045734
parent5585b06670bcdebf11388e1098bd4e96552ea15b (diff)
ARM: tegra: clock: Add sysfs interface for bus floors
Added mechanism to install sysfs objects for tegra shared bus floors. Currently no floor objects are installed. Change-Id: I20e1a1448ee799a5ec59087f3214b77a80c05408 Signed-off-by: Alex Frid <afrid@nvidia.com> Reviewed-on: http://git-master/r/250564 (cherry picked from commit caf42e72c877189dfc3b75d3d9d21fb2d2491fef) Reviewed-on: http://git-master/r/253681 Tested-by: Shaoming Feng <shaomingf@nvidia.com> GVS: Gerrit_Virtual_Submit Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
-rw-r--r--arch/arm/mach-tegra/dvfs.h20
-rw-r--r--arch/arm/mach-tegra/tegra11_dvfs.c1
-rw-r--r--arch/arm/mach-tegra/tegra3_dvfs.c1
-rw-r--r--arch/arm/mach-tegra/tegra_core_sysfs_limits.h51
-rw-r--r--arch/arm/mach-tegra/tegra_core_volt_cap.c99
5 files changed, 151 insertions, 21 deletions
diff --git a/arch/arm/mach-tegra/dvfs.h b/arch/arm/mach-tegra/dvfs.h
index 3105606f9372..129c03daf901 100644
--- a/arch/arm/mach-tegra/dvfs.h
+++ b/arch/arm/mach-tegra/dvfs.h
@@ -173,21 +173,6 @@ struct dvfs_data {
unsigned int num_voltages;
};
-struct core_dvfs_cap_table {
- const char *cap_name;
- struct clk *cap_clk;
- unsigned long freqs[MAX_DVFS_FREQS];
-};
-
-struct core_bus_cap_table {
- const char *cap_name;
- struct clk *cap_clk;
- struct kobj_attribute refcnt_attr;
- struct kobj_attribute level_attr;
- int refcnt;
- int level;
-};
-
#ifdef CONFIG_OF
typedef int (*of_tegra_dvfs_init_cb_t)(struct device_node *);
int of_tegra_dvfs_init(const struct of_device_id *matches);
@@ -241,11 +226,6 @@ static inline void tegra_dvfs_age_cpu(int cur_linear_age)
{ return; }
#endif
-int tegra_init_core_cap(struct core_dvfs_cap_table *table, int table_size,
- const int *millivolts, int millivolts_num, struct kobject *cap_kobj);
-int tegra_init_shared_bus_cap(struct core_bus_cap_table *table, int table_size,
- struct kobject *cap_kobj);
-
static inline bool tegra_dvfs_rail_is_dfll_mode(struct dvfs_rail *rail)
{
return rail ? rail->dfll_mode : false;
diff --git a/arch/arm/mach-tegra/tegra11_dvfs.c b/arch/arm/mach-tegra/tegra11_dvfs.c
index 45a9390b6ee6..2c6bac409708 100644
--- a/arch/arm/mach-tegra/tegra11_dvfs.c
+++ b/arch/arm/mach-tegra/tegra11_dvfs.c
@@ -27,6 +27,7 @@
#include "fuse.h"
#include "board.h"
#include "tegra_cl_dvfs.h"
+#include "tegra_core_sysfs_limits.h"
static bool tegra_dvfs_cpu_disabled;
static bool tegra_dvfs_core_disabled;
diff --git a/arch/arm/mach-tegra/tegra3_dvfs.c b/arch/arm/mach-tegra/tegra3_dvfs.c
index 2f2f09edf57a..457e179c5da7 100644
--- a/arch/arm/mach-tegra/tegra3_dvfs.c
+++ b/arch/arm/mach-tegra/tegra3_dvfs.c
@@ -28,6 +28,7 @@
#include "fuse.h"
#include "board.h"
#include "tegra3_emc.h"
+#include "tegra_core_sysfs_limits.h"
#define CPU_MILLIVOLTS {\
750, 762, 775, 787, 800, 825, 837, 850, 862, 875, 887, 900, 912, 916, 925, 937, 950, 962, 975, 987, 1000, 1007, 1012, 1025, 1037, 1050, 1062, 1075, 1087, 1100, 1112, 1125, 1137, 1150, 1162, 1175, 1187, 1200, 1212, 1237};
diff --git a/arch/arm/mach-tegra/tegra_core_sysfs_limits.h b/arch/arm/mach-tegra/tegra_core_sysfs_limits.h
new file mode 100644
index 000000000000..79d39531add7
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra_core_sysfs_limits.h
@@ -0,0 +1,51 @@
+/*
+ * arch/arm/mach-tegra/tegra_core_sysfs_limits.h
+ *
+ * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _MACH_TEGRA_CORE_SYSFS_LIMITS_H_
+#define _MACH_TEGRA_CORE_SYSFS_LIMITS_H_
+
+struct core_dvfs_cap_table {
+ const char *cap_name;
+ struct clk *cap_clk;
+ unsigned long freqs[MAX_DVFS_FREQS];
+};
+
+struct core_bus_cap_table {
+ const char *cap_name;
+ struct clk *cap_clk;
+ struct kobj_attribute refcnt_attr;
+ struct kobj_attribute level_attr;
+ int refcnt;
+ int level;
+};
+
+struct core_bus_floor_table {
+ const char *floor_name;
+ struct clk *floor_clk;
+ struct kobj_attribute refcnt_attr;
+ struct kobj_attribute level_attr;
+ int refcnt;
+ int level;
+};
+
+int tegra_init_core_cap(struct core_dvfs_cap_table *table, int table_size,
+ const int *millivolts, int millivolts_num, struct kobject *cap_kobj);
+int tegra_init_shared_bus_cap(struct core_bus_cap_table *table, int table_size,
+ struct kobject *cap_kobj);
+int tegra_init_shared_bus_floor(struct core_bus_floor_table *table,
+ int table_size, struct kobject *floor_kobj);
+
+#endif /* _MACH_TEGRA_CORE_SYSFS_LIMITS_H_ */
diff --git a/arch/arm/mach-tegra/tegra_core_volt_cap.c b/arch/arm/mach-tegra/tegra_core_volt_cap.c
index c842158c2b19..c7a286f9c7bf 100644
--- a/arch/arm/mach-tegra/tegra_core_volt_cap.c
+++ b/arch/arm/mach-tegra/tegra_core_volt_cap.c
@@ -1,7 +1,7 @@
/*
* arch/arm/mach-tegra/tegra_core_volt_cap.c
*
- * Copyright (C) 2013 NVIDIA Corporation.
+ * Copyright (c), NVIDIA CORPORATION. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -23,6 +23,7 @@
#include "clock.h"
#include "dvfs.h"
+#include "tegra_core_sysfs_limits.h"
/*
* sysfs and kernel interfaces to limit tegra core shared bus frequencies based
@@ -414,3 +415,99 @@ int __init tegra_init_shared_bus_cap(
return -ENOMEM;
return 0;
}
+
+static DEFINE_MUTEX(bus_floor_lock);
+const struct attribute *bus_floor_attributes[2 * MAX_BUS_NUM + 1];
+
+#define refcnt_to_bus_floor(attr) \
+ container_of(attr, struct core_bus_floor_table, refcnt_attr)
+#define level_to_bus_floor(attr) \
+ container_of(attr, struct core_bus_floor_table, level_attr)
+
+static ssize_t
+bus_floor_state_show(struct kobject *kobj, struct kobj_attribute *attr,
+ char *buf)
+{
+ struct core_bus_floor_table *bus_floor = refcnt_to_bus_floor(attr);
+ struct clk *c = bus_floor->floor_clk;
+ return sprintf(buf, "%d\n", tegra_is_clk_enabled(c) ? 1 : 0);
+}
+static ssize_t
+bus_floor_state_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ int state;
+ struct core_bus_floor_table *bus_floor = refcnt_to_bus_floor(attr);
+ struct clk *c = bus_floor->floor_clk;
+
+ if (sscanf(buf, "%d", &state) != 1)
+ return -EINVAL;
+
+ if (state) {
+ int ret = tegra_clk_prepare_enable(c);
+ if (ret)
+ return ret;
+ } else {
+ tegra_clk_disable_unprepare(c);
+ }
+ return count;
+}
+
+static ssize_t
+bus_floor_level_show(struct kobject *kobj, struct kobj_attribute *attr,
+ char *buf)
+{
+ struct core_bus_floor_table *bus_floor = level_to_bus_floor(attr);
+ return sprintf(buf, "%d\n", bus_floor->level);
+}
+static ssize_t
+bus_floor_level_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ int level, ret;
+ struct core_bus_floor_table *bus_floor = level_to_bus_floor(attr);
+ struct clk *c = bus_floor->floor_clk;
+
+ if (sscanf(buf, "%d", &level) != 1)
+ return -EINVAL;
+
+ mutex_lock(&bus_floor_lock);
+ ret = clk_set_rate(c, level);
+ if (!ret)
+ bus_floor->level = level;
+ mutex_unlock(&bus_floor_lock);
+ return ret ? : count;
+}
+
+int __init tegra_init_shared_bus_floor(
+ struct core_bus_floor_table *table, int table_size,
+ struct kobject *floor_kobj)
+{
+ int i, j;
+ struct clk *c = NULL;
+
+ if (!table || !table_size || (table_size > MAX_BUS_NUM))
+ return -EINVAL;
+
+ for (i = 0, j = 0; i < table_size; i++) {
+ c = tegra_get_clock_by_name(table[i].floor_name);
+ if (!c) {
+ pr_err("%s: failed to initialize %s table\n",
+ __func__, table[i].floor_name);
+ continue;
+ }
+ table[i].floor_clk = c;
+ table[i].level = clk_get_max_rate(c);
+ table[i].refcnt_attr.show = bus_floor_state_show;
+ table[i].refcnt_attr.store = bus_floor_state_store;
+ table[i].level_attr.show = bus_floor_level_show;
+ table[i].level_attr.store = bus_floor_level_store;
+ bus_floor_attributes[j++] = &table[i].refcnt_attr.attr;
+ bus_floor_attributes[j++] = &table[i].level_attr.attr;
+ }
+ bus_floor_attributes[j] = NULL;
+
+ if (!floor_kobj || sysfs_create_files(floor_kobj, bus_floor_attributes))
+ return -ENOMEM;
+ return 0;
+}