dvfs: add pvtm support
authorcl <cl@rock-chips.com>
Wed, 5 Nov 2014 02:00:00 +0000 (10:00 +0800)
committercl <cl@rock-chips.com>
Wed, 5 Nov 2014 02:00:28 +0000 (10:00 +0800)
Signed-off-by: cl <cl@rock-chips.com>
arch/arm/boot/dts/rk3288-tb_8846.dts
arch/arm/boot/dts/rk3288.dtsi
arch/arm/mach-rockchip/dvfs.c
include/linux/rockchip/common.h
include/linux/rockchip/dvfs.h

index 9ec042d07fa0d9842c187ec0ef3efc7e9b32db05..3c15425a45f802184b77f313feb3993f7131479c 100644 (file)
        //      1704000 1350000
        //      1800000 1400000
                >;
+        support-pvtm = <1>;
+        pvtm-operating-points = <
+                /* KHz    uV    margin(uV)*/
+                126000 900000   25000
+                216000 900000   25000
+                312000 900000   25000
+                408000 900000   25000
+                600000 900000   25000
+                696000 950000   25000
+                816000 1000000  25000
+                1008000 1050000 25000
+                1200000 1100000 25000
+                1416000 1200000 25000
+                1512000 1300000 25000
+                1608000 1350000 25000
+                >;
        status="okay";
 };
 
index 2c3ed665c3d629719320fadd66d522b4fb68d4a6..481063dc96bcd15feb495b090310da458113a2a5 100755 (executable)
                                                816000 1100000
                                                1008000 1100000
                                                >;
+                                       channel = <0>;
                                        temp-limit-enable = <1>;
                                        target-temp = <80>;
-                                       temp-channel = <1>;
                                        normal-temp-limit = <
                                        /*delta-temp    delta-freq*/
                                                3       96000
                                                300000 1200000
                                                400000 1200000
                                                >;
+                                       channel = <2>;
                                        status = "disabled";
                                };
                        };
                                                300000 1200000
                                                400000 1200000
                                                >;
+                                       channel = <1>;
                                        status = "okay";
                                        regu-mode-table = <
                                                /*freq     mode*/
index ea62aa4d6cd9dbc8df25525015d1a2d6905f30dc..fe12ce36f41c7450e253bfb6e60f9aa2408f5c9d 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/tick.h>
 #include <dt-bindings/clock/rk_system_status.h>
 #include "../../../drivers/clk/rockchip/clk-pd.h"
+#include "efuse.h"
 
 extern int rockchip_tsadc_get_temp(int chn);
 
@@ -858,6 +859,161 @@ static void dvfs_temp_limit_work_func(struct work_struct *work)
 }
 #endif
 
+static struct cpufreq_frequency_table rk3288v0_arm_pvtm_table[] = {
+       {.frequency = 216000,  .index = 4006},
+       {.frequency = 408000,  .index = 6518},
+       {.frequency = 600000,  .index = 8345},
+       {.frequency = 816000,  .index = 11026},
+       {.frequency = 1008000,  .index = 12906},
+       {.frequency = 1200000,  .index = 15532},
+       {.frequency = 1416000,  .index = 18076},
+       {.frequency = 1608000,  .index = 21282},
+       {.frequency = CPUFREQ_TABLE_END, .index = 1},
+};
+
+static struct pvtm_info rk3288v0_arm_pvtm_info = {
+       .compatible = "rockchip,rk3288",
+       .pvtm_table = rk3288v0_arm_pvtm_table,
+       .channel = ARM_DVFS_CH,
+       .process_version = RK3288_PROCESS_V0,
+       .scan_rate_hz = 216000000,
+       .sample_time_us = 1000,
+       .volt_step_uv = 12500,
+       .delta_pvtm_by_volt = 400,
+       .delta_pvtm_by_temp = 14,
+       .volt_margin_uv = 25000,
+       .min_volt_uv = 850000,
+       .max_volt_uv = 1400000,
+};
+
+static struct cpufreq_frequency_table rk3288v1_arm_pvtm_table[] = {
+       {.frequency = 216000,  .index = 4710},
+       {.frequency = 408000,  .index = 7200},
+       {.frequency = 600000,  .index = 9192},
+       {.frequency = 816000,  .index = 12560},
+       {.frequency = 1008000,  .index = 14741},
+       {.frequency = 1200000,  .index = 16886},
+       {.frequency = 1416000,  .index = 20081},
+       {.frequency = 1608000,  .index = 24061},
+       {.frequency = CPUFREQ_TABLE_END, .index = 1},
+};
+
+static struct pvtm_info rk3288v1_arm_pvtm_info = {
+       .compatible = "rockchip,rk3288",
+       .pvtm_table = rk3288v1_arm_pvtm_table,
+       .channel = ARM_DVFS_CH,
+       .process_version = RK3288_PROCESS_V1,
+       .scan_rate_hz = 216000000,
+       .sample_time_us = 1000,
+       .volt_step_uv = 12500,
+       .delta_pvtm_by_volt = 450,
+       .delta_pvtm_by_temp = 7,
+       .volt_margin_uv = 25000,
+       .min_volt_uv = 850000,
+       .max_volt_uv = 1400000,
+};
+
+static struct pvtm_info *pvtm_info_table[] = {
+       &rk3288v0_arm_pvtm_info,
+       &rk3288v1_arm_pvtm_info,
+       NULL
+};
+
+static int pvtm_set_single_dvfs(struct dvfs_node *dvfs_node, u32 idx,
+                               struct pvtm_info *info, int *pvtm_list,
+                               u32 min_pvtm)
+{
+       struct cpufreq_frequency_table *dvfs_table = dvfs_node->dvfs_table;
+       struct cpufreq_frequency_table *pvtm_table = dvfs_node->pvtm_table;
+       int target_pvtm, pvtm_margin, volt_margin;
+       unsigned int n_voltages = dvfs_node->vd->n_voltages;
+       int *volt_list = dvfs_node->vd->volt_list;
+       int n, temp;
+
+       volt_margin = info->volt_margin_uv + pvtm_table[idx].index;
+       n = volt_margin/info->volt_step_uv;
+       if (volt_margin%info->volt_step_uv)
+               n++;
+
+       pvtm_margin = n*info->delta_pvtm_by_volt;
+       temp = rockchip_tsadc_get_temp(1);
+       target_pvtm = min_pvtm+temp * info->delta_pvtm_by_temp + pvtm_margin;
+
+       DVFS_DBG("=====%s: temp:%d, freq:%d, target pvtm:%d=====\n",
+                __func__, temp, dvfs_table[idx].frequency, target_pvtm);
+
+       for (n = 0; n < n_voltages; n++) {
+               if (pvtm_list[n] >= target_pvtm) {
+                       dvfs_table[idx].index = volt_list[n];
+                       DVFS_DBG("freq[%d]=%d, volt=%d\n",
+                                idx, dvfs_table[idx].frequency, volt_list[n]);
+
+                       return 0;
+               }
+       }
+
+       return -EINVAL;
+
+       return 0;
+}
+
+static void pvtm_set_dvfs_table(struct dvfs_node *dvfs_node)
+{
+       struct cpufreq_frequency_table *dvfs_table = dvfs_node->dvfs_table;
+       struct pvtm_info *info = dvfs_node->pvtm_info;
+       struct regulator *regulator = dvfs_node->vd->regulator;
+       int i, j;
+       int ret = 0;
+       int pvtm_list[VD_VOL_LIST_CNT] = {0};
+       unsigned int n_voltages = dvfs_node->vd->n_voltages;
+       int *volt_list = dvfs_node->vd->volt_list;
+
+       if (!info)
+               return;
+
+       clk_set_rate(dvfs_node->clk, info->scan_rate_hz);
+       DVFS_DBG("%s:%lu\n", __func__, clk_get_rate(dvfs_node->clk));
+
+       for (i = 0; i < n_voltages; i++) {
+               if ((volt_list[i] >= info->min_volt_uv) &&
+                   (volt_list[i] <= info->max_volt_uv)) {
+                       regulator_set_voltage(regulator, volt_list[i],
+                                             volt_list[i]);
+                       pvtm_list[i] = pvtm_get_value(info->channel,
+                                                     info->sample_time_us);
+               }
+       }
+
+       for (i = 0; dvfs_table[i].frequency != CPUFREQ_TABLE_END; i++) {
+               for (j = 0; info->pvtm_table[j].frequency !=
+                    CPUFREQ_TABLE_END; j++)
+                       if (info->pvtm_table[j].frequency >=
+                           dvfs_table[i].frequency) {
+                               int min_pvtm = info->pvtm_table[j].index;
+
+                               ret = pvtm_set_single_dvfs(dvfs_node,
+                                                          i,
+                                                          info,
+                                                          pvtm_list,
+                                                          min_pvtm);
+                               break;
+                       }
+
+               if (ret) {
+                       DVFS_WARNING("freq: %d can not reach target pvtm\n",
+                                    dvfs_table[i].frequency);
+                       break;
+               }
+
+               if (info->pvtm_table[j].frequency == CPUFREQ_TABLE_END) {
+                       DVFS_WARNING("not support freq :%d, max freq is %d\n",
+                                    dvfs_table[i].frequency,
+                                    info->pvtm_table[j-1].frequency);
+                       break;
+               }
+       }
+}
+
 static void dvfs_virt_temp_limit_work_func(void)
 {
        const struct cpufreq_frequency_table *limits_table = NULL;
@@ -995,7 +1151,6 @@ static void dvfs_temp_limit_work_func(struct work_struct *work)
 }
 static DECLARE_DELAYED_WORK(dvfs_temp_limit_work, dvfs_temp_limit_work_func);
 
-
 int dvfs_clk_enable_limit(struct dvfs_node *clk_dvfs_node, unsigned int min_rate, unsigned int max_rate)
 {
        u32 rate = 0, ret = 0;
@@ -1144,7 +1299,6 @@ int clk_enable_dvfs(struct dvfs_node *clk_dvfs_node)
        unsigned int mode;
        int ret;
 
-
        if (!clk_dvfs_node)
                return -EINVAL;
        
@@ -1184,6 +1338,8 @@ int clk_enable_dvfs(struct dvfs_node *clk_dvfs_node)
                dvfs_table_round_clk_rate(clk_dvfs_node);
                dvfs_get_rate_range(clk_dvfs_node);
                clk_dvfs_node->freq_limit_en = 1;
+               if (clk_dvfs_node->support_pvtm)
+                       pvtm_set_dvfs_table(clk_dvfs_node);
                dvfs_table_round_volt(clk_dvfs_node);
                clk_dvfs_node->set_freq = clk_dvfs_node_get_rate_kz(clk_dvfs_node->clk);
                clk_dvfs_node->last_set_rate = clk_dvfs_node->set_freq*1000;
@@ -1587,25 +1743,6 @@ int rk_regist_clk(struct dvfs_node *clk_dvfs_node)
 }
 EXPORT_SYMBOL_GPL(rk_regist_clk);
 
-static int rk_convert_cpufreq_table(struct dvfs_node *dvfs_node)
-{
-       struct opp *opp;
-       struct device *dev;
-       struct cpufreq_frequency_table *table;
-       int i;
-
-       table = dvfs_node->dvfs_table;
-       dev = &dvfs_node->dev;
-
-       for (i = 0; table[i].frequency!= CPUFREQ_TABLE_END; i++){
-               opp = opp_find_freq_exact(dev, table[i].frequency * 1000, true);
-               if (IS_ERR(opp))
-                       return PTR_ERR(opp);
-               table[i].index = opp_get_voltage(opp);
-       }
-       return 0;
-}
-
 static struct cpufreq_frequency_table *of_get_temp_limit_table(struct device_node *dev_node, const char *propname)
 {
        struct cpufreq_frequency_table *temp_limt_table = NULL;
@@ -1642,6 +1779,167 @@ static struct cpufreq_frequency_table *of_get_temp_limit_table(struct device_nod
 
 }
 
+static int of_get_dvfs_table(struct device_node *dev_node,
+                            struct cpufreq_frequency_table **dvfs_table)
+{
+       struct cpufreq_frequency_table *tmp_dvfs_table = NULL;
+       const struct property *prop;
+       const __be32 *val;
+       int nr, i;
+
+       prop = of_find_property(dev_node, "operating-points", NULL);
+       if (!prop)
+               return -EINVAL;
+       if (!prop->value)
+               return -EINVAL;
+
+       nr = prop->length / sizeof(u32);
+       if (nr % 2) {
+               pr_err("%s: Invalid freq list\n", __func__);
+               return -EINVAL;
+       }
+
+       tmp_dvfs_table = kzalloc(sizeof(*tmp_dvfs_table) *
+                            (nr/2 + 1), GFP_KERNEL);
+       val = prop->value;
+
+       for (i = 0; i < nr/2; i++) {
+               tmp_dvfs_table[i].frequency = be32_to_cpup(val++);
+               tmp_dvfs_table[i].index = be32_to_cpup(val++);
+       }
+
+       tmp_dvfs_table[i].index = 0;
+       tmp_dvfs_table[i].frequency = CPUFREQ_TABLE_END;
+
+       *dvfs_table = tmp_dvfs_table;
+
+       return 0;
+}
+
+
+static int of_get_dvfs_pvtm_table(struct device_node *dev_node,
+                                 struct cpufreq_frequency_table **dvfs_table,
+                                 struct cpufreq_frequency_table **pvtm_table)
+{
+       struct cpufreq_frequency_table *tmp_dvfs_table = NULL;
+       struct cpufreq_frequency_table *tmp_pvtm_table = NULL;
+       const struct property *prop;
+       const __be32 *val;
+       int nr, i;
+
+       prop = of_find_property(dev_node, "pvtm-operating-points", NULL);
+       if (!prop)
+               return -EINVAL;
+       if (!prop->value)
+               return -EINVAL;
+
+       nr = prop->length / sizeof(u32);
+       if (nr % 3) {
+               pr_err("%s: Invalid freq list\n", __func__);
+               return -EINVAL;
+       }
+
+       tmp_dvfs_table = kzalloc(sizeof(*tmp_dvfs_table) *
+                            (nr/3 + 1), GFP_KERNEL);
+
+       tmp_pvtm_table = kzalloc(sizeof(*tmp_pvtm_table) *
+                            (nr/3 + 1), GFP_KERNEL);
+
+       val = prop->value;
+
+       for (i = 0; i < nr/3; i++) {
+               tmp_dvfs_table[i].frequency = be32_to_cpup(val++);
+               tmp_dvfs_table[i].index = be32_to_cpup(val++);
+
+               tmp_pvtm_table[i].frequency = tmp_dvfs_table[i].frequency;
+               tmp_pvtm_table[i].index = be32_to_cpup(val++);
+       }
+
+       tmp_dvfs_table[i].index = 0;
+       tmp_dvfs_table[i].frequency = CPUFREQ_TABLE_END;
+
+       tmp_pvtm_table[i].index = 0;
+       tmp_pvtm_table[i].frequency = CPUFREQ_TABLE_END;
+
+       *dvfs_table = tmp_dvfs_table;
+       *pvtm_table = tmp_pvtm_table;
+
+       return 0;
+}
+
+static int dvfs_node_parse_dt(struct device_node *np,
+                             struct dvfs_node *dvfs_node)
+{
+       int process_version = rockchip_process_version();
+       int i = 0;
+       int ret;
+
+       of_property_read_u32_index(np, "channel", 0, &dvfs_node->channel);
+
+       pr_info("channel:%d, lkg:%d\n",
+               dvfs_node->channel, rockchip_get_leakage(dvfs_node->channel));
+
+       of_property_read_u32_index(np, "regu-mode-en", 0,
+                                  &dvfs_node->regu_mode_en);
+       if (dvfs_node->regu_mode_en)
+               dvfs_node->regu_mode_table = of_get_regu_mode_table(np);
+       else
+               dvfs_node->regu_mode_table = NULL;
+
+       of_property_read_u32_index(np, "temp-limit-enable", 0,
+                                  &temp_limit_enable);
+       if (temp_limit_enable) {
+               of_property_read_u32_index(np, "target-temp", 0, &target_temp);
+               pr_info("target-temp:%d\n", target_temp);
+               dvfs_node->nor_temp_limit_table =
+                       of_get_temp_limit_table(np,
+                                               "normal-temp-limit");
+               dvfs_node->per_temp_limit_table =
+                       of_get_temp_limit_table(np,
+                                               "performance-temp-limit");
+               dvfs_node->virt_temp_limit_table[0] =
+                       of_get_temp_limit_table(np,
+                                               "virt-temp-limit-1-cpu-busy");
+               dvfs_node->virt_temp_limit_table[1] =
+                       of_get_temp_limit_table(np,
+                                               "virt-temp-limit-2-cpu-busy");
+               dvfs_node->virt_temp_limit_table[2] =
+                       of_get_temp_limit_table(np,
+                                               "virt-temp-limit-3-cpu-busy");
+               dvfs_node->virt_temp_limit_table[3] =
+                       of_get_temp_limit_table(np,
+                                               "virt-temp-limit-4-cpu-busy");
+       }
+       dvfs_node->temp_limit_rate = -1;
+
+       ret = of_property_read_u32_index(np, "support-pvtm", 0,
+                                        &dvfs_node->support_pvtm);
+       if (!ret) {
+               if (of_get_dvfs_pvtm_table(np, &dvfs_node->dvfs_table,
+                                          &dvfs_node->pvtm_table))
+                       return -EINVAL;
+
+               for (i = 0; i < ARRAY_SIZE(pvtm_info_table); i++) {
+                       struct pvtm_info *pvtm_info = pvtm_info_table[i];
+
+                       if ((pvtm_info->channel == dvfs_node->channel) &&
+                           (pvtm_info->process_version == process_version) &&
+                            of_machine_is_compatible(pvtm_info->compatible)) {
+                               dvfs_node->pvtm_info = pvtm_info;
+                               break;
+                       }
+               }
+
+               if (!dvfs_node->pvtm_info)
+                       dvfs_node->support_pvtm = 0;
+       } else {
+               if (of_get_dvfs_table(np, &dvfs_node->dvfs_table))
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
 int of_dvfs_init(void)
 {
        struct vd_node *vd;
@@ -1649,10 +1947,10 @@ int of_dvfs_init(void)
        struct device_node *dvfs_dev_node, *clk_dev_node, *vd_dev_node, *pd_dev_node;
        struct dvfs_node *dvfs_node;
        struct clk *clk;
-       const __be32 *val;
        int ret;
 
        DVFS_DBG("%s\n", __func__);
+       pr_info("process version: %d\n", rockchip_process_version());
 
        dvfs_dev_node = of_find_node_by_name(NULL, "dvfs");
        if (IS_ERR_OR_NULL(dvfs_dev_node)) {
@@ -1717,56 +2015,8 @@ int of_dvfs_init(void)
                                dvfs_node->pd = pd;
                                dvfs_node->vd = vd;
 
-                               val = of_get_property(clk_dev_node, "regu-mode-en", NULL);
-                               if (val)
-                                       dvfs_node->regu_mode_en = be32_to_cpup(val);
-                               if (dvfs_node->regu_mode_en)
-                                       dvfs_node->regu_mode_table = of_get_regu_mode_table(clk_dev_node);
-                               else
-                                       dvfs_node->regu_mode_table = NULL;
-
-                               val = of_get_property(clk_dev_node, "temp-limit-enable", NULL);
-                               if (val)
-                                       temp_limit_enable = be32_to_cpup(val);
-                               if (temp_limit_enable) {
-                                       val = of_get_property(clk_dev_node, "target-temp", NULL);
-                                       if (val)
-                                               target_temp = be32_to_cpup(val);
-                                       val = of_get_property(clk_dev_node, "temp-channel", NULL);
-                                       if (val)
-                                               dvfs_node->temp_channel = be32_to_cpup(val);
-
-                                       dvfs_node->nor_temp_limit_table = of_get_temp_limit_table(clk_dev_node, "normal-temp-limit");
-                                       dvfs_node->per_temp_limit_table = of_get_temp_limit_table(clk_dev_node, "performance-temp-limit");
-                                       dvfs_node->virt_temp_limit_table[0] =
-                                               of_get_temp_limit_table(clk_dev_node, "virt-temp-limit-1-cpu-busy");
-                                       dvfs_node->virt_temp_limit_table[1] =
-                                               of_get_temp_limit_table(clk_dev_node, "virt-temp-limit-2-cpu-busy");
-                                       dvfs_node->virt_temp_limit_table[2] =
-                                               of_get_temp_limit_table(clk_dev_node, "virt-temp-limit-3-cpu-busy");
-                                       dvfs_node->virt_temp_limit_table[3] =
-                                               of_get_temp_limit_table(clk_dev_node, "virt-temp-limit-4-cpu-busy");
-                               }
-                               dvfs_node->temp_limit_rate = -1;
-                               dvfs_node->dev.of_node = clk_dev_node;
-                               ret = of_init_opp_table(&dvfs_node->dev);
-                               if (ret) {
-                                       DVFS_ERR("%s:clk(%s) get opp table err:%d\n", __func__, dvfs_node->name, ret);
-                                       kfree(dvfs_node);
-                                       continue;
-                               }
-                               
-                               ret = opp_init_cpufreq_table(&dvfs_node->dev, &dvfs_node->dvfs_table);
-                               if (ret) {
-                                       DVFS_ERR("%s:clk(%s) get cpufreq table err:%d\n", __func__, dvfs_node->name, ret);
-                                       kfree(dvfs_node);
+                               if (dvfs_node_parse_dt(clk_dev_node, dvfs_node))
                                        continue;
-                               }
-                               ret = rk_convert_cpufreq_table(dvfs_node);
-                               if (ret) {
-                                       kfree(dvfs_node);
-                                       continue;
-                               }
                                
                                clk = clk_get(NULL, clk_dev_node->name);
                                if (IS_ERR(clk)){
index 986943afa2f148d45ee0c4320c60cac3df63a395..cf51a1ce95c1b4d3bdcc4b95e8bf37deb4c01b78 100644 (file)
@@ -76,6 +76,7 @@ int rockchip_unregister_system_status_notifier(struct notifier_block *nb);
 int rockchip_set_system_status(unsigned long status);
 int rockchip_clear_system_status(unsigned long status);
 unsigned long rockchip_get_system_status(void);
+u32 pvtm_get_value(u32 ch, u32 time_us);
 
 #define INVALID_TEMP INT_MAX
 #if IS_ENABLED(CONFIG_SENSORS_ROCKCHIP_TSADC)
index 209374a211825590c0448694b63e19f98f3b5391..47c3d084fd9d59b6e1615de0981df4a2ddc7e3b6 100644 (file)
 #include <linux/device.h>
 #include <linux/clk-provider.h>
 
+#define ARM_DVFS_CH    0
+#define GPU_DVFS_CH    1
+#define LOG_DVFS_CH    2
+
 struct dvfs_node;
 typedef int (*dvfs_set_rate_callback)(struct dvfs_node *clk_dvfs_node, unsigned long rate);
 typedef int (*clk_set_rate_callback)(struct clk *clk, unsigned long rate);
@@ -76,6 +80,21 @@ struct pd_node {
        unsigned int            regu_mode;
 };
 
+struct pvtm_info {
+       const char *compatible;
+       struct cpufreq_frequency_table *pvtm_table;
+       int channel;
+       int process_version;
+       int scan_rate_hz;
+       int sample_time_us;
+       int volt_step_uv;
+       int delta_pvtm_by_volt;
+       int delta_pvtm_by_temp;
+       int volt_margin_uv;
+       int min_volt_uv;
+       int max_volt_uv;
+};
+
 /**
  * struct dvfs_node:   To Store All dvfs clocks' info
  * @name:              Dvfs clock's Name
@@ -96,9 +115,11 @@ struct dvfs_node {
        int                     set_volt;       //MV
        int                     enable_count;
        int                     freq_limit_en;  //sign if use limit frequency
+       int                     support_pvtm;
        unsigned int            min_rate;       //limit min frequency
        unsigned int            max_rate;       //limit max frequency
        unsigned long           last_set_rate;
+       unsigned int            channel;
        unsigned int            temp_channel;
        unsigned long           temp_limit_rate;
        struct clk              *clk;
@@ -107,6 +128,7 @@ struct dvfs_node {
        struct list_head        node;
        struct notifier_block   *dvfs_nb;
        struct cpufreq_frequency_table  *dvfs_table;
+       struct cpufreq_frequency_table  *pvtm_table;
        struct cpufreq_frequency_table  *per_temp_limit_table;
        struct cpufreq_frequency_table  *nor_temp_limit_table;
        struct cpufreq_frequency_table  *virt_temp_limit_table[4];
@@ -114,6 +136,7 @@ struct dvfs_node {
        struct cpufreq_frequency_table  *regu_mode_table;
        int                     regu_mode_en;
        unsigned int            regu_mode;
+       struct pvtm_info        *pvtm_info;
 };