diff options
author | Alex Frid <afrid@nvidia.com> | 2013-07-16 22:47:55 -0700 |
---|---|---|
committer | Harshada Kale <hkale@nvidia.com> | 2013-09-12 02:37:55 -0700 |
commit | 51ef87ecd0e7c769276c4b2ec1418e35e2ea20dd (patch) | |
tree | a75dc74d92c34b62cee5825ec5641540dc045734 | |
parent | 5585b06670bcdebf11388e1098bd4e96552ea15b (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.h | 20 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra11_dvfs.c | 1 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra3_dvfs.c | 1 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra_core_sysfs_limits.h | 51 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra_core_volt_cap.c | 99 |
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; +} |