@@ -367,3 +367,4 @@ source "drivers/power/reset/Kconfig"
endif # POWER_SUPPLY
source "drivers/power/avs/Kconfig"
+source "drivers/power/asv/Kconfig"
@@ -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/
new file mode 100644
@@ -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.
new file mode 100644
@@ -0,0 +1 @@
+obj-$(CONFIG_POWER_ASV) += asv.o
new file mode 100644
@@ -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);
+ }
+}
new file mode 100644
@@ -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 */
new file mode 100644
@@ -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 */
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@samsung.com> --- 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