This patch introduces a common ASV(Adaptive Supply Voltage) basic framework for samsung SoCs. It provides common APIs (to be called by users to get ASV values or init opp_table) and an interface for SoC specific drivers to register ASV members(instances). Signed-off-by: Yadwinder Singh Brar <yadi.brar@xxxxxxxxxxx> --- Hopefully asv_get_volt() can go out in future, once all users start using OPP library. --- drivers/power/Kconfig | 1 + drivers/power/Makefile | 1 + drivers/power/asv/Kconfig | 11 ++ drivers/power/asv/Makefile | 1 + drivers/power/asv/samsung-asv.c | 175 ++++++++++++++++++++++++++++++ include/linux/power/samsung-asv-driver.h | 61 +++++++++++ include/linux/power/samsung-asv.h | 37 +++++++ 7 files changed, 287 insertions(+), 0 deletions(-) create mode 100644 drivers/power/asv/Kconfig create mode 100644 drivers/power/asv/Makefile create mode 100644 drivers/power/asv/samsung-asv.c create mode 100644 include/linux/power/samsung-asv-driver.h create mode 100644 include/linux/power/samsung-asv.h diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 7b8979c..2e6b087 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -367,3 +367,4 @@ source "drivers/power/reset/Kconfig" endif # POWER_SUPPLY source "drivers/power/avs/Kconfig" +source "drivers/power/asv/Kconfig" diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 653bf6c..da93c46 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -51,6 +51,7 @@ obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o obj-$(CONFIG_POWER_AVS) += avs/ +obj-$(CONFIG_POWER_ASV) += asv/ obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o obj-$(CONFIG_POWER_RESET) += reset/ diff --git a/drivers/power/asv/Kconfig b/drivers/power/asv/Kconfig new file mode 100644 index 0000000..7cd84bd --- /dev/null +++ b/drivers/power/asv/Kconfig @@ -0,0 +1,11 @@ +menuconfig POWER_ASV + bool "Adaptive Supply Voltage support" + help + ASV is a technique used on samsung SoCs, which provides the + recommended supply voltage for some specific parts(like arm, mif etc) + of SoCs which supports dvfs. For a given operating frequency, the + voltage is recommended based on SoCs ASV group. + ASV group info is provided in the chip id info which depends on chip + manufacturing process. + + Say Y here to enable Adaptive Supply voltage support. diff --git a/drivers/power/asv/Makefile b/drivers/power/asv/Makefile new file mode 100644 index 0000000..62921da --- /dev/null +++ b/drivers/power/asv/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_POWER_ASV) += asv.o diff --git a/drivers/power/asv/asv.c b/drivers/power/asv/asv.c new file mode 100644 index 0000000..61f4a83 --- /dev/null +++ b/drivers/power/asv/asv.c @@ -0,0 +1,175 @@ +/* + * ASV(Adaptive Supply Voltage) common core + * + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include <linux/io.h> +#include <linux/opp.h> +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/power/asv-driver.h> + +static LIST_HEAD(asv_list); +static DEFINE_MUTEX(asv_mutex); + +struct asv_member { + struct list_head node; + struct asv_info *asv_info; +}; + +static void add_asv_member(struct asv_member *asv_mem) +{ + mutex_lock(&asv_mutex); + list_add_tail(&asv_mem->node, &asv_list); + mutex_unlock(&asv_mutex); +} + +static struct asv_member *asv_get_mem(enum asv_type_id asv_type) +{ + struct asv_member *asv_mem; + struct asv_info *asv_info; + + list_for_each_entry(asv_mem, &asv_list, node) { + asv_info = asv_mem->asv_info; + if (asv_type == asv_info->type) + return asv_mem; + } + + return NULL; +} + +unsigned int asv_get_volt(enum asv_type_id target_type, + unsigned int target_freq) +{ + struct asv_member *asv_mem = asv_get_mem(target_type); + struct asv_freq_table *dvfs_table; + struct asv_info *asv_info; + unsigned int i; + + if (!asv_mem) + return 0; + + asv_info = asv_mem->asv_info; + dvfs_table = asv_info->dvfs_table; + + for (i = 0; i < asv_info->nr_dvfs_level; i++) { + if (dvfs_table[i].freq == target_freq) + return dvfs_table[i].volt; + } + + return 0; +} + +int asv_init_opp_table(struct device *dev, enum asv_type_id target_type) +{ + struct asv_member *asv_mem = asv_get_mem(target_type); + struct asv_info *asv_info; + struct asv_freq_table *dvfs_table; + unsigned int i; + + if (!asv_mem) + return -EINVAL; + + asv_info = asv_mem->asv_info; + dvfs_table = asv_info->dvfs_table; + + for (i = 0; i < asv_info->nr_dvfs_level; i++) { + if (opp_add(dev, dvfs_table[i].freq * 1000, + dvfs_table[i].volt)) { + dev_warn(dev, "%s: Failed to add OPP %d\n", + __func__, dvfs_table[i].freq); + continue; + } + } + + return 0; +} + +static struct asv_member *asv_init_member(struct asv_info *asv_info) +{ + struct asv_member *asv_mem; + int ret = 0; + + if (!asv_info) { + pr_err("%s: No ASV info provided\n", __func__); + return NULL; + } + + asv_mem = kzalloc(sizeof(struct asv_member), GFP_KERNEL); + if (!asv_mem) { + pr_err("%s: Allocation failed for member: %s\n", __func__, + asv_info->name); + return NULL; + } + + asv_mem->asv_info = kmemdup(asv_info, sizeof(*asv_info), GFP_KERNEL); + if (!asv_mem->asv_info) { + pr_err("%s: Copying asv_info failed for member: %s\n", + __func__, asv_info->name); + return NULL; + } + asv_info = asv_mem->asv_info; + + if (asv_info->ops->get_asv_group) { + ret = asv_info->ops->get_asv_group(asv_info); + if (ret) { + pr_err("%s: get_asv_group failed for %s : %d\n", + __func__, asv_info->name, ret); + goto err; + } + } + + if (asv_info->ops->init_asv) + ret = asv_info->ops->init_asv(asv_info); + if (ret) { + pr_err("%s: asv_init failed for %s : %d\n", __func__, + asv_info->name, ret); + goto err; + } + + /* In case of parsing table from DT, we may need to add flag to identify + DT supporting members and call init_asv_table from asv_init_opp_table( + after getting dev_node from dev,if required), instead of calling here. + */ + + if (asv_info->ops->init_asv_table) { + ret = asv_info->ops->init_asv_table(asv_info); + if (ret) { + pr_err("%s: init_asv_table failed for %s : %d\n", + __func__, asv_info->name, ret); + goto err; + } + } + + if (!asv_info->nr_dvfs_level || !asv_info->dvfs_table) { + pr_err("%s: No dvfs_table for %s\n", __func__, asv_info->name); + goto err; + } + + pr_info("%s: Registered asv member: %s with group: %d", __func__, + asv_info->name, asv_info->asv_grp); + + return asv_mem; +err: + kfree(asv_mem->asv_info); + kfree(asv_mem); + return NULL; +} + +void register_asv_member(struct asv_info *list, unsigned int nr_member) +{ + struct asv_member *asv_mem; + int cnt; + for (cnt = 0; cnt < nr_member; cnt++) { + asv_mem = asv_init_member(&list[cnt]); + + if (asv_mem) + add_asv_member(asv_mem); + } +} diff --git a/include/linux/power/asv-driver.h b/include/linux/power/asv-driver.h new file mode 100644 index 0000000..faab388 --- /dev/null +++ b/include/linux/power/asv-driver.h @@ -0,0 +1,61 @@ +/* + * Adaptive Supply Voltage Driver Header File + * + * copyright (c) 2013 samsung electronics co., ltd. + * http://www.samsung.com/ + * + * this program is free software; you can redistribute it and/or modify + * it under the terms of the gnu general public license version 2 as + * published by the free software foundation. +*/ + +#ifndef __ASV_D_H +#define __ASV_D_H __FILE__ + +#include <linux/power/asv.h> + +struct asv_freq_table { + unsigned int freq; /* KHz */ + unsigned int volt; /* uV */ +}; + +/* struct asv_info - information of ASV member for intialisation + * + * Each member to be registered should be described using this struct + * intialised with all required information for that member. + * + * @asv_type: Type to identify particular member. + * @name: Name to used for member. + * @asv_ops: Callbacks which can be used for SoC specific operations. + * @nr_dvfs_level: Number of dvfs levels supported by member. + * @dvfs_table: Table containing supported ASV freqs and corresponding volts. + * @asv_grp: ASV group of member. + */ +struct asv_info { + const char *name; + enum asv_type_id type; + struct asv_ops *ops; + unsigned int nr_dvfs_level; + struct asv_freq_table *dvfs_table; + unsigned int asv_grp; + unsigned int flags; +}; + +/* struct asv_ops - SoC specific operation for ASV members + * @get_asv_group - Calcuates and intializes asv_grp of asv_info. + * @init_asv - SoC specific intilisation(if anything required)based on asv_grp. + * @init_asv_table - Intializes linear array(dvfs_table) for corresponding + * asv_grp. + * + * All ops should return 0 on sucess. + */ +struct asv_ops { + int (*init_asv)(struct asv_info *); + int (*get_asv_group)(struct asv_info *); + int (*init_asv_table)(struct asv_info *); +}; + +/* function for registering ASV members */ +void register_asv_member(struct asv_info *list, unsigned int nr_member); + +#endif /* __ASV_D_H */ diff --git a/include/linux/power/asv.h b/include/linux/power/asv.h new file mode 100644 index 0000000..9b187f7 --- /dev/null +++ b/include/linux/power/asv.h @@ -0,0 +1,37 @@ +/* + * Adaptive Supply Voltage Header File + * + * copyright (c) 2013 samsung electronics co., ltd. + * http://www.samsung.com/ + * + * this program is free software; you can redistribute it and/or modify + * it under the terms of the gnu general public license version 2 as + * published by the free software foundation. +*/ + +#ifndef __ASV_H +#define __ASV_H __FILE__ + +enum asv_type_id { + ASV_ARM, + ASV_INT, + ASV_MIF, + ASV_G3D, +}; + +#ifdef CONFIG_POWER_ASV +/* asv_get_volt - get the ASV for target_freq for particular target_type. + * returns 0 if target_freq is not supported + */ +extern unsigned int asv_get_volt(enum asv_type_id target_type, + unsigned int target_freq); +extern int asv_init_opp_table(struct device *dev, + enum asv_type_id target_type); +#else +static inline unsigned int asv_get_volt(enum asv_type_id target_type, + unsigned int target_freq) { return 0; } +static int asv_init_opp_table(struct device *dev, enum asv_type_id target_type) + { return 0; } + +#endif /* CONFIG_POWER_EXYNOS_AVS */ +#endif /* __ASV_H */ -- 1.7.0.4 -- To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html