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

Commit 211063c2 authored by Tirupathi Reddy's avatar Tirupathi Reddy
Browse files

regulator: cpr3: Add panic handler to dump register contents



Add panic handler to dump a few critical CPR registers when device
crashes due to kernel panic errors.

CRs-Fixed: 1004533
Change-Id: Id01b4f959c134af48c509ade61c7ec46401b4e70
Signed-off-by: default avatarTirupathi Reddy <tirupath@codeaurora.org>
parent a3df2e4c
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -183,6 +183,19 @@ Platform independent properties:
		    This is the voltage that vdd-supply must be set to when
		    performing an aging measurement.

- qcom,cpr-panic-reg-addr-list
	Usage:      optional
	Value type: <prop-encoded-array>
	Definition: Array of register addresses to be dumped when device resets.

- qcom,cpr-panic-reg-name-list
	Usage:      optional, though only meaningful if
		    qcom,cpr-panic-reg-addr-list is specified
	Value type: <prop-encoded-array>
	Definition: Address names. Must be specified in the same order
		    as the corresponding addresses are specified in
		    the qcom,cpr-panic-reg-addr-list property.

=================================================
Second Level Nodes - CPR Threads for a Controller
=================================================
+7 −0
Original line number Diff line number Diff line
@@ -407,6 +407,13 @@ apc_cpr: cpr4-ctrl@b018000 {
	qcom,cpr-step-quot-fixed = <16>;
	qcom,cpr-voltage-settling-time = <1600>;

	qcom,cpr-panic-reg-addr-list =
			<0xb1d2c18 0xb1d2900 0x0b1112b0 0xb018798>;
	qcom,cpr-panic-reg-name-list =
			"CCI_SAW4_PMIC_STS", "CCI_SAW4_VCTL",
			"APCS_ALIAS0_APM_CTLER_STATUS",
			"APCS0_CPR_CORE_ADJ_MODE_REG";

	thread@0 {
		qcom,cpr-thread-id = <0>;
		qcom,cpr-consecutive-up = <1>;
+47 −0
Original line number Diff line number Diff line
@@ -5817,6 +5817,42 @@ static int cpr3_regulator_validate_controller(struct cpr3_controller *ctrl)
	return 0;
}

/**
 * cpr3_panic_callback() - panic notification callback function. This function
 *		is invoked when a kernel panic occurs.
 * @nfb:	Notifier block pointer of CPR3 controller
 * @event:	Value passed unmodified to notifier function
 * @data:	Pointer passed unmodified to notifier function
 *
 * Return: NOTIFY_OK
 */
static int cpr3_panic_callback(struct notifier_block *nfb,
			unsigned long event, void *data)
{
	struct cpr3_controller *ctrl = container_of(nfb,
				struct cpr3_controller, panic_notifier);
	struct cpr3_panic_regs_info *regs_info = ctrl->panic_regs_info;
	struct cpr3_reg_info *reg;
	void __iomem *virt_addr;
	int i = 0;

	for (i = 0; i < regs_info->reg_count; i++) {
		reg = &(regs_info->regs[i]);
		virt_addr = ioremap(reg->addr, 0x4);
		reg->value = readl_relaxed(virt_addr);
		iounmap(virt_addr);
		pr_err("%s[0x%08x] = 0x%08x\n", reg->name, reg->addr,
			reg->value);
	}
	/*
	 * Barrier to ensure that the information has been updated in the
	 * structure.
	 */
	mb();

	return NOTIFY_OK;
}

/**
 * cpr3_regulator_register() - register the regulators for a CPR3 controller and
 *		perform CPR hardware initialization
@@ -5968,6 +6004,13 @@ int cpr3_regulator_register(struct platform_device *pdev,
	list_add(&ctrl->list, &cpr3_controller_list);
	mutex_unlock(&cpr3_controller_list_mutex);

	if (ctrl->panic_regs_info) {
		/* Register panic notification call back */
		ctrl->panic_notifier.notifier_call = cpr3_panic_callback;
		atomic_notifier_chain_register(&panic_notifier_list,
			&ctrl->panic_notifier);
	}

	return 0;

free_regulators:
@@ -6021,5 +6064,9 @@ int cpr3_regulator_unregister(struct cpr3_controller *ctrl)
		for (j = 0; j < ctrl->thread[i].vreg_count; j++)
			regulator_unregister(ctrl->thread[i].vreg[j].rdev);

	if (ctrl->panic_notifier.notifier_call)
		atomic_notifier_chain_unregister(&panic_notifier_list,
			&ctrl->panic_notifier);

	return 0;
}
+34 −0
Original line number Diff line number Diff line
@@ -493,6 +493,35 @@ struct cpr3_aging_sensor_info {
	u32			bypass_mask[CPR3_MAX_SENSOR_COUNT / 32];
};

/**
 * struct cpr3_reg_info - Register information data structure
 * @name:	Register name
 * @addr:	Register physical address
 * @value:	Register content
 *
 * This data structure is used to dump some critical register contents
 * when the device crashes due to a kernel panic.
 */
struct cpr3_reg_info {
	const char	*name;
	u32		addr;
	u32		value;
};

/**
 * struct cpr3_panic_regs_info - Data structure to dump critical register
 *		contents.
 * @reg_count:		Number of elements in the regs array
 * @regs:		Array of critical registers information
 *
 * This data structure is used to dump critical register contents when
 * the device crashes due to a kernel panic.
 */
struct cpr3_panic_regs_info {
	int			reg_count;
	struct cpr3_reg_info	*regs;
};

/**
 * struct cpr3_controller - CPR3 controller data structure
 * @dev:		Device pointer for the CPR3 controller device
@@ -662,6 +691,9 @@ struct cpr3_aging_sensor_info {
 *			VDD supply voltage to settle after being increased or
 *			decreased by step_volt microvolts which is used when
 *			SDELTA voltage margin adjustments are applied.
 * @panic_regs_info:	Array of panic registers information which provides the
 *			list of registers to dump when the device crashes.
 * @panic_notifier:	Notifier block registered to global panic notifier list.
 *
 * This structure contains both configuration and runtime state data.  The
 * elements cpr_allowed_sw, use_hw_closed_loop, aggr_corner, cpr_enabled,
@@ -755,6 +787,8 @@ struct cpr3_controller {
	u32			temp_sensor_id_start;
	u32			temp_sensor_id_end;
	u32			voltage_settling_time;
	struct cpr3_panic_regs_info *panic_regs_info;
	struct notifier_block	panic_notifier;
};

/* Used for rounding voltages to the closest physically available set point. */
+70 −0
Original line number Diff line number Diff line
@@ -1020,6 +1020,74 @@ static int cpr3_parse_irq_affinity(struct cpr3_controller *ctrl)
	return 0;
}

static int cpr3_panic_notifier_init(struct cpr3_controller *ctrl)
{
	struct device_node *node = ctrl->dev->of_node;
	struct cpr3_panic_regs_info *panic_regs_info;
	struct cpr3_reg_info *regs;
	int i, reg_count, len, rc = 0;

	if (!of_find_property(node, "qcom,cpr-panic-reg-addr-list", &len)) {
		/* panic register address list not specified */
		return rc;
	}

	reg_count = len / sizeof(u32);
	if (!reg_count) {
		cpr3_err(ctrl, "qcom,cpr-panic-reg-addr-list has invalid len = %d\n",
			len);
		return -EINVAL;
	}

	if (!of_find_property(node, "qcom,cpr-panic-reg-name-list", NULL)) {
		cpr3_err(ctrl, "property qcom,cpr-panic-reg-name-list not specified\n");
		return -EINVAL;
	}

	len = of_property_count_strings(node, "qcom,cpr-panic-reg-name-list");
	if (reg_count != len) {
		cpr3_err(ctrl, "qcom,cpr-panic-reg-name-list should have %d strings\n",
			reg_count);
		return -EINVAL;
	}

	panic_regs_info = devm_kzalloc(ctrl->dev, sizeof(*panic_regs_info),
					GFP_KERNEL);
	if (!panic_regs_info)
		return -ENOMEM;

	regs = devm_kcalloc(ctrl->dev, reg_count, sizeof(*regs), GFP_KERNEL);
	if (!regs)
		return -ENOMEM;

	for (i = 0; i < reg_count; i++) {
		rc = of_property_read_string_index(node,
				"qcom,cpr-panic-reg-name-list", i,
				&(regs[i].name));
		if (rc) {
			cpr3_err(ctrl, "error reading property qcom,cpr-panic-reg-name-list, rc=%d\n",
				rc);
			return rc;
		}

		rc = of_property_read_u32_index(node,
				"qcom,cpr-panic-reg-addr-list", i,
				&(regs[i].addr));
		if (rc) {
			cpr3_err(ctrl, "error reading property qcom,cpr-panic-reg-addr-list, rc=%d\n",
				rc);
			return rc;
		}
		regs[i].value = 0xFFFFFFFF;
	}

	panic_regs_info->reg_count = reg_count;
	panic_regs_info->regs = regs;
	ctrl->panic_regs_info = panic_regs_info;

	return rc;
}

/**
 * cpr3_parse_common_ctrl_data() - parse common CPR3 controller properties from
 *		device tree
@@ -1147,6 +1215,8 @@ int cpr3_parse_common_ctrl_data(struct cpr3_controller *ctrl)
		}
	}

	rc = cpr3_panic_notifier_init(ctrl);

	return rc;
}