Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4c4fa4b9 authored by Tirupathi Reddy's avatar Tirupathi Reddy
Browse files

regulator: mem-acc-regulator: Skip L1 memory configuration based on fuse



Add a fuse based logic to skip the L1 memory ACC configuration. This is
required on some parts.

CRs-Fixed: 680261
Change-Id: Id07d65b8482f63e114ec6ae3b3fc3b961235d846
Signed-off-by: default avatarTirupathi Reddy <tirupath@codeaurora.org>
parent d81481d4
Loading
Loading
Loading
Loading
+22 −4
Original line number Original line Diff line number Diff line
@@ -17,8 +17,10 @@ Required properties:
				[2] maps APC TURBO corner (3) to accelerator TURBO corner
				[2] maps APC TURBO corner (3) to accelerator TURBO corner


Optional properties:
Optional properties:
- reg:				Register addresses for acc-en and acc-sel-l1 acc-sel-l2 control.
- reg:				Register addresses for acc-en and acc-sel-l1
- reg-names:			Register names. Must be "acc-sel-l1", "acc-sel-l2", "acc-en".
				acc-sel-l2 control and MEM ACC eFuse address.
- reg-names:			Register names. Must be "acc-sel-l1",
				"acc-sel-l2", "acc-en", "efuse_addr".
				A given mem-acc-regulator driver must have "acc-sel-l1" or
				A given mem-acc-regulator driver must have "acc-sel-l1" or
				"acc-sel-l2" reg-names property and related register address
				"acc-sel-l2" reg-names property and related register address
				property.
				property.
@@ -36,11 +38,26 @@ Optional properties:
				is the LSB of a 2-bit value. This 2-bit value
				is the LSB of a 2-bit value. This 2-bit value
				specifies the corner value used by the
				specifies the corner value used by the
				accelerator for L2 cache.
				accelerator for L2 cache.
- qcom,l1-config-skip-fuse-sel:	Array of 5 elements to indicate where to read the bits, what value to
				compare with in order to decide whether to
				skip configuring the L1 accelerator or not while changing the APC corner
				and method to read fuse row, using SCM to read or read register directly.
				The 5 elements with index [0..4] are:
				  [0] => the fuse row number of the selector
				  [1] => LSB bit position of the bits
				  [2] => number of bits
				  [3] => the value to select skip L1 config logic
				  [4] => fuse reading method, 0 for direct reading or 1 for SCM reading
				When the value of the fuse bits specified by first 3 elements equals to
				the value in 4th element, L1 accelerator
				configuration logic is skipped. Otherwise, the original configuration sent
				from corner map should be applied. If the 5th element is 0, read the fuse row
				from register directly. Otherwise, read it through SCM.


mem_acc_vreg_corner: regulator@fd4aa044 {
mem_acc_vreg_corner: regulator@fd4aa044 {
	compatible = "qcom,mem-acc-regulator";
	compatible = "qcom,mem-acc-regulator";
	reg = <0xfd4aa048 0x1>, <0xfd4aa044 0x1>, <0xfd4af000 0x1>;
	reg = <0xfd4aa048 0x1>, <0xfd4aa044 0x1>, <0xfd4af000 0x1>, <0x58000 0x1000>;
	reg-names = "acc-en", "acc-sel-l1" , "acc-sel-l2";
	reg-names = "acc-en", "acc-sel-l1" , "acc-sel-l2", "efuse_addr";
	regulator-name = "mem_acc_corner";
	regulator-name = "mem_acc_corner";
	regulator-min-microvolt = <1>;
	regulator-min-microvolt = <1>;
	regulator-max-microvolt = <3>;
	regulator-max-microvolt = <3>;
@@ -49,4 +66,5 @@ mem_acc_vreg_corner: regulator@fd4aa044 {
	qcom,acc-sel-l1-bit-pos = <0>;
	qcom,acc-sel-l1-bit-pos = <0>;
	qcom,acc-sel-l2-bit-pos = <0>;
	qcom,acc-sel-l2-bit-pos = <0>;
	qcom,corner-acc-map = <0 1 3>;
	qcom,corner-acc-map = <0 1 3>;
	qcom,l1-config-skip-fuse-sel = <0 52 1 1 0>;
};
};
+136 −0
Original line number Original line Diff line number Diff line
@@ -23,9 +23,15 @@
#include <linux/regulator/driver.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/of_regulator.h>
#include <soc/qcom/scm.h>


#define MEM_ACC_SEL_MASK	0x3
#define MEM_ACC_SEL_MASK	0x3


#define BYTES_PER_FUSE_ROW	8

/* mem-acc config flags */
#define MEM_ACC_SKIP_L1_CONFIG	BIT(0)

enum {
enum {
	MEMORY_L1,
	MEMORY_L1,
	MEMORY_L2,
	MEMORY_L2,
@@ -53,8 +59,74 @@ struct mem_acc_regulator {
	void __iomem		*acc_en_base;
	void __iomem		*acc_en_base;
	phys_addr_t		acc_sel_addr[MEMORY_MAX];
	phys_addr_t		acc_sel_addr[MEMORY_MAX];
	phys_addr_t		acc_en_addr;
	phys_addr_t		acc_en_addr;
	u32			flags;

	/* eFuse parameters */
	phys_addr_t		efuse_addr;
	void __iomem		*efuse_base;
};
};


static u64 mem_acc_read_efuse_row(struct mem_acc_regulator *mem_acc_vreg,
					u32 row_num, bool use_tz_api)
{
	int rc;
	u64 efuse_bits;
	struct mem_acc_read_req {
		u32 row_address;
		int addr_type;
	} req;

	struct mem_acc_read_rsp {
		u32 row_data[2];
		u32 status;
	} rsp;

	if (!use_tz_api) {
		efuse_bits = readq_relaxed(mem_acc_vreg->efuse_base
			+ row_num * BYTES_PER_FUSE_ROW);
		return efuse_bits;
	}

	req.row_address = mem_acc_vreg->efuse_addr +
					row_num * BYTES_PER_FUSE_ROW;
	req.addr_type = 0;
	efuse_bits = 0;

	rc = scm_call(SCM_SVC_FUSE, SCM_FUSE_READ,
			&req, sizeof(req), &rsp, sizeof(rsp));

	if (rc) {
		pr_err("read row %d failed, err code = %d", row_num, rc);
	} else {
		efuse_bits = ((u64)(rsp.row_data[1]) << 32) +
				(u64)rsp.row_data[0];
	}

	return efuse_bits;
}

static int mem_acc_fuse_is_setting_expected(
		struct mem_acc_regulator *mem_acc_vreg, u32 sel_array[5])
{
	u64 fuse_bits;
	u32 ret;

	fuse_bits = mem_acc_read_efuse_row(mem_acc_vreg, sel_array[0],
							sel_array[4]);
	ret = (fuse_bits >> sel_array[1]) & ((1 << sel_array[2]) - 1);
	if (ret == sel_array[3])
		ret = 1;
	else
		ret = 0;

	pr_info("[row:%d] = 0x%llx @%d:%d == %d ?: %s\n",
			sel_array[0], fuse_bits,
			sel_array[1], sel_array[2],
			sel_array[3],
			(ret == 1) ? "yes" : "no");
	return ret;
}

static inline u32 apc_to_acc_corner(struct mem_acc_regulator *mem_acc_vreg,
static inline u32 apc_to_acc_corner(struct mem_acc_regulator *mem_acc_vreg,
								int corner)
								int corner)
{
{
@@ -70,6 +142,14 @@ static void __update_acc_sel(struct mem_acc_regulator *mem_acc_vreg,
{
{
	u32 acc_data, i, bit, acc_corner;
	u32 acc_data, i, bit, acc_corner;


	/*
	 * Do not configure the L1 ACC corner if the the corresponding flag is
	 * set.
	 */
	if ((mem_type == MEMORY_L1)
			&& (mem_acc_vreg->flags & MEM_ACC_SKIP_L1_CONFIG))
		return;

	acc_data = mem_acc_vreg->acc_sel_reg[mem_type];
	acc_data = mem_acc_vreg->acc_sel_reg[mem_type];
	for (i = 0; i < mem_acc_vreg->num_acc_sel[mem_type]; i++) {
	for (i = 0; i < mem_acc_vreg->num_acc_sel[mem_type]; i++) {
		bit = mem_acc_vreg->acc_sel_bit_pos[mem_type][i];
		bit = mem_acc_vreg->acc_sel_bit_pos[mem_type][i];
@@ -273,6 +353,56 @@ static int mem_acc_sel_setup(struct mem_acc_regulator *mem_acc_vreg,
	return rc;
	return rc;
}
}


static int mem_acc_efuse_init(struct platform_device *pdev,
				 struct mem_acc_regulator *mem_acc_vreg)
{
	struct resource *res;
	int len, rc = 0;
	u32 l1_config_skip_fuse_sel[5];

	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "efuse_addr");
	if (!res || !res->start) {
		mem_acc_vreg->efuse_base = NULL;
		pr_debug("'efuse_addr' resource missing or not used.\n");
		return 0;
	}

	mem_acc_vreg->efuse_addr = res->start;
	len = res->end - res->start + 1;

	pr_info("efuse_addr = %pa (len=0x%x)\n", &res->start, len);

	mem_acc_vreg->efuse_base = ioremap(mem_acc_vreg->efuse_addr, len);
	if (!mem_acc_vreg->efuse_base) {
		pr_err("Unable to map efuse_addr %pa\n",
				&mem_acc_vreg->efuse_addr);
		return -EINVAL;
	}

	if (of_find_property(mem_acc_vreg->dev->of_node,
				"qcom,l1-config-skip-fuse-sel", NULL)) {
		rc = of_property_read_u32_array(mem_acc_vreg->dev->of_node,
					"qcom,l1-config-skip-fuse-sel",
					l1_config_skip_fuse_sel, 5);
		if (rc < 0) {
			pr_err("Read failed - qcom,l1-config-skip-fuse-sel rc=%d\n",
					rc);
			goto err_out;
		}

		if (mem_acc_fuse_is_setting_expected(mem_acc_vreg,
						l1_config_skip_fuse_sel)) {
			mem_acc_vreg->flags |= MEM_ACC_SKIP_L1_CONFIG;
			pr_debug("Skip L1 configuration enabled\n");
		}
	}


err_out:
	iounmap(mem_acc_vreg->efuse_base);
	return rc;
}

static int mem_acc_init(struct platform_device *pdev,
static int mem_acc_init(struct platform_device *pdev,
		struct mem_acc_regulator *mem_acc_vreg)
		struct mem_acc_regulator *mem_acc_vreg)
{
{
@@ -305,6 +435,12 @@ static int mem_acc_init(struct platform_device *pdev,
		}
		}
	}
	}


	rc = mem_acc_efuse_init(pdev, mem_acc_vreg);
	if (rc) {
		pr_err("Wrong eFuse address specified: rc=%d\n", rc);
		return rc;
	}

	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "acc-sel-l1");
	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "acc-sel-l1");
	if (!res || !res->start) {
	if (!res || !res->start) {
		pr_debug("'acc-sel-l1' resource missing or not used.\n");
		pr_debug("'acc-sel-l1' resource missing or not used.\n");