soc: samsung: Add Exynos Adaptive Supply Voltage (ASV) driver
authorSylwester Nawrocki <s.nawrocki@samsung.com>
Fri, 7 Dec 2018 09:33:06 +0000 (10:33 +0100)
committerJunghoon Kim <jhoon20.kim@samsung.com>
Thu, 14 Feb 2019 05:57:49 +0000 (14:57 +0900)
The purpose of exynos-asv driver added in this patch is to modify
CPU OPPs as defined in DT depending on exact SoC revision specified
by data available in the CHIP ID block.

This patch is based on hardkernel code:
https://github.com/hardkernel/linux/blob/odroidxu4-4.14.y/arch/arm/mach-exynos/exynos5422-asv.c
https://github.com/hardkernel/linux/blob/odroidxu4-4.14.y/arch/arm/mach-exynos/exynos5422-asv.h

Change-Id: I115c693684ed5e87d69d82f662a1a828ad1c998a
Signed-off by: Sylwester Nawrocki <s.nawrocki@samsung.com>

drivers/soc/samsung/Kconfig
drivers/soc/samsung/Makefile
drivers/soc/samsung/exynos-asv.c [new file with mode: 0644]

index a90a4ad..e6dc8e1 100644 (file)
@@ -6,6 +6,11 @@ menuconfig SOC_SAMSUNG
 
 if SOC_SAMSUNG
 
+config EXYNOS_ASV
+       bool "Exynos Adaptive Supply Voltage support" if COMPILE_TEST
+       depends on ARCH_EXYNOS || COMPILE_TEST
+       depends on EXYNOS_CHIPID
+
 config EXYNOS_CHIPID
        bool "Exynos Chipid controller driver" if COMPILE_TEST
        depends on ARCH_EXYNOS || COMPILE_TEST
index be3b6bb..b46eaa9 100644 (file)
@@ -1,6 +1,8 @@
 obj-$(CONFIG_EXYNOS_CHIPID)    += exynos-chipid.o
 obj-$(CONFIG_EXYNOS_PMU)       += exynos-pmu.o
 
+obj-$(CONFIG_EXYNOS_ASV)       += exynos-asv.o
+
 obj-$(CONFIG_EXYNOS_PMU_ARM_DRIVERS)   += exynos3250-pmu.o exynos4-pmu.o \
                                        exynos5250-pmu.o exynos5420-pmu.o
 obj-$(CONFIG_EXYNOS_PM_DOMAINS) += pm_domains.o
diff --git a/drivers/soc/samsung/exynos-asv.c b/drivers/soc/samsung/exynos-asv.c
new file mode 100644 (file)
index 0000000..7117f92
--- /dev/null
@@ -0,0 +1,493 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *           http://www.samsung.com/
+ *
+ * Samsung Exynos SoC Adaptive Supply Voltage support
+ */
+
+#include <linux/bitrev.h>
+#include <linux/cpu.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/irqchip.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_fdt.h>
+#include <linux/platform_device.h>
+#include <linux/pm_opp.h>
+
+#include "exynos-chipid.h"
+
+#define        EXYNOS5422_ASV_NR               14
+
+#define ARM_DVFS_NR                    20
+#define ARM_BIN2_DVFS_NR               17
+
+#define KFC_DVFS_NR                    14
+#define KFC_BIN2_DVFS_NR               12
+
+ /* HPM, IDS values to select target group */
+struct asv_limit_entry {
+       unsigned int hpm;
+       unsigned int ids;
+};
+
+struct asv_cluster {
+       unsigned int base_volt;
+       unsigned int dvfs_nr;
+       unsigned int offset_volt_h;
+       unsigned int offset_volt_l;
+       const unsigned int (*asv_table)[EXYNOS5422_ASV_NR + 1];
+};
+
+enum {
+       CLUSTER_ID_ARM,
+       CLUSTER_ID_KFC,
+       CLUSTER_ID_MAX
+};
+
+struct exynos_asv {
+       struct asv_cluster cluster[CLUSTER_ID_MAX];
+       unsigned int bin2;
+       unsigned int special_lot;
+       unsigned int group;
+       unsigned int table;
+};
+
+static struct exynos_asv *exynos_asv = NULL;
+
+static const unsigned int arm_info_table01[ARM_DVFS_NR][EXYNOS5422_ASV_NR + 1] = {
+       { 2100000, 1362500, 1362500, 1350000, 1337500, 1325000, 1312500, 1300000, 1275000, 1262500, 1250000, 1237500, 1225000, 1212500, 1200000 },
+       { 2000000, 1312500, 1312500, 1300000, 1287500, 1275000, 1262500, 1250000, 1237500, 1225000, 1237500, 1225000, 1212500, 1200000, 1187500 },
+       { 1900000, 1250000, 1237500, 1225000, 1212500, 1200000, 1187500, 1175000, 1162500, 1150000, 1162500, 1150000, 1137500, 1125000, 1112500 },
+       { 1800000, 1200000, 1187500, 1175000, 1162500, 1150000, 1137500, 1125000, 1112500, 1100000, 1112500, 1100000, 1087500, 1075000, 1062500 },
+       { 1700000, 1162500, 1150000, 1137500, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500, 1075000, 1062500, 1050000, 1037500, 1025000 },
+       { 1600000, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, 1037500, 1025000, 1012500, 1000000,  987500 },
+       { 1500000, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000,  987500, 1000000,  987500,  975000,  962500,  950000 },
+       { 1400000, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000,  987500,  975000,  962500,  975000,  962500,  950000,  937500,  925000 },
+       { 1300000, 1050000, 1037500, 1025000, 1012500, 1000000,  987500,  975000,  962500,  950000,  962500,  950000,  937500,  925000,  912500 },
+       { 1200000, 1025000, 1012500, 1000000,  987500,  975000,  962500,  950000,  937500,  925000,  937500,  925000,  912500,  900000,  900000 },
+       { 1100000, 1000000,  987500,  975000,  962500,  950000,  937500,  925000,  912500,  900000,  900000,  900000,  900000,  900000,  900000 },
+       { 1000000,  975000,  962500,  950000,  937500,  925000,  912500,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  900000,  950000,  937500,  925000,  912500,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  800000,  925000,  912500,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  700000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  600000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  500000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  400000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  300000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  200000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+};
+
+static const unsigned int arm_info_table2[ARM_DVFS_NR][EXYNOS5422_ASV_NR + 1] = {
+       { 2100000, 1362500, 1362500, 1350000, 1337500, 1325000, 1312500, 1300000, 1275000, 1262500, 1250000, 1237500, 1225000, 1212500, 1200000 },
+       { 2000000, 1312500, 1312500, 1312500, 1300000, 1275000, 1262500, 1250000, 1237500, 1225000, 1237500, 1225000, 1212500, 1200000, 1187500 },
+       { 1900000, 1262500, 1250000, 1250000, 1237500, 1212500, 1200000, 1187500, 1175000, 1162500, 1175000, 1162500, 1150000, 1137500, 1125000 },
+       { 1800000, 1212500, 1200000, 1187500, 1175000, 1162500, 1150000, 1137500, 1125000, 1112500, 1125000, 1112500, 1100000, 1087500, 1075000 },
+       { 1700000, 1175000, 1162500, 1150000, 1137500, 1125000, 1112500, 1100000, 1087500, 1075000, 1087500, 1075000, 1062500, 1050000, 1037500 },
+       { 1600000, 1137500, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500, 1050000, 1037500, 1050000, 1037500, 1025000, 1012500, 1000000 },
+       { 1500000, 1100000, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000, 1012500, 1000000,  987500,  975000,  962500 },
+       { 1400000, 1075000, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000,  987500,  975000,  987500,  975000,  962500,  950000,  937500 },
+       { 1300000, 1050000, 1037500, 1025000, 1012500, 1000000,  987500,  975000,  962500,  950000,  962500,  950000,  937500,  925000,  912500 },
+       { 1200000, 1025000, 1012500, 1000000,  987500,  975000,  962500,  950000,  937500,  925000,  937500,  925000,  912500,  900000,  900000 },
+       { 1100000, 1000000,  987500,  975000,  962500,  950000,  937500,  925000,  912500,  900000,  900000,  900000,  900000,  900000,  900000 },
+       { 1000000,  975000,  962500,  950000,  937500,  925000,  912500,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  900000,  950000,  937500,  925000,  912500,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  800000,  925000,  912500,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  700000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  600000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  500000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  400000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  300000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  200000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+};
+
+static const unsigned int arm_info_table3[ARM_DVFS_NR][EXYNOS5422_ASV_NR + 1] = {
+       { 2100000, 1362500, 1362500, 1350000, 1337500, 1325000, 1312500, 1300000, 1275000, 1262500, 1250000, 1237500, 1225000, 1212500, 1200000 },
+       { 2000000, 1312500, 1312500, 1300000, 1287500, 1275000, 1262500, 1250000, 1237500, 1225000, 1237500, 1225000, 1212500, 1200000, 1187500 },
+       { 1900000, 1262500, 1250000, 1237500, 1225000, 1212500, 1200000, 1187500, 1175000, 1162500, 1175000, 1162500, 1150000, 1137500, 1125000 },
+       { 1800000, 1212500, 1200000, 1187500, 1175000, 1162500, 1150000, 1137500, 1125000, 1112500, 1125000, 1112500, 1100000, 1087500, 1075000 },
+       { 1700000, 1175000, 1162500, 1150000, 1137500, 1125000, 1112500, 1100000, 1087500, 1075000, 1087500, 1075000, 1062500, 1050000, 1037500 },
+       { 1600000, 1137500, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500, 1050000, 1037500, 1050000, 1037500, 1025000, 1012500, 1000000 },
+       { 1500000, 1100000, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000, 1012500, 1000000,  987500,  975000,  962500 },
+       { 1400000, 1075000, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000,  987500,  975000,  987500,  975000,  962500,  950000,  937500 },
+       { 1300000, 1050000, 1037500, 1025000, 1012500, 1000000,  987500,  975000,  962500,  950000,  962500,  950000,  937500,  925000,  912500 },
+       { 1200000, 1025000, 1012500, 1000000,  987500,  975000,  962500,  950000,  937500,  925000,  937500,  925000,  912500,  900000,  900000 },
+       { 1100000, 1000000,  987500,  975000,  962500,  950000,  937500,  925000,  912500,  900000,  900000,  900000,  900000,  900000,  900000 },
+       { 1000000,  975000,  962500,  950000,  937500,  925000,  912500,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  900000,  950000,  937500,  925000,  912500,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  800000,  925000,  912500,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  700000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  600000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  500000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  400000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  300000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  200000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+};
+
+static const unsigned int arm_info_bin2[ARM_BIN2_DVFS_NR][EXYNOS5422_ASV_NR + 1] = {
+       { 1800000, 1237500, 1225000, 1212500, 1200000, 1187500, 1175000, 1162500, 1150000, 1137500, 1150000, 1137500, 1125000, 1112500, 1100000 },
+       { 1700000, 1200000, 1187500, 1175000, 1162500, 1150000, 1137500, 1125000, 1112500, 1100000, 1112500, 1100000, 1087500, 1075000, 1062500 },
+       { 1600000, 1162500, 1150000, 1137500, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500, 1075000, 1062500, 1050000, 1037500, 1025000 },
+       { 1500000, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, 1037500, 1025000, 1012500, 1000000,  987500 },
+       { 1400000, 1100000, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000, 1012500, 1000000,  987500,  975000,  962500 },
+       { 1300000, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000,  987500, 1000000,  987500,  975000,  962500,  950000 },
+       { 1200000, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000,  987500,  975000,  962500,  975000,  962500,  950000,  937500,  925000 },
+       { 1100000, 1037500, 1025000, 1012500, 1000000,  987500,  975000,  962500,  950000,  937500,  950000,  937500,  925000,  912500,  900000 },
+       { 1000000, 1012500, 1000000,  987500,  975000,  962500,  950000,  937500,  925000,  912500,  925000,  912500,  900000,  900000,  900000 },
+       {  900000,  987500,  975000,  962500,  950000,  937500,  925000,  912500,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  800000,  962500,  950000,  937500,  925000,  912500,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  700000,  937500,  925000,  912500,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  600000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  500000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  400000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  300000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  200000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+};
+
+static const unsigned int kfc_info_table01[KFC_DVFS_NR][EXYNOS5422_ASV_NR + 1] = {
+       { 1500000, 1300000, 1300000, 1300000, 1287500, 1287500, 1287500, 1275000, 1262500, 1250000, 1237500, 1225000, 1212500, 1200000, 1187500 },
+       { 1400000, 1275000, 1262500, 1250000, 1237500, 1225000, 1212500, 1200000, 1187500, 1175000, 1162500, 1150000, 1137500, 1125000, 1112500 },
+       { 1300000, 1225000, 1212500, 1200000, 1187500, 1175000, 1162500, 1150000, 1137500, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500 },
+       { 1200000, 1175000, 1162500, 1150000, 1137500, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, 1012500 },
+       { 1100000, 1137500, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000,  987500,  975000 },
+       { 1000000, 1100000, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000,  987500,  975000,  962500,  950000,  937500 },
+       {  900000, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000,  987500,  975000,  962500,  950000,  937500,  925000,  912500,  900000 },
+       {  800000, 1025000, 1012500, 1000000,  987500,  975000,  962500,  950000,  937500,  925000,  912500,  900000,  900000,  900000,  900000 },
+       {  700000,  987500,  975000,  962500,  950000,  937500,  925000,  912500,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  600000,  950000,  937500,  925000,  912500,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  500000,  912500,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  400000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  300000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  200000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+};
+
+static const unsigned int kfc_info_table2[KFC_DVFS_NR][EXYNOS5422_ASV_NR + 1] = {
+       { 1500000, 1300000, 1300000, 1300000, 1287500, 1287500, 1287500, 1275000, 1262500, 1250000, 1237500, 1225000, 1212500, 1200000, 1187500 },
+       { 1400000, 1275000, 1262500, 1250000, 1237500, 1225000, 1212500, 1200000, 1187500, 1175000, 1162500, 1150000, 1137500, 1125000, 1112500 },
+       { 1300000, 1225000, 1212500, 1200000, 1187500, 1175000, 1162500, 1150000, 1137500, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500 },
+       { 1200000, 1175000, 1162500, 1150000, 1137500, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, 1012500 },
+       { 1100000, 1137500, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000,  987500,  975000 },
+       { 1000000, 1100000, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000,  987500,  975000,  962500,  950000,  937500 },
+       {  900000, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000,  987500,  975000,  962500,  950000,  937500,  925000,  912500,  900000 },
+       {  800000, 1025000, 1012500, 1000000,  987500,  975000,  962500,  950000,  937500,  925000,  912500,  900000,  900000,  900000,  900000 },
+       {  700000,  987500,  975000,  962500,  950000,  937500,  925000,  912500,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  600000,  950000,  937500,  925000,  912500,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  500000,  912500,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  400000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  300000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  200000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+};
+
+static const unsigned int kfc_info_table3[KFC_DVFS_NR][EXYNOS5422_ASV_NR + 1] = {
+       { 1500000, 1300000, 1300000, 1300000, 1287500, 1287500, 1287500, 1275000, 1262500, 1250000, 1237500, 1225000, 1212500, 1200000, 1187500 },
+       { 1400000, 1275000, 1262500, 1250000, 1237500, 1225000, 1212500, 1200000, 1187500, 1175000, 1162500, 1150000, 1137500, 1125000, 1112500 },
+       { 1300000, 1225000, 1212500, 1200000, 1187500, 1175000, 1162500, 1150000, 1137500, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500 },
+       { 1200000, 1175000, 1162500, 1150000, 1137500, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, 1012500 },
+       { 1100000, 1137500, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000,  987500,  975000 },
+       { 1000000, 1100000, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000,  987500,  975000,  962500,  950000,  937500 },
+       {  900000, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000,  987500,  975000,  962500,  950000,  937500,  925000,  912500,  900000 },
+       {  800000, 1025000, 1012500, 1000000,  987500,  975000,  962500,  950000,  937500,  925000,  912500,  900000,  900000,  900000,  900000 },
+       {  700000,  987500,  975000,  962500,  950000,  937500,  925000,  912500,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  600000,  950000,  937500,  925000,  912500,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  500000,  912500,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  400000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  300000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  200000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+};
+
+static const unsigned int kfc_info_bin2[KFC_BIN2_DVFS_NR][EXYNOS5422_ASV_NR + 1] = {
+       { 1300000, 1250000, 1237500, 1225000, 1212500, 1200000, 1187500, 1175000, 1162500, 1150000, 1137500, 1125000, 1112500, 1100000, 1087500 },
+       { 1200000, 1200000, 1187500, 1175000, 1162500, 1150000, 1137500, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500, 1050000, 1037500 },
+       { 1100000, 1162500, 1150000, 1137500, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000 },
+       { 1000000, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000,  987500,  975000,  962500 },
+       {  900000, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000,  987500,  975000,  962500,  950000,  937500,  925000 },
+       {  800000, 1050000, 1037500, 1025000, 1012500, 1000000,  987500,  975000,  962500,  950000,  937500,  925000,  912500,  900000,  900000 },
+       {  700000, 1012500, 1000000,  987500,  975000,  962500,  950000,  937500,  925000,  912500,  900000,  900000,  900000,  900000,  900000 },
+       {  600000,  975000,  962500,  950000,  937500,  925000,  912500,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  500000,  937500,  925000,  912500,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  400000,  925000,  912500,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  300000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+       {  200000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000,  900000 },
+
+};
+
+
+static unsigned int exynos_asv_opp_get_voltage(struct asv_cluster *cluster,
+                                  unsigned int freq, unsigned int volt)
+{
+       unsigned int i, asv_volt;
+
+       for (i = 0; i < cluster->dvfs_nr; i++) {
+               if (freq == cluster->asv_table[i][0])
+                       break;
+       }
+
+       if (i == cluster->dvfs_nr)
+               return  0;
+
+       asv_volt = cluster->asv_table[i][exynos_asv->group + 1];
+
+       if (volt > cluster->base_volt)
+               asv_volt += cluster->offset_volt_h;
+       else
+               asv_volt += cluster->offset_volt_l;
+
+       return asv_volt;
+}
+
+static int exynos_asv_get_bin2(void)
+{
+       unsigned int reg = exynos_chipid_read(EXYNOS_CHIPID_REG_PKG_ID);
+
+       return (reg >> EXYNOS5422_BIN2_OFFSET) & EXYNOS5422_BIN2_MASK;
+}
+
+static bool asv_special_group_check(void)
+{
+       unsigned int reg = exynos_chipid_read(EXYNOS_CHIPID_REG_PKG_ID);
+
+       return (((reg >> EXYNOS5422_USESG_OFFSET) & EXYNOS5422_USESG_MASK)
+               && exynos_asv->bin2 == 0);
+}
+
+static const struct asv_limit_entry exynos5422_asv_limits[EXYNOS5422_ASV_NR] = {
+       { 13, 55 },
+       { 21, 65 },
+       { 25, 69 },
+       { 30, 72 },
+       { 36, 74 },
+       { 43, 76 },
+       { 51, 78 },
+       { 65, 80 },
+       { 81, 82 },
+       { 98, 84 },
+       { 119, 87 },
+       { 135, 89 },
+       { 150, 92 },
+       { 999, 999 },
+};
+
+static int exynos_asv_get_group(struct exynos_asv *exynos_asv)
+{
+       unsigned int pkgid_reg = exynos_chipid_read(EXYNOS_CHIPID_REG_PKG_ID);
+       unsigned int auxi_reg = exynos_chipid_read(EXYNOS_CHIPID_AUX_INFO);
+       int hpm, ids, i;
+
+       if (exynos_asv->special_lot) {
+               u32 sga = (pkgid_reg >> EXYNOS5422_SG_A_OFFSET) &
+                          EXYNOS5422_SG_A_MASK;
+
+               u32 sgb = (pkgid_reg >> EXYNOS5422_SG_B_OFFSET) &
+                          EXYNOS5422_SG_B_MASK;
+
+               if ((pkgid_reg >> EXYNOS5422_SG_BSIGN_OFFSET) &
+                    EXYNOS5422_SG_BSIGN_MASK)
+                       return sga + sgb;
+               else
+                       return sga - sgb;
+       }
+
+       hpm = (auxi_reg >> EXYNOS5422_TMCB_OFFSET) & EXYNOS5422_TMCB_MASK;
+       ids = (pkgid_reg >> EXYNOS5422_IDS_OFFSET) & EXYNOS5422_IDS_MASK;
+
+       for (i = 0; i < EXYNOS5422_ASV_NR; i++) {
+               if (ids <= exynos5422_asv_limits[i].ids)
+                       break;
+               if (hpm <= exynos5422_asv_limits[i].hpm)
+                       break;
+       }
+
+       if (i < EXYNOS5422_ASV_NR)
+               return i;
+       else
+               return 0;
+}
+
+static int asv_table_check(void)
+{
+       unsigned int pkg_id_reg = exynos_chipid_read(EXYNOS_CHIPID_REG_PKG_ID);
+
+       return  (pkg_id_reg >> EXYNOS5422_TABLE_OFFSET) & EXYNOS5422_TABLE_MASK;
+}
+
+static int exynos5422_offset_voltage(unsigned int index)
+{
+       static const unsigned int offset_table[] = { 12500, 50000, 25000 };
+
+       if (index == 0 || index > 3)
+               return 0;
+
+       return offset_table[index - 1];
+}
+
+static void asv_offset_voltage_setup(void)
+{
+       struct asv_cluster *cluster;
+       unsigned int reg, value;
+
+       if (exynos_asv->bin2) {
+               exynos_asv->cluster[CLUSTER_ID_ARM].dvfs_nr = ARM_BIN2_DVFS_NR;
+               exynos_asv->cluster[CLUSTER_ID_KFC].dvfs_nr = KFC_BIN2_DVFS_NR;
+       } else {
+               exynos_asv->cluster[CLUSTER_ID_ARM].dvfs_nr = ARM_DVFS_NR;
+               exynos_asv->cluster[CLUSTER_ID_KFC].dvfs_nr = KFC_DVFS_NR;
+       }
+
+       reg = exynos_chipid_read(EXYNOS_CHIPID_AUX_INFO);
+
+       /* ARM offset voltage setup */
+       cluster = &exynos_asv->cluster[CLUSTER_ID_ARM];
+
+       cluster->base_volt = 1000000;
+
+       value = (reg >> EXYNOS5422_ARM_UP_OFFSET) & EXYNOS5422_ARM_UP_MASK;
+       cluster->offset_volt_h = exynos5422_offset_voltage(value);
+
+       value = (reg >> EXYNOS5422_ARM_DN_OFFSET) & EXYNOS5422_ARM_DN_MASK;
+       cluster->offset_volt_l = exynos5422_offset_voltage(value);
+
+       /* KFC offset voltage setup */
+       cluster = &exynos_asv->cluster[CLUSTER_ID_KFC];
+
+       cluster->base_volt = 1000000;
+
+       value = (reg >> EXYNOS5422_KFC_UP_OFFSET) & EXYNOS5422_KFC_UP_MASK;
+       cluster->offset_volt_h = exynos5422_offset_voltage(value);
+
+       value = (reg >> EXYNOS5422_KFC_DN_OFFSET) & EXYNOS5422_KFC_DN_MASK;
+       cluster->offset_volt_l = exynos5422_offset_voltage(value);
+}
+
+int exynos_asv_update_opps(struct device *cpu)
+{
+       struct dev_pm_opp *opp;
+       struct asv_cluster *cluster;
+       unsigned int opp_freq;
+       int cpuid = cpu->id;
+       int cluster_id = -1;
+       int i;
+
+       if (of_device_is_compatible(cpu->of_node, "arm,cortex-a7"))
+               cluster_id = CLUSTER_ID_KFC;
+       else if (of_device_is_compatible(cpu->of_node, "arm,cortex-a15"))
+               cluster_id = CLUSTER_ID_ARM;
+
+       if (cluster_id < 0)
+               return -EINVAL;
+
+       cluster = &exynos_asv->cluster[cluster_id];
+
+       for (i = 0; i < cluster->dvfs_nr; i++) {
+               unsigned int new_voltage;
+               unsigned int voltage;
+               int err;
+
+               opp_freq = cluster->asv_table[i][0];
+
+               opp = dev_pm_opp_find_freq_exact(cpu, opp_freq * 1000, true);
+               if (IS_ERR(opp)) {
+                       pr_info("%s cpu%d opp%d, freq: %u missing\n",
+                               __func__, cpuid, i, opp_freq);
+
+                       continue;
+               }
+
+               voltage = dev_pm_opp_get_voltage(opp);
+               new_voltage = exynos_asv_opp_get_voltage(cluster, opp_freq,
+                                                        voltage);
+               dev_pm_opp_put(opp);
+
+               opp_freq *= 1000;
+               dev_pm_opp_remove(cpu, opp_freq);
+
+               err = dev_pm_opp_add(cpu, opp_freq, new_voltage);
+               if (err < 0)
+                       pr_err("%s: Failed to add OPP %u Hz/%u uV for cpu%d\n",
+                              __func__, opp_freq, new_voltage, cpuid);
+       }
+
+       return 0;
+}
+
+
+static int __init exynos_asv_init(void)
+{
+       const unsigned int (*arm_asv_table)[EXYNOS5422_ASV_NR + 1];
+       const unsigned int (*kfc_asv_table)[EXYNOS5422_ASV_NR + 1];
+       struct opp_table *last_opp_table = NULL;
+       struct device *cpu;
+       int ret, cpuid;
+
+       /* Currently only exynos5800 is supported */
+       if (!of_machine_is_compatible("samsung,exynos5800") &&
+           !of_machine_is_compatible("samsung,exynos5420"))
+               return 0;
+
+       exynos_asv = kmalloc(sizeof(struct exynos_asv), GFP_KERNEL);
+       if (!exynos_asv)
+               return -ENOMEM;
+
+       exynos_asv->bin2 = exynos_asv_get_bin2();
+
+       if (of_machine_is_compatible("hardkernel,odroid-xu3-lite"))
+               exynos_asv->bin2 = true;
+
+       exynos_asv->special_lot = asv_special_group_check();
+       exynos_asv->group = exynos_asv_get_group(exynos_asv);
+       exynos_asv->table = asv_table_check();
+
+       asv_offset_voltage_setup();
+
+       if (exynos_asv->bin2) {
+               arm_asv_table = arm_info_bin2;
+               kfc_asv_table = kfc_info_bin2;
+       } else {
+               switch(exynos_asv->table) {
+               case 2:
+                       arm_asv_table = arm_info_table2;
+                       kfc_asv_table = kfc_info_table2;
+                       break;
+               case 3:
+                       arm_asv_table = arm_info_table3;
+                       kfc_asv_table = kfc_info_table3;
+                       break;
+               default:
+                       arm_asv_table = arm_info_table01;
+                       kfc_asv_table = kfc_info_table01;
+                       break;
+               }
+       }
+
+       exynos_asv->cluster[CLUSTER_ID_ARM].asv_table = arm_asv_table;
+       exynos_asv->cluster[CLUSTER_ID_KFC].asv_table = kfc_asv_table;
+
+       for_each_possible_cpu(cpuid) {
+               struct opp_table *opp_table;
+
+               cpu = get_cpu_device(cpuid);
+               if (!cpu)
+                       continue;
+
+               opp_table = dev_pm_opp_get_opp_table(cpu);
+               if (IS_ERR(opp_table))
+                       continue;
+
+               if (!last_opp_table || opp_table != last_opp_table) {
+                       last_opp_table = opp_table;
+
+                       ret = exynos_asv_update_opps(cpu);
+                       if (ret < 0)
+                               pr_err("%s: Couldn't udate OPPs for cpu%d\n",
+                                      __func__, cpuid);
+               }
+
+               dev_pm_opp_put_opp_table(opp_table);
+       }
+
+       return  0;
+}
+late_initcall(exynos_asv_init);