Loading Documentation/devicetree/bindings/regulator/mem-acc-regulator.txt +117 −5 Original line number Diff line number Diff line Loading @@ -10,11 +10,6 @@ Required properties: should be 1 for SVS corner - regulator-max-microvolt: Maximum corner value as max constraint, which should be 4 for SUPER_TURBO or 3 for TURBO - qcom,corner-acc-map Array which maps the APC (application processor) corner value to the accelerator corner. [0] maps APC SVS corner (1) to accelerator SVS corner [1] maps APC NOMINAL corner (2) to accelerator NOMINAL corner [2] maps APC TURBO corner (3) to accelerator TURBO corner Optional properties: - reg: Register addresses for acc-sel-l1, acc-sel-l2 control, acc-en, Loading @@ -29,6 +24,11 @@ Optional properties: A given mem-acc-regulator driver must have "acc-sel-l1" or "acc-sel-l2" or "mem-acc-type*" reg-names property and related register address property. - qcom,corner-acc-map Array which maps the APC (application processor) corner value to the accelerator corner. The number of elements in this property defines the number of accelerator corners. Either qcom,corner-acc-map property or qcom,cornerX-reg-config properties should be specified. - qcom,acc-en-bit-pos Array which specifies bit positions in the 'acc-en' register. Setting these bits forces the the acclerator to use the corner value specified Loading Loading @@ -116,6 +116,49 @@ Optional properties: - qcom,mem-acc-type4: Same as qcom,mem-acc-type1 except for mem acc type4 register. - qcom,mem-acc-type5: Same as qcom,mem-acc-type1 except for mem acc type5 register. - qcom,mem-acc-type6: Same as qcom,mem-acc-type1 except for mem acc type6 register. - qcom,acc-reg-addr-list: Array of register addresses which need to be programmed during any corner switch. This property can be used when multi register configuration is needed during a corner switch. - qcom,acc-init-reg-config: Array of tuples specify the multi register configuration sequence need to be programmed one time during device boot. The format of each tuple as below: <register-address-index, value> Where register-address-index is used as an index in to qcom,acc-reg-addr-list property to get the required register address and the value is programmed in to the corresponding mapped register address. This property is required if qcom,acc-corner-addr-val-map property specified. - qcom,cornerX-reg-config: Array of tuples specify the multi register configuration sequence need to be programmed when switching from acc corner X to any other corner. The possible values for X are {1, N}, where N is the value defined in qcom,num-acc-corners. The format of each tuple as below: <register-address-index, value> Where register-address-index is used as an index in to qcom,acc-reg-addr-list property to get the required register address and the value is programmed in to the corresponding mapped register address. Same index can be used multiple times when the register is required to configure multiple times with different values in the sequence. The number of register configuration sequences should be equal to N, where N is the value specified in qcom,num-acc-corners property. Also, the number of tuples in each register configuration sequence should be same and must be equal to the maximum required register configurations in any sequence. The invalid register configuration can be specified as <(-1) (-1)>. This property can only be specified when qcom,acc-corner-addr-val-map property already defined. Either this property or qcom,corner-acc-map should be specified. - qcom,num-acc-corners: The number of acc corners supported. This property is required if qcom,cornerX-reg-config property specified. - qcom,boot-acc-corner: The acc corner used during device boot. This property is required if qcom,cornerX-reg-config property specified. - qcom,override-cornerX-reg-config: A grouping of register configuration sequence lists. Each list is same as the qcom,cornerX-reg-config property. The possible values for X are {1, N} where N is the value defined in qcom,num-acc-corners. This property is used to specify the different register configuration sequence lists and select one list among them based on the selected index in qcom,override-fuse-version-map property. The selected list overrides the existing register configuration sequence list specified in "qcom,cornerX-reg-config". If the "qcom,override-fuse-version-map" property is specified, then "qcom,override-cornerX-reg-config" must contain the same number of register configuration sequence lists as the number of tuples in "qcom,override-fuse-version-map". These register configuration sequence lists are then mapped one-to-one in the order specified. If the qcom,override-fuse-version-map property is not specified, then "qcom,override-cornerX-reg-config" must contain a single register configuration sequence list which is then applied unconditionally. This property can only be specified if qcom,cornerX-reg-config property is already defined. mem_acc_vreg_corner: regulator@fd4aa044 { compatible = "qcom,mem-acc-regulator"; Loading Loading @@ -151,4 +194,73 @@ mem_acc_vreg_corner: regulator@fd4aa044 { qcom,mem-acc-type2 = <0x02 0x02 0x00>; qcom,mem-acc-type3 = <0x02 0x02 0x00>; qcom,mem-acc-type4 = <0x02 0x02 0x00>; qcom,acc-reg-addr-list = <0x01942130 0x01942124 0x01942120>; qcom,acc-init-reg-config = <1 0x55> <2 0x02>; qcom,num-acc-corners = <3>; qcom,boot-acc-corner = <2>; qcom,corner1-reg-config = <(-1) (-1)>, <(-1) (-1)>, <(-1) (-1)>, /* 1 -> 1 */ < 1 0x155>, < 2 0x0>, < 3 0x155>, /* 1 -> 2 */ < 1 0x0>, < 2 0x155>, <(-1) (-1)>; /* 1 -> 3 */ qcom,corner2-reg-config = < 1 0x155>, <(-1) (-1)>, <(-1) (-1)>, /* 2 -> 1 */ <(-1) (-1)>, <(-1) (-1)>, <(-1) (-1)>, /* 2 -> 2 */ < 1 0x155>, < 2 0x0>, < 3 0x155>; /* 2 -> 3 */ qcom,corner3-reg-config = < 1 0x0>, < 2 0x155>, <(-1) (-1)>, /* 3 -> 1 */ < 1 0x155>, <(-1) (-1)>, <(-1) (-1)>, /* 3 -> 2 */ <(-1) (-1)>, <(-1) (-1)>, <(-1) (-1)>; /* 3 -> 3 */ qcom,override-corner1-reg-config = /* 1st fuse version tuple matched */ <(-1) (-1)>, <(-1) (-1)>, <(-1) (-1)>, /* 1 -> 1 */ < 1 0x155>, < 2 0x0>, < 3 0x155>, /* 1 -> 2 */ < 1 0x0>, < 2 0x155>, <(-1) (-1)>, /* 1 -> 3 */ /* 2nd fuse version tuple matched */ <(-1) (-1)>, <(-1) (-1)>, <(-1) (-1)>, /* 1 -> 1 */ < 1 0x155>, < 2 0x0>, < 3 0x155>, /* 1 -> 2 */ < 1 0x0>, < 2 0x155>, <(-1) (-1)>, /* 1 -> 3 */ /* 3rd fuse version tuple matched */ <(-1) (-1)>, <(-1) (-1)>, <(-1) (-1)>, /* 1 -> 1 */ < 1 0x155>, < 3 0x22>, < 3 0x155>, /* 1 -> 2 */ < 1 0x0>, < 2 0x155>, < 3 0x144>; /* 1 -> 3 */ qcom,override-corner2-reg-config = /* 1st fuse version tuple matched */ < 1 0x144>, < 1 0x11>, <(-1) (-1)>, /* 2 -> 1 */ <(-1) (-1)>, <(-1) (-1)>, <(-1) (-1)>, /* 2 -> 2 */ < 1 0x155>, < 2 0x0>, < 3 0x155>, /* 2 -> 3 */ /* 2nd fuse version tuple matched */ < 1 0x144>, < 2 0x133>, <(-1) (-1)>, /* 2 -> 1 */ <(-1) (-1)>, < 1 0x33>, <(-1) (-1)>, /* 2 -> 2 */ < 1 0x133>, < 2 0x0>, < 3 0x155>, /* 2 -> 3 */ /* 3rd fuse version tuple matched */ < 1 0x144>, < 1 0x11>, <(-1) (-1)>, /* 2 -> 1 */ <(-1) (-1)>, <(-1) (-1)>, <(-1) (-1)>, /* 2 -> 2 */ < 1 0x155>, < 2 0x22>, < 3 0x155>; /* 2 -> 3 */ qcom,override-corner3-reg-config = /* 1st fuse version tuple matched */ < 1 0x0>, < 2 0x155>, <(-1) (-1)>, /* 3 -> 1 */ < 1 0x155>, <(-1) (-1)>, <(-1) (-1)>, /* 3 -> 2 */ <(-1) (-1)>, <(-1) (-1)>, <(-1) (-1)>, /* 3 -> 3 */ /* 2nd fuse version tuple matched */ < 1 0x0>, < 2 0x155>, <(-1) (-1)>, /* 3 -> 1 */ < 1 0x155>, <(-1) (-1)>, <(-1) (-1)>, /* 3 -> 2 */ <(-1) (-1)>, <(-1) (-1)>, <(-1) (-1)>, /* 3 -> 3 */ /* 3rd fuse version tuple matched */ < 1 0x0>, < 2 0x155>, <(-1) (-1)>, /* 3 -> 1 */ < 1 0x155>, < 3 0x11>, <(-1) (-1)>, /* 3 -> 2 */ <(-1) (-1)>, <(-1) (-1)>, <(-1) (-1)>; /* 3 -> 3 */ }; drivers/regulator/mem-acc-regulator.c +472 −20 Original line number Diff line number Diff line Loading @@ -32,8 +32,15 @@ #define BYTES_PER_FUSE_ROW 8 /* mem-acc config flags */ enum { MEM_ACC_USE_CORNER_ACC_MAP = BIT(0), MEM_ACC_USE_ADDR_VAL_MAP = BIT(1), }; #define FUSE_MAP_NO_MATCH (-1) #define FUSE_PARAM_MATCH_ANY (-1) #define PARAM_MATCH_ANY (-1) enum { MEMORY_L1, Loading @@ -43,6 +50,22 @@ enum { #define MEM_ACC_TYPE_MAX 6 /** * struct acc_reg_value - Acc register configuration structure * @addr_index: An index in to phys_reg_addr_list and remap_reg_addr_list * to get the ACC register physical address and remapped address. * @reg_val: Value to program in to the register mapped by addr_index. */ struct acc_reg_value { u32 addr_index; u32 reg_val; }; struct corner_acc_reg_config { struct acc_reg_value *reg_config_list; int max_reg_config_len; }; struct mem_acc_regulator { struct device *dev; struct regulator_desc rdesc; Loading Loading @@ -80,6 +103,11 @@ struct mem_acc_regulator { /* eFuse parameters */ phys_addr_t efuse_addr; void __iomem *efuse_base; u32 num_acc_reg; u32 *phys_reg_addr_list; void __iomem **remap_reg_addr_list; struct corner_acc_reg_config *corner_acc_reg_config; }; static DEFINE_MUTEX(mem_acc_memory_mutex); Loading Loading @@ -205,6 +233,41 @@ static void update_acc_sel(struct mem_acc_regulator *mem_acc_vreg, int corner) __update_acc_type(mem_acc_vreg, corner); } static void update_acc_reg(struct mem_acc_regulator *mem_acc_vreg, int corner) { struct corner_acc_reg_config *corner_acc_reg_config; struct acc_reg_value *reg_config_list; int i, index; u32 addr_index, reg_val; corner_acc_reg_config = &mem_acc_vreg->corner_acc_reg_config[mem_acc_vreg->corner]; reg_config_list = corner_acc_reg_config->reg_config_list; for (i = 0; i < corner_acc_reg_config->max_reg_config_len; i++) { /* * Use (corner - 1) in the below equation as * the reg_config_list[] stores the values starting from * index '0' where as the minimum corner value allowed * in regulator framework is '1'. */ index = (corner - 1) * corner_acc_reg_config->max_reg_config_len + i; addr_index = reg_config_list[index].addr_index; reg_val = reg_config_list[index].reg_val; if (addr_index == PARAM_MATCH_ANY) break; writel_relaxed(reg_val, mem_acc_vreg->remap_reg_addr_list[addr_index]); /* make sure write complete */ mb(); pr_debug("corner=%d register:0x%x value:0x%x\n", corner, mem_acc_vreg->phys_reg_addr_list[addr_index], reg_val); } } static int mem_acc_regulator_set_voltage(struct regulator_dev *rdev, int corner, int corner_max, unsigned *selector) { Loading @@ -224,6 +287,10 @@ static int mem_acc_regulator_set_voltage(struct regulator_dev *rdev, /* go up or down one level at a time */ mutex_lock(&mem_acc_memory_mutex); if (mem_acc_vreg->flags & MEM_ACC_USE_ADDR_VAL_MAP) { update_acc_reg(mem_acc_vreg, corner); } else if (mem_acc_vreg->flags & MEM_ACC_USE_CORNER_ACC_MAP) { if (corner > mem_acc_vreg->corner) { for (i = mem_acc_vreg->corner + 1; i <= corner; i++) { pr_debug("UP: to corner %d\n", i); Loading @@ -235,6 +302,8 @@ static int mem_acc_regulator_set_voltage(struct regulator_dev *rdev, update_acc_sel(mem_acc_vreg, i); } } } mutex_unlock(&mem_acc_memory_mutex); pr_debug("new voltage corner set %d\n", corner); Loading Loading @@ -638,10 +707,356 @@ done: return rc; } #define MAX_CHARS_PER_INT 20 static int mem_acc_reg_addr_val_dump(struct mem_acc_regulator *mem_acc_vreg, struct corner_acc_reg_config *corner_acc_reg_config, u32 corner) { int i, k, index, pos = 0; u32 addr_index; size_t buflen; char *buf; struct acc_reg_value *reg_config_list = corner_acc_reg_config->reg_config_list; int max_reg_config_len = corner_acc_reg_config->max_reg_config_len; int num_corners = mem_acc_vreg->num_corners; /* * Log register and value mapping since they are useful for * baseline MEM ACC logging. */ buflen = max_reg_config_len * (MAX_CHARS_PER_INT + 6) * sizeof(*buf); buf = kzalloc(buflen, GFP_KERNEL); if (buf == NULL) { pr_err("Could not allocate memory for acc register and value logging\n"); return -ENOMEM; } for (i = 0; i < num_corners; i++) { if (corner == i + 1) continue; pr_debug("Corner: %d --> %d:\n", corner, i + 1); pos = 0; for (k = 0; k < max_reg_config_len; k++) { index = i * max_reg_config_len + k; addr_index = reg_config_list[index].addr_index; if (addr_index == PARAM_MATCH_ANY) break; pos += scnprintf(buf + pos, buflen - pos, "<0x%x 0x%x> ", mem_acc_vreg->phys_reg_addr_list[addr_index], reg_config_list[index].reg_val); } buf[pos] = '\0'; pr_debug("%s\n", buf); } return 0; } static int mem_acc_get_reg_addr_val(struct device_node *of_node, const char *prop_str, struct acc_reg_value *reg_config_list, int list_offset, int list_size, u32 max_reg_index) { int i, index, rc = 0; for (i = 0; i < list_size / 2; i++) { index = (list_offset * list_size) + i * 2; rc = of_property_read_u32_index(of_node, prop_str, index, ®_config_list[i].addr_index); rc |= of_property_read_u32_index(of_node, prop_str, index + 1, ®_config_list[i].reg_val); if (rc) { pr_err("could not read %s at tuple %u: rc=%d\n", prop_str, index, rc); return rc; } if (reg_config_list[i].addr_index == PARAM_MATCH_ANY) continue; if ((!reg_config_list[i].addr_index) || reg_config_list[i].addr_index > max_reg_index) { pr_err("Invalid register index %u in %s at tuple %u\n", reg_config_list[i].addr_index, prop_str, index); return -EINVAL; } } return rc; } static int mem_acc_init_reg_config(struct mem_acc_regulator *mem_acc_vreg) { struct device_node *of_node = mem_acc_vreg->dev->of_node; int i, size, len = 0, rc = 0; u32 addr_index, reg_val, index; char *prop_str = "qcom,acc-init-reg-config"; if (!of_find_property(of_node, prop_str, &len)) { /* Initial acc register configuration not specified */ return rc; } size = len / sizeof(u32); if ((!size) || (size % 2)) { pr_err("%s specified with invalid length: %d\n", prop_str, size); return -EINVAL; } for (i = 0; i < size / 2; i++) { index = i * 2; rc = of_property_read_u32_index(of_node, prop_str, index, &addr_index); rc |= of_property_read_u32_index(of_node, prop_str, index + 1, ®_val); if (rc) { pr_err("could not read %s at tuple %u: rc=%d\n", prop_str, index, rc); return rc; } if ((!addr_index) || addr_index > mem_acc_vreg->num_acc_reg) { pr_err("Invalid register index %u in %s at tuple %u\n", addr_index, prop_str, index); return -EINVAL; } writel_relaxed(reg_val, mem_acc_vreg->remap_reg_addr_list[addr_index]); /* make sure write complete */ mb(); pr_debug("acc initial config: register:0x%x value:0x%x\n", mem_acc_vreg->phys_reg_addr_list[addr_index], reg_val); } return rc; } static int mem_acc_get_reg_addr(struct mem_acc_regulator *mem_acc_vreg) { struct device_node *of_node = mem_acc_vreg->dev->of_node; void __iomem **remap_reg_addr_list; u32 *phys_reg_addr_list; int i, num_acc_reg, len = 0, rc = 0; if (!of_find_property(of_node, "qcom,acc-reg-addr-list", &len)) { /* acc register address list not specified */ return rc; } num_acc_reg = len / sizeof(u32); if (!num_acc_reg) { pr_err("qcom,acc-reg-addr-list has invalid len = %d\n", len); return -EINVAL; } phys_reg_addr_list = devm_kcalloc(mem_acc_vreg->dev, num_acc_reg + 1, sizeof(*phys_reg_addr_list), GFP_KERNEL); if (!phys_reg_addr_list) return -ENOMEM; remap_reg_addr_list = devm_kcalloc(mem_acc_vreg->dev, num_acc_reg + 1, sizeof(*remap_reg_addr_list), GFP_KERNEL); if (!remap_reg_addr_list) return -ENOMEM; rc = of_property_read_u32_array(of_node, "qcom,acc-reg-addr-list", &phys_reg_addr_list[1], num_acc_reg); if (rc) { pr_err("Read- qcom,acc-reg-addr-list failed: rc=%d\n", rc); return rc; } for (i = 1; i <= num_acc_reg; i++) { remap_reg_addr_list[i] = devm_ioremap(mem_acc_vreg->dev, phys_reg_addr_list[i], 0x4); if (!remap_reg_addr_list[i]) { pr_err("Unable to map register address 0x%x\n", phys_reg_addr_list[i]); return -EINVAL; } } mem_acc_vreg->num_acc_reg = num_acc_reg; mem_acc_vreg->phys_reg_addr_list = phys_reg_addr_list; mem_acc_vreg->remap_reg_addr_list = remap_reg_addr_list; return rc; } static int mem_acc_reg_config_init(struct mem_acc_regulator *mem_acc_vreg) { struct device_node *of_node = mem_acc_vreg->dev->of_node; struct acc_reg_value *reg_config_list; int len, size, rc, i, num_corners; struct property *prop; char prop_str[30]; struct corner_acc_reg_config *corner_acc_reg_config; rc = of_property_read_u32(of_node, "qcom,num-acc-corners", &num_corners); if (rc) { pr_err("could not read qcom,num-acc-corners: rc=%d\n", rc); return rc; } mem_acc_vreg->num_corners = num_corners; rc = of_property_read_u32(of_node, "qcom,boot-acc-corner", &mem_acc_vreg->corner); if (rc) { pr_err("could not read qcom,boot-acc-corner: rc=%d\n", rc); return rc; } pr_debug("boot acc corner = %d\n", mem_acc_vreg->corner); corner_acc_reg_config = devm_kcalloc(mem_acc_vreg->dev, num_corners + 1, sizeof(*corner_acc_reg_config), GFP_KERNEL); if (!corner_acc_reg_config) return -ENOMEM; for (i = 1; i <= num_corners; i++) { snprintf(prop_str, sizeof(prop_str), "qcom,corner%d-reg-config", i); prop = of_find_property(of_node, prop_str, &len); size = len / sizeof(u32); if ((!prop) || (!size) || size < (num_corners * 2)) { pr_err("%s property is missed or invalid length: len=%d\n", prop_str, len); return -EINVAL; } reg_config_list = devm_kcalloc(mem_acc_vreg->dev, size / 2, sizeof(*reg_config_list), GFP_KERNEL); if (!reg_config_list) return -ENOMEM; rc = mem_acc_get_reg_addr_val(of_node, prop_str, reg_config_list, 0, size, mem_acc_vreg->num_acc_reg); if (rc) { pr_err("Failed to read %s property: rc=%d\n", prop_str, rc); return rc; } corner_acc_reg_config[i].max_reg_config_len = size / (num_corners * 2); corner_acc_reg_config[i].reg_config_list = reg_config_list; rc = mem_acc_reg_addr_val_dump(mem_acc_vreg, &corner_acc_reg_config[i], i); if (rc) { pr_err("could not dump acc address-value dump for corner=%d: rc=%d\n", i, rc); return rc; } } mem_acc_vreg->corner_acc_reg_config = corner_acc_reg_config; mem_acc_vreg->flags |= MEM_ACC_USE_ADDR_VAL_MAP; return rc; } static int mem_acc_override_reg_addr_val_init( struct mem_acc_regulator *mem_acc_vreg) { struct device_node *of_node = mem_acc_vreg->dev->of_node; struct corner_acc_reg_config *corner_acc_reg_config; struct acc_reg_value *override_reg_config_list; int i, tuple_count, tuple_match, len = 0, rc = 0; u32 list_size, override_max_reg_config_len; char prop_str[40]; struct property *prop; int num_corners = mem_acc_vreg->num_corners; if (!mem_acc_vreg->corner_acc_reg_config) return 0; if (mem_acc_vreg->override_map_count) { if (mem_acc_vreg->override_map_match == FUSE_MAP_NO_MATCH) return 0; tuple_count = mem_acc_vreg->override_map_count; tuple_match = mem_acc_vreg->override_map_match; } else { tuple_count = 1; tuple_match = 0; } corner_acc_reg_config = mem_acc_vreg->corner_acc_reg_config; for (i = 1; i <= num_corners; i++) { snprintf(prop_str, sizeof(prop_str), "qcom,override-corner%d-addr-val-map", i); prop = of_find_property(of_node, prop_str, &len); list_size = len / (tuple_count * sizeof(u32)); if (!prop) { pr_debug("%s property not specified\n", prop_str); continue; } if ((!list_size) || list_size < (num_corners * 2)) { pr_err("qcom,override-corner%d-addr-val-map property is missed or invalid length: len=%d\n", i, len); return -EINVAL; } override_max_reg_config_len = list_size / (num_corners * 2); override_reg_config_list = corner_acc_reg_config[i].reg_config_list; if (corner_acc_reg_config[i].max_reg_config_len != override_max_reg_config_len) { /* Free already allocate memory */ devm_kfree(mem_acc_vreg->dev, override_reg_config_list); /* Allocated memory for new requirement */ override_reg_config_list = devm_kcalloc(mem_acc_vreg->dev, override_max_reg_config_len * num_corners, sizeof(*override_reg_config_list), GFP_KERNEL); if (!override_reg_config_list) return -ENOMEM; corner_acc_reg_config[i].max_reg_config_len = override_max_reg_config_len; corner_acc_reg_config[i].reg_config_list = override_reg_config_list; } rc = mem_acc_get_reg_addr_val(of_node, prop_str, override_reg_config_list, tuple_match, list_size, mem_acc_vreg->num_acc_reg); if (rc) { pr_err("Failed to read %s property: rc=%d\n", prop_str, rc); return rc; } rc = mem_acc_reg_addr_val_dump(mem_acc_vreg, &corner_acc_reg_config[i], i); if (rc) { pr_err("could not dump acc address-value dump for corner=%d: rc=%d\n", i, rc); return rc; } } return rc; } #define MEM_TYPE_STRING_LEN 20 static int mem_acc_init(struct platform_device *pdev, struct mem_acc_regulator *mem_acc_vreg) { struct device_node *of_node = pdev->dev.of_node; struct resource *res; int len, rc, i, j; u32 fuse_sel[4]; Loading Loading @@ -719,15 +1134,25 @@ static int mem_acc_init(struct platform_device *pdev, } } rc = populate_acc_data(mem_acc_vreg, "qcom,corner-acc-map", &mem_acc_vreg->corner_acc_map, &mem_acc_vreg->num_corners); rc = mem_acc_get_reg_addr(mem_acc_vreg); if (rc) { pr_err("Unable to find 'qcom,corner-acc-map' rc=%d\n", rc); pr_err("Unable to get acc register addresses: rc=%d\n", rc); return rc; } pr_debug("num_corners = %d\n", mem_acc_vreg->num_corners); if (mem_acc_vreg->phys_reg_addr_list) { rc = mem_acc_reg_config_init(mem_acc_vreg); if (rc) { pr_err("acc register address-value map failed: rc=%d\n", rc); return rc; } } if (of_find_property(of_node, "qcom,corner-acc-map", NULL)) { rc = populate_acc_data(mem_acc_vreg, "qcom,corner-acc-map", &mem_acc_vreg->corner_acc_map, &mem_acc_vreg->num_corners); /* Check if at least one valid mem-acc config. is specified */ for (i = 0; i < MEMORY_MAX; i++) { Loading @@ -739,9 +1164,29 @@ static int mem_acc_init(struct platform_device *pdev, return -EINVAL; } mem_acc_vreg->flags |= MEM_ACC_USE_CORNER_ACC_MAP; } if ((mem_acc_vreg->flags & MEM_ACC_USE_CORNER_ACC_MAP) && (mem_acc_vreg->flags & MEM_ACC_USE_ADDR_VAL_MAP)) { pr_err("Invalid configuration, both qcom,corner-acc-map and qcom,cornerX-addr-val-map specified\n"); return -EINVAL; } pr_debug("num_corners = %d\n", mem_acc_vreg->num_corners); if (mem_acc_vreg->num_acc_en) mem_acc_en_init(mem_acc_vreg); if (mem_acc_vreg->phys_reg_addr_list) { rc = mem_acc_init_reg_config(mem_acc_vreg); if (rc) { pr_err("acc initial register configuration failed: rc=%d\n", rc); return rc; } } rc = mem_acc_sel_init(mem_acc_vreg); if (rc) { pr_err("Unable to intialize mem_acc_sel reg rc=%d\n", rc); Loading Loading @@ -792,6 +1237,13 @@ static int mem_acc_init(struct platform_device *pdev, return rc; } rc = mem_acc_override_reg_addr_val_init(mem_acc_vreg); if (rc) { pr_err("Unable to override reg_config_list init rc=%d\n", rc); return rc; } for (i = 0; i < MEMORY_MAX; i++) { rc = override_mem_acc_custom_data(pdev, mem_acc_vreg, i); Loading Loading
Documentation/devicetree/bindings/regulator/mem-acc-regulator.txt +117 −5 Original line number Diff line number Diff line Loading @@ -10,11 +10,6 @@ Required properties: should be 1 for SVS corner - regulator-max-microvolt: Maximum corner value as max constraint, which should be 4 for SUPER_TURBO or 3 for TURBO - qcom,corner-acc-map Array which maps the APC (application processor) corner value to the accelerator corner. [0] maps APC SVS corner (1) to accelerator SVS corner [1] maps APC NOMINAL corner (2) to accelerator NOMINAL corner [2] maps APC TURBO corner (3) to accelerator TURBO corner Optional properties: - reg: Register addresses for acc-sel-l1, acc-sel-l2 control, acc-en, Loading @@ -29,6 +24,11 @@ Optional properties: A given mem-acc-regulator driver must have "acc-sel-l1" or "acc-sel-l2" or "mem-acc-type*" reg-names property and related register address property. - qcom,corner-acc-map Array which maps the APC (application processor) corner value to the accelerator corner. The number of elements in this property defines the number of accelerator corners. Either qcom,corner-acc-map property or qcom,cornerX-reg-config properties should be specified. - qcom,acc-en-bit-pos Array which specifies bit positions in the 'acc-en' register. Setting these bits forces the the acclerator to use the corner value specified Loading Loading @@ -116,6 +116,49 @@ Optional properties: - qcom,mem-acc-type4: Same as qcom,mem-acc-type1 except for mem acc type4 register. - qcom,mem-acc-type5: Same as qcom,mem-acc-type1 except for mem acc type5 register. - qcom,mem-acc-type6: Same as qcom,mem-acc-type1 except for mem acc type6 register. - qcom,acc-reg-addr-list: Array of register addresses which need to be programmed during any corner switch. This property can be used when multi register configuration is needed during a corner switch. - qcom,acc-init-reg-config: Array of tuples specify the multi register configuration sequence need to be programmed one time during device boot. The format of each tuple as below: <register-address-index, value> Where register-address-index is used as an index in to qcom,acc-reg-addr-list property to get the required register address and the value is programmed in to the corresponding mapped register address. This property is required if qcom,acc-corner-addr-val-map property specified. - qcom,cornerX-reg-config: Array of tuples specify the multi register configuration sequence need to be programmed when switching from acc corner X to any other corner. The possible values for X are {1, N}, where N is the value defined in qcom,num-acc-corners. The format of each tuple as below: <register-address-index, value> Where register-address-index is used as an index in to qcom,acc-reg-addr-list property to get the required register address and the value is programmed in to the corresponding mapped register address. Same index can be used multiple times when the register is required to configure multiple times with different values in the sequence. The number of register configuration sequences should be equal to N, where N is the value specified in qcom,num-acc-corners property. Also, the number of tuples in each register configuration sequence should be same and must be equal to the maximum required register configurations in any sequence. The invalid register configuration can be specified as <(-1) (-1)>. This property can only be specified when qcom,acc-corner-addr-val-map property already defined. Either this property or qcom,corner-acc-map should be specified. - qcom,num-acc-corners: The number of acc corners supported. This property is required if qcom,cornerX-reg-config property specified. - qcom,boot-acc-corner: The acc corner used during device boot. This property is required if qcom,cornerX-reg-config property specified. - qcom,override-cornerX-reg-config: A grouping of register configuration sequence lists. Each list is same as the qcom,cornerX-reg-config property. The possible values for X are {1, N} where N is the value defined in qcom,num-acc-corners. This property is used to specify the different register configuration sequence lists and select one list among them based on the selected index in qcom,override-fuse-version-map property. The selected list overrides the existing register configuration sequence list specified in "qcom,cornerX-reg-config". If the "qcom,override-fuse-version-map" property is specified, then "qcom,override-cornerX-reg-config" must contain the same number of register configuration sequence lists as the number of tuples in "qcom,override-fuse-version-map". These register configuration sequence lists are then mapped one-to-one in the order specified. If the qcom,override-fuse-version-map property is not specified, then "qcom,override-cornerX-reg-config" must contain a single register configuration sequence list which is then applied unconditionally. This property can only be specified if qcom,cornerX-reg-config property is already defined. mem_acc_vreg_corner: regulator@fd4aa044 { compatible = "qcom,mem-acc-regulator"; Loading Loading @@ -151,4 +194,73 @@ mem_acc_vreg_corner: regulator@fd4aa044 { qcom,mem-acc-type2 = <0x02 0x02 0x00>; qcom,mem-acc-type3 = <0x02 0x02 0x00>; qcom,mem-acc-type4 = <0x02 0x02 0x00>; qcom,acc-reg-addr-list = <0x01942130 0x01942124 0x01942120>; qcom,acc-init-reg-config = <1 0x55> <2 0x02>; qcom,num-acc-corners = <3>; qcom,boot-acc-corner = <2>; qcom,corner1-reg-config = <(-1) (-1)>, <(-1) (-1)>, <(-1) (-1)>, /* 1 -> 1 */ < 1 0x155>, < 2 0x0>, < 3 0x155>, /* 1 -> 2 */ < 1 0x0>, < 2 0x155>, <(-1) (-1)>; /* 1 -> 3 */ qcom,corner2-reg-config = < 1 0x155>, <(-1) (-1)>, <(-1) (-1)>, /* 2 -> 1 */ <(-1) (-1)>, <(-1) (-1)>, <(-1) (-1)>, /* 2 -> 2 */ < 1 0x155>, < 2 0x0>, < 3 0x155>; /* 2 -> 3 */ qcom,corner3-reg-config = < 1 0x0>, < 2 0x155>, <(-1) (-1)>, /* 3 -> 1 */ < 1 0x155>, <(-1) (-1)>, <(-1) (-1)>, /* 3 -> 2 */ <(-1) (-1)>, <(-1) (-1)>, <(-1) (-1)>; /* 3 -> 3 */ qcom,override-corner1-reg-config = /* 1st fuse version tuple matched */ <(-1) (-1)>, <(-1) (-1)>, <(-1) (-1)>, /* 1 -> 1 */ < 1 0x155>, < 2 0x0>, < 3 0x155>, /* 1 -> 2 */ < 1 0x0>, < 2 0x155>, <(-1) (-1)>, /* 1 -> 3 */ /* 2nd fuse version tuple matched */ <(-1) (-1)>, <(-1) (-1)>, <(-1) (-1)>, /* 1 -> 1 */ < 1 0x155>, < 2 0x0>, < 3 0x155>, /* 1 -> 2 */ < 1 0x0>, < 2 0x155>, <(-1) (-1)>, /* 1 -> 3 */ /* 3rd fuse version tuple matched */ <(-1) (-1)>, <(-1) (-1)>, <(-1) (-1)>, /* 1 -> 1 */ < 1 0x155>, < 3 0x22>, < 3 0x155>, /* 1 -> 2 */ < 1 0x0>, < 2 0x155>, < 3 0x144>; /* 1 -> 3 */ qcom,override-corner2-reg-config = /* 1st fuse version tuple matched */ < 1 0x144>, < 1 0x11>, <(-1) (-1)>, /* 2 -> 1 */ <(-1) (-1)>, <(-1) (-1)>, <(-1) (-1)>, /* 2 -> 2 */ < 1 0x155>, < 2 0x0>, < 3 0x155>, /* 2 -> 3 */ /* 2nd fuse version tuple matched */ < 1 0x144>, < 2 0x133>, <(-1) (-1)>, /* 2 -> 1 */ <(-1) (-1)>, < 1 0x33>, <(-1) (-1)>, /* 2 -> 2 */ < 1 0x133>, < 2 0x0>, < 3 0x155>, /* 2 -> 3 */ /* 3rd fuse version tuple matched */ < 1 0x144>, < 1 0x11>, <(-1) (-1)>, /* 2 -> 1 */ <(-1) (-1)>, <(-1) (-1)>, <(-1) (-1)>, /* 2 -> 2 */ < 1 0x155>, < 2 0x22>, < 3 0x155>; /* 2 -> 3 */ qcom,override-corner3-reg-config = /* 1st fuse version tuple matched */ < 1 0x0>, < 2 0x155>, <(-1) (-1)>, /* 3 -> 1 */ < 1 0x155>, <(-1) (-1)>, <(-1) (-1)>, /* 3 -> 2 */ <(-1) (-1)>, <(-1) (-1)>, <(-1) (-1)>, /* 3 -> 3 */ /* 2nd fuse version tuple matched */ < 1 0x0>, < 2 0x155>, <(-1) (-1)>, /* 3 -> 1 */ < 1 0x155>, <(-1) (-1)>, <(-1) (-1)>, /* 3 -> 2 */ <(-1) (-1)>, <(-1) (-1)>, <(-1) (-1)>, /* 3 -> 3 */ /* 3rd fuse version tuple matched */ < 1 0x0>, < 2 0x155>, <(-1) (-1)>, /* 3 -> 1 */ < 1 0x155>, < 3 0x11>, <(-1) (-1)>, /* 3 -> 2 */ <(-1) (-1)>, <(-1) (-1)>, <(-1) (-1)>; /* 3 -> 3 */ };
drivers/regulator/mem-acc-regulator.c +472 −20 Original line number Diff line number Diff line Loading @@ -32,8 +32,15 @@ #define BYTES_PER_FUSE_ROW 8 /* mem-acc config flags */ enum { MEM_ACC_USE_CORNER_ACC_MAP = BIT(0), MEM_ACC_USE_ADDR_VAL_MAP = BIT(1), }; #define FUSE_MAP_NO_MATCH (-1) #define FUSE_PARAM_MATCH_ANY (-1) #define PARAM_MATCH_ANY (-1) enum { MEMORY_L1, Loading @@ -43,6 +50,22 @@ enum { #define MEM_ACC_TYPE_MAX 6 /** * struct acc_reg_value - Acc register configuration structure * @addr_index: An index in to phys_reg_addr_list and remap_reg_addr_list * to get the ACC register physical address and remapped address. * @reg_val: Value to program in to the register mapped by addr_index. */ struct acc_reg_value { u32 addr_index; u32 reg_val; }; struct corner_acc_reg_config { struct acc_reg_value *reg_config_list; int max_reg_config_len; }; struct mem_acc_regulator { struct device *dev; struct regulator_desc rdesc; Loading Loading @@ -80,6 +103,11 @@ struct mem_acc_regulator { /* eFuse parameters */ phys_addr_t efuse_addr; void __iomem *efuse_base; u32 num_acc_reg; u32 *phys_reg_addr_list; void __iomem **remap_reg_addr_list; struct corner_acc_reg_config *corner_acc_reg_config; }; static DEFINE_MUTEX(mem_acc_memory_mutex); Loading Loading @@ -205,6 +233,41 @@ static void update_acc_sel(struct mem_acc_regulator *mem_acc_vreg, int corner) __update_acc_type(mem_acc_vreg, corner); } static void update_acc_reg(struct mem_acc_regulator *mem_acc_vreg, int corner) { struct corner_acc_reg_config *corner_acc_reg_config; struct acc_reg_value *reg_config_list; int i, index; u32 addr_index, reg_val; corner_acc_reg_config = &mem_acc_vreg->corner_acc_reg_config[mem_acc_vreg->corner]; reg_config_list = corner_acc_reg_config->reg_config_list; for (i = 0; i < corner_acc_reg_config->max_reg_config_len; i++) { /* * Use (corner - 1) in the below equation as * the reg_config_list[] stores the values starting from * index '0' where as the minimum corner value allowed * in regulator framework is '1'. */ index = (corner - 1) * corner_acc_reg_config->max_reg_config_len + i; addr_index = reg_config_list[index].addr_index; reg_val = reg_config_list[index].reg_val; if (addr_index == PARAM_MATCH_ANY) break; writel_relaxed(reg_val, mem_acc_vreg->remap_reg_addr_list[addr_index]); /* make sure write complete */ mb(); pr_debug("corner=%d register:0x%x value:0x%x\n", corner, mem_acc_vreg->phys_reg_addr_list[addr_index], reg_val); } } static int mem_acc_regulator_set_voltage(struct regulator_dev *rdev, int corner, int corner_max, unsigned *selector) { Loading @@ -224,6 +287,10 @@ static int mem_acc_regulator_set_voltage(struct regulator_dev *rdev, /* go up or down one level at a time */ mutex_lock(&mem_acc_memory_mutex); if (mem_acc_vreg->flags & MEM_ACC_USE_ADDR_VAL_MAP) { update_acc_reg(mem_acc_vreg, corner); } else if (mem_acc_vreg->flags & MEM_ACC_USE_CORNER_ACC_MAP) { if (corner > mem_acc_vreg->corner) { for (i = mem_acc_vreg->corner + 1; i <= corner; i++) { pr_debug("UP: to corner %d\n", i); Loading @@ -235,6 +302,8 @@ static int mem_acc_regulator_set_voltage(struct regulator_dev *rdev, update_acc_sel(mem_acc_vreg, i); } } } mutex_unlock(&mem_acc_memory_mutex); pr_debug("new voltage corner set %d\n", corner); Loading Loading @@ -638,10 +707,356 @@ done: return rc; } #define MAX_CHARS_PER_INT 20 static int mem_acc_reg_addr_val_dump(struct mem_acc_regulator *mem_acc_vreg, struct corner_acc_reg_config *corner_acc_reg_config, u32 corner) { int i, k, index, pos = 0; u32 addr_index; size_t buflen; char *buf; struct acc_reg_value *reg_config_list = corner_acc_reg_config->reg_config_list; int max_reg_config_len = corner_acc_reg_config->max_reg_config_len; int num_corners = mem_acc_vreg->num_corners; /* * Log register and value mapping since they are useful for * baseline MEM ACC logging. */ buflen = max_reg_config_len * (MAX_CHARS_PER_INT + 6) * sizeof(*buf); buf = kzalloc(buflen, GFP_KERNEL); if (buf == NULL) { pr_err("Could not allocate memory for acc register and value logging\n"); return -ENOMEM; } for (i = 0; i < num_corners; i++) { if (corner == i + 1) continue; pr_debug("Corner: %d --> %d:\n", corner, i + 1); pos = 0; for (k = 0; k < max_reg_config_len; k++) { index = i * max_reg_config_len + k; addr_index = reg_config_list[index].addr_index; if (addr_index == PARAM_MATCH_ANY) break; pos += scnprintf(buf + pos, buflen - pos, "<0x%x 0x%x> ", mem_acc_vreg->phys_reg_addr_list[addr_index], reg_config_list[index].reg_val); } buf[pos] = '\0'; pr_debug("%s\n", buf); } return 0; } static int mem_acc_get_reg_addr_val(struct device_node *of_node, const char *prop_str, struct acc_reg_value *reg_config_list, int list_offset, int list_size, u32 max_reg_index) { int i, index, rc = 0; for (i = 0; i < list_size / 2; i++) { index = (list_offset * list_size) + i * 2; rc = of_property_read_u32_index(of_node, prop_str, index, ®_config_list[i].addr_index); rc |= of_property_read_u32_index(of_node, prop_str, index + 1, ®_config_list[i].reg_val); if (rc) { pr_err("could not read %s at tuple %u: rc=%d\n", prop_str, index, rc); return rc; } if (reg_config_list[i].addr_index == PARAM_MATCH_ANY) continue; if ((!reg_config_list[i].addr_index) || reg_config_list[i].addr_index > max_reg_index) { pr_err("Invalid register index %u in %s at tuple %u\n", reg_config_list[i].addr_index, prop_str, index); return -EINVAL; } } return rc; } static int mem_acc_init_reg_config(struct mem_acc_regulator *mem_acc_vreg) { struct device_node *of_node = mem_acc_vreg->dev->of_node; int i, size, len = 0, rc = 0; u32 addr_index, reg_val, index; char *prop_str = "qcom,acc-init-reg-config"; if (!of_find_property(of_node, prop_str, &len)) { /* Initial acc register configuration not specified */ return rc; } size = len / sizeof(u32); if ((!size) || (size % 2)) { pr_err("%s specified with invalid length: %d\n", prop_str, size); return -EINVAL; } for (i = 0; i < size / 2; i++) { index = i * 2; rc = of_property_read_u32_index(of_node, prop_str, index, &addr_index); rc |= of_property_read_u32_index(of_node, prop_str, index + 1, ®_val); if (rc) { pr_err("could not read %s at tuple %u: rc=%d\n", prop_str, index, rc); return rc; } if ((!addr_index) || addr_index > mem_acc_vreg->num_acc_reg) { pr_err("Invalid register index %u in %s at tuple %u\n", addr_index, prop_str, index); return -EINVAL; } writel_relaxed(reg_val, mem_acc_vreg->remap_reg_addr_list[addr_index]); /* make sure write complete */ mb(); pr_debug("acc initial config: register:0x%x value:0x%x\n", mem_acc_vreg->phys_reg_addr_list[addr_index], reg_val); } return rc; } static int mem_acc_get_reg_addr(struct mem_acc_regulator *mem_acc_vreg) { struct device_node *of_node = mem_acc_vreg->dev->of_node; void __iomem **remap_reg_addr_list; u32 *phys_reg_addr_list; int i, num_acc_reg, len = 0, rc = 0; if (!of_find_property(of_node, "qcom,acc-reg-addr-list", &len)) { /* acc register address list not specified */ return rc; } num_acc_reg = len / sizeof(u32); if (!num_acc_reg) { pr_err("qcom,acc-reg-addr-list has invalid len = %d\n", len); return -EINVAL; } phys_reg_addr_list = devm_kcalloc(mem_acc_vreg->dev, num_acc_reg + 1, sizeof(*phys_reg_addr_list), GFP_KERNEL); if (!phys_reg_addr_list) return -ENOMEM; remap_reg_addr_list = devm_kcalloc(mem_acc_vreg->dev, num_acc_reg + 1, sizeof(*remap_reg_addr_list), GFP_KERNEL); if (!remap_reg_addr_list) return -ENOMEM; rc = of_property_read_u32_array(of_node, "qcom,acc-reg-addr-list", &phys_reg_addr_list[1], num_acc_reg); if (rc) { pr_err("Read- qcom,acc-reg-addr-list failed: rc=%d\n", rc); return rc; } for (i = 1; i <= num_acc_reg; i++) { remap_reg_addr_list[i] = devm_ioremap(mem_acc_vreg->dev, phys_reg_addr_list[i], 0x4); if (!remap_reg_addr_list[i]) { pr_err("Unable to map register address 0x%x\n", phys_reg_addr_list[i]); return -EINVAL; } } mem_acc_vreg->num_acc_reg = num_acc_reg; mem_acc_vreg->phys_reg_addr_list = phys_reg_addr_list; mem_acc_vreg->remap_reg_addr_list = remap_reg_addr_list; return rc; } static int mem_acc_reg_config_init(struct mem_acc_regulator *mem_acc_vreg) { struct device_node *of_node = mem_acc_vreg->dev->of_node; struct acc_reg_value *reg_config_list; int len, size, rc, i, num_corners; struct property *prop; char prop_str[30]; struct corner_acc_reg_config *corner_acc_reg_config; rc = of_property_read_u32(of_node, "qcom,num-acc-corners", &num_corners); if (rc) { pr_err("could not read qcom,num-acc-corners: rc=%d\n", rc); return rc; } mem_acc_vreg->num_corners = num_corners; rc = of_property_read_u32(of_node, "qcom,boot-acc-corner", &mem_acc_vreg->corner); if (rc) { pr_err("could not read qcom,boot-acc-corner: rc=%d\n", rc); return rc; } pr_debug("boot acc corner = %d\n", mem_acc_vreg->corner); corner_acc_reg_config = devm_kcalloc(mem_acc_vreg->dev, num_corners + 1, sizeof(*corner_acc_reg_config), GFP_KERNEL); if (!corner_acc_reg_config) return -ENOMEM; for (i = 1; i <= num_corners; i++) { snprintf(prop_str, sizeof(prop_str), "qcom,corner%d-reg-config", i); prop = of_find_property(of_node, prop_str, &len); size = len / sizeof(u32); if ((!prop) || (!size) || size < (num_corners * 2)) { pr_err("%s property is missed or invalid length: len=%d\n", prop_str, len); return -EINVAL; } reg_config_list = devm_kcalloc(mem_acc_vreg->dev, size / 2, sizeof(*reg_config_list), GFP_KERNEL); if (!reg_config_list) return -ENOMEM; rc = mem_acc_get_reg_addr_val(of_node, prop_str, reg_config_list, 0, size, mem_acc_vreg->num_acc_reg); if (rc) { pr_err("Failed to read %s property: rc=%d\n", prop_str, rc); return rc; } corner_acc_reg_config[i].max_reg_config_len = size / (num_corners * 2); corner_acc_reg_config[i].reg_config_list = reg_config_list; rc = mem_acc_reg_addr_val_dump(mem_acc_vreg, &corner_acc_reg_config[i], i); if (rc) { pr_err("could not dump acc address-value dump for corner=%d: rc=%d\n", i, rc); return rc; } } mem_acc_vreg->corner_acc_reg_config = corner_acc_reg_config; mem_acc_vreg->flags |= MEM_ACC_USE_ADDR_VAL_MAP; return rc; } static int mem_acc_override_reg_addr_val_init( struct mem_acc_regulator *mem_acc_vreg) { struct device_node *of_node = mem_acc_vreg->dev->of_node; struct corner_acc_reg_config *corner_acc_reg_config; struct acc_reg_value *override_reg_config_list; int i, tuple_count, tuple_match, len = 0, rc = 0; u32 list_size, override_max_reg_config_len; char prop_str[40]; struct property *prop; int num_corners = mem_acc_vreg->num_corners; if (!mem_acc_vreg->corner_acc_reg_config) return 0; if (mem_acc_vreg->override_map_count) { if (mem_acc_vreg->override_map_match == FUSE_MAP_NO_MATCH) return 0; tuple_count = mem_acc_vreg->override_map_count; tuple_match = mem_acc_vreg->override_map_match; } else { tuple_count = 1; tuple_match = 0; } corner_acc_reg_config = mem_acc_vreg->corner_acc_reg_config; for (i = 1; i <= num_corners; i++) { snprintf(prop_str, sizeof(prop_str), "qcom,override-corner%d-addr-val-map", i); prop = of_find_property(of_node, prop_str, &len); list_size = len / (tuple_count * sizeof(u32)); if (!prop) { pr_debug("%s property not specified\n", prop_str); continue; } if ((!list_size) || list_size < (num_corners * 2)) { pr_err("qcom,override-corner%d-addr-val-map property is missed or invalid length: len=%d\n", i, len); return -EINVAL; } override_max_reg_config_len = list_size / (num_corners * 2); override_reg_config_list = corner_acc_reg_config[i].reg_config_list; if (corner_acc_reg_config[i].max_reg_config_len != override_max_reg_config_len) { /* Free already allocate memory */ devm_kfree(mem_acc_vreg->dev, override_reg_config_list); /* Allocated memory for new requirement */ override_reg_config_list = devm_kcalloc(mem_acc_vreg->dev, override_max_reg_config_len * num_corners, sizeof(*override_reg_config_list), GFP_KERNEL); if (!override_reg_config_list) return -ENOMEM; corner_acc_reg_config[i].max_reg_config_len = override_max_reg_config_len; corner_acc_reg_config[i].reg_config_list = override_reg_config_list; } rc = mem_acc_get_reg_addr_val(of_node, prop_str, override_reg_config_list, tuple_match, list_size, mem_acc_vreg->num_acc_reg); if (rc) { pr_err("Failed to read %s property: rc=%d\n", prop_str, rc); return rc; } rc = mem_acc_reg_addr_val_dump(mem_acc_vreg, &corner_acc_reg_config[i], i); if (rc) { pr_err("could not dump acc address-value dump for corner=%d: rc=%d\n", i, rc); return rc; } } return rc; } #define MEM_TYPE_STRING_LEN 20 static int mem_acc_init(struct platform_device *pdev, struct mem_acc_regulator *mem_acc_vreg) { struct device_node *of_node = pdev->dev.of_node; struct resource *res; int len, rc, i, j; u32 fuse_sel[4]; Loading Loading @@ -719,15 +1134,25 @@ static int mem_acc_init(struct platform_device *pdev, } } rc = populate_acc_data(mem_acc_vreg, "qcom,corner-acc-map", &mem_acc_vreg->corner_acc_map, &mem_acc_vreg->num_corners); rc = mem_acc_get_reg_addr(mem_acc_vreg); if (rc) { pr_err("Unable to find 'qcom,corner-acc-map' rc=%d\n", rc); pr_err("Unable to get acc register addresses: rc=%d\n", rc); return rc; } pr_debug("num_corners = %d\n", mem_acc_vreg->num_corners); if (mem_acc_vreg->phys_reg_addr_list) { rc = mem_acc_reg_config_init(mem_acc_vreg); if (rc) { pr_err("acc register address-value map failed: rc=%d\n", rc); return rc; } } if (of_find_property(of_node, "qcom,corner-acc-map", NULL)) { rc = populate_acc_data(mem_acc_vreg, "qcom,corner-acc-map", &mem_acc_vreg->corner_acc_map, &mem_acc_vreg->num_corners); /* Check if at least one valid mem-acc config. is specified */ for (i = 0; i < MEMORY_MAX; i++) { Loading @@ -739,9 +1164,29 @@ static int mem_acc_init(struct platform_device *pdev, return -EINVAL; } mem_acc_vreg->flags |= MEM_ACC_USE_CORNER_ACC_MAP; } if ((mem_acc_vreg->flags & MEM_ACC_USE_CORNER_ACC_MAP) && (mem_acc_vreg->flags & MEM_ACC_USE_ADDR_VAL_MAP)) { pr_err("Invalid configuration, both qcom,corner-acc-map and qcom,cornerX-addr-val-map specified\n"); return -EINVAL; } pr_debug("num_corners = %d\n", mem_acc_vreg->num_corners); if (mem_acc_vreg->num_acc_en) mem_acc_en_init(mem_acc_vreg); if (mem_acc_vreg->phys_reg_addr_list) { rc = mem_acc_init_reg_config(mem_acc_vreg); if (rc) { pr_err("acc initial register configuration failed: rc=%d\n", rc); return rc; } } rc = mem_acc_sel_init(mem_acc_vreg); if (rc) { pr_err("Unable to intialize mem_acc_sel reg rc=%d\n", rc); Loading Loading @@ -792,6 +1237,13 @@ static int mem_acc_init(struct platform_device *pdev, return rc; } rc = mem_acc_override_reg_addr_val_init(mem_acc_vreg); if (rc) { pr_err("Unable to override reg_config_list init rc=%d\n", rc); return rc; } for (i = 0; i < MEMORY_MAX; i++) { rc = override_mem_acc_custom_data(pdev, mem_acc_vreg, i); Loading