From: Yadwinder Singh Brar <yadi.brar@xxxxxxxxxxx> 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> Signed-off-by: Sachin Kamat <sachin.kamat@xxxxxxxxxx> --- drivers/power/Kconfig | 1 + drivers/power/Makefile | 1 + drivers/power/asv/Kconfig | 10 +++ drivers/power/asv/Makefile | 1 + drivers/power/asv/asv.c | 176 ++++++++++++++++++++++++++++++++++++++ include/linux/power/asv-driver.h | 62 ++++++++++++++ include/linux/power/asv.h | 37 ++++++++ 7 files changed, 288 insertions(+) create mode 100644 drivers/power/asv/Kconfig create mode 100644 drivers/power/asv/Makefile create mode 100644 drivers/power/asv/asv.c create mode 100644 include/linux/power/asv-driver.h create mode 100644 include/linux/power/asv.h diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 5e2054afe840..09da1fd730cd 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -385,3 +385,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 372b4e8ab598..788e36d37d24 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -54,6 +54,7 @@ obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o obj-$(CONFIG_CHARGER_BQ24190) += bq24190_charger.o obj-$(CONFIG_CHARGER_BQ24735) += bq24735-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 000000000000..761119d9f7f8 --- /dev/null +++ b/drivers/power/asv/Kconfig @@ -0,0 +1,10 @@ +menuconfig POWER_ASV + bool "Adaptive Supply Voltage (ASV) support" + help + ASV is a technique used on Samsung SoCs which provides the + recommended supply voltage for some specific parts(like CPU, MIF, etc) + that support 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 the 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 000000000000..366cb04f557b --- /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 000000000000..3f2c31a0d3a9 --- /dev/null +++ b/drivers/power/asv/asv.c @@ -0,0 +1,176 @@ +/* + * 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/pm_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 (dev_pm_opp_add(dev, dvfs_table[i].freq * 1000, + dvfs_table[i].volt)) { + dev_warn(dev, "Failed to add OPP %d\n", + 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("No ASV info provided\n"); + return NULL; + } + + asv_mem = kzalloc(sizeof(*asv_mem), GFP_KERNEL); + if (!asv_mem) { + pr_err("Allocation failed for member: %s\n", asv_info->name); + return NULL; + } + + asv_mem->asv_info = kmemdup(asv_info, sizeof(*asv_info), GFP_KERNEL); + if (!asv_mem->asv_info) { + pr_err("Copying asv_info failed for member: %s\n", + asv_info->name); + kfree(asv_mem); + 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("get_asv_group failed for %s : %d\n", + asv_info->name, ret); + goto err; + } + } + + if (asv_info->ops->init_asv) + ret = asv_info->ops->init_asv(asv_info); + if (ret) { + pr_err("asv_init failed for %s : %d\n", + 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("init_asv_table failed for %s : %d\n", + asv_info->name, ret); + goto err; + } + } + + if (!asv_info->nr_dvfs_level || !asv_info->dvfs_table) { + pr_err("No dvfs_table for %s\n", asv_info->name); + goto err; + } + + pr_info("Registered asv member: %s with group: %d", + 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 000000000000..afe072cbd451 --- /dev/null +++ b/include/linux/power/asv-driver.h @@ -0,0 +1,62 @@ +/* + * 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. + * + * @name: Name to use for member. + * @asv_type_id: Type to identify particular 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. + * @flags: ASV flags + */ +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 - Calculates and initializes asv_grp of asv_info. + * @init_asv - SoC specific initialisation (if required) based on asv_grp. + * @init_asv_table - Initializes 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 000000000000..bfc4e4fa8719 --- /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.9.5 -- 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