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

Commit 6cf4c49f authored by Channagoud Kadabi's avatar Channagoud Kadabi Committed by Prasad Sodagudi
Browse files

drivers: edac: Make ecc polling configurable



ECC can work both in polling and interrupt mode. Add config option to
enable polling mode.

Change-Id: I99668a143ecbd70a3a4f713f89268df64bfca2da

drivers: edac: Configure the interrupt as level triggered

As per the interrupt maps the ecc interrupt for llcc is level triggered.
Update the irq request call to pass the right trigger.

Change-Id: I029ca8fcf5207d2dffdb13ebc627c64811cf196a

edac: llcc: Add NULL pointer check and fix data types

Add NULL pointer check to make sure edac device is allocated properly,
pass nr_blocks as 0 to edac alloc control device api since the
block_name is null in case of llcc and change the data type of
llcc_banks to u32 instead of phys_addr_t.

Change-Id: I52a0d33a0d546db235751104cd225c29f79f5356

drivers: edac: Update banks for edac sysfs entry

Pass the number of banks and bank name to edac framework to have sysfs
nodes created for each llcc bank for reading out the ce/ue error counts.
Add edac device before registering for interrupt, as the interrupt
handler uses edac device for interrupt processing and would result into
crash if there is any ecc error immidiately after registering the interrupt
and before adding the edac device.

Change-Id: I2c36458684feaf68a63203f871df91f862354e6a
Signed-off-by: default avatarChannagoud Kadabi <ckadabi@codeaurora.org>
Signed-off-by: default avatarPrasad Sodagudi <psodagud@codeaurora.org>
parent 4026f98c
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -510,6 +510,17 @@ config EDAC_QCOM_LLCC
	  For debugging issues having to do with stability and overall system
	  health, you should probably say 'Y' here.

config EDAC_LLCC_POLL
	depends on EDAC_QCOM_LLCC
	bool "Poll on LLCC ECC registers - LLCC"
	help
	   This option chooses whether or not you want to poll on the LLCC
	   ECC registers. When this is enabled, the polling rate can be set as
	   a module parameter. By default, it will call the polling function
	   every second.
	   This option should only be used if the associated interrupt lines
	   are not enabled.

config EDAC_QCOM_LLCC_PANIC_ON_CE
	depends on EDAC_QCOM_LLCC
	bool "panic on correctable errors - qcom llcc"
+50 −40
Original line number Diff line number Diff line
@@ -78,10 +78,12 @@
#define DRP_TRP_INT_CLEAR	0x3
#define DRP_TRP_CNT_CLEAR	0x3

#ifdef CONFIG_EDAC_LLCC_POLL
static int poll_msec = 5000;
module_param(poll_msec, int, 0444);
#endif

static int interrupt_mode;
static int interrupt_mode = 1;
module_param(interrupt_mode, int, 0444);
MODULE_PARM_DESC(interrupt_mode,
		 "Controls whether to use interrupt or poll mode");
@@ -101,7 +103,7 @@ struct errors_edac {

struct erp_drvdata {
	struct regmap *llcc_map;
	phys_addr_t *llcc_banks;
	u32 *llcc_banks;
	u32 ecc_irq;
	u32 num_banks;
	u32 b_off;
@@ -289,7 +291,7 @@ static void dump_syn_reg(struct edac_device_ctl_info *edev_ctl,

	qcom_llcc_clear_errors(err_type, drv);

	errors[err_type].func(edev_ctl, 0, 0, errors[err_type].msg);
	errors[err_type].func(edev_ctl, 0, bank, errors[err_type].msg);
}

static void qcom_llcc_check_cache_errors
@@ -331,10 +333,12 @@ static void qcom_llcc_check_cache_errors
	}
}

#ifdef CONFIG_EDAC_LLCC_POLL
static void qcom_llcc_poll_cache_errors(struct edac_device_ctl_info *edev_ctl)
{
	qcom_llcc_check_cache_errors(edev_ctl);
}
#endif

static irqreturn_t llcc_ecc_irq_handler
			(int irq, void *edev_ctl)
@@ -349,89 +353,95 @@ static int qcom_llcc_erp_probe(struct platform_device *pdev)
	struct erp_drvdata *drv;
	struct edac_device_ctl_info *edev_ctl;
	struct device *dev = &pdev->dev;
	u32 *banks;
	u32 i;
	u32 num_banks;
	struct regmap *llcc_map = NULL;

	llcc_map = syscon_node_to_regmap(dev->parent->of_node);
	if (IS_ERR(llcc_map)) {
		dev_err(dev, "no regmap for syscon llcc parent\n");
		return -ENOMEM;
	}

	/* Find the number of LLC banks supported */
	regmap_read(llcc_map, LLCC_COMMON_STATUS0,
		    &num_banks);

	num_banks &= LLCC_LB_CNT_MASK;
	num_banks >>= LLCC_LB_CNT_SHIFT;

	/* Allocate edac control info */
	edev_ctl = edac_device_alloc_ctl_info(sizeof(*drv), "qcom-llcc", 1,
			NULL, 1, 1, NULL, 0, edac_device_alloc_index());
			"bank", num_banks, 1, NULL, 0,
			edac_device_alloc_index());

	if (!edev_ctl)
		return -ENOMEM;

	edev_ctl->dev = dev;
	edev_ctl->mod_name = dev_name(dev);
	edev_ctl->dev_name = dev_name(dev);
	edev_ctl->ctl_name = "llcc";
#ifdef CONFIG_EDAC_LLCC_POLL
	edev_ctl->poll_msec = poll_msec;
	edev_ctl->edac_check = qcom_llcc_poll_cache_errors;
	edev_ctl->defer_work = 1;
#endif
	edev_ctl->panic_on_ce = LLCC_ERP_PANIC_ON_CE;
	edev_ctl->panic_on_ue = LLCC_ERP_PANIC_ON_UE;

	drv = edev_ctl->pvt_info;
	drv->num_banks = num_banks;
	drv->llcc_map = llcc_map;

	drv->llcc_map = syscon_node_to_regmap(dev->parent->of_node);
	if (IS_ERR(drv->llcc_map)) {
		dev_err(dev, "no regmap for syscon llcc parent\n");
		rc = -ENOMEM;
		goto out;
	}
	rc = edac_device_add_device(edev_ctl);
	if (rc)
		goto out_mem;

	if (interrupt_mode) {
		drv->ecc_irq = platform_get_irq_byname(pdev, "ecc_irq");
		if (!drv->ecc_irq) {
			rc = -ENODEV;
			goto out;
			goto out_dev;
		}

		rc = devm_request_irq(dev, drv->ecc_irq, llcc_ecc_irq_handler,
				IRQF_TRIGGER_RISING, "llcc_ecc", edev_ctl);
				IRQF_TRIGGER_HIGH, "llcc_ecc", edev_ctl);
		if (rc) {
			dev_err(dev, "failed to request ecc irq\n");
			goto out;
			goto out_dev;
		}
	}

	/* Find the number of LLC banks supported */
	regmap_read(drv->llcc_map, LLCC_COMMON_STATUS0,
		    &drv->num_banks);

	drv->num_banks &= LLCC_LB_CNT_MASK;
	drv->num_banks >>= LLCC_LB_CNT_SHIFT;

	drv->llcc_banks = devm_kzalloc(&pdev->dev,
		sizeof(phys_addr_t) * drv->num_banks, GFP_KERNEL);
		sizeof(u32) * drv->num_banks, GFP_KERNEL);

	if (!drv->num_banks) {
	if (!drv->llcc_banks) {
		dev_err(dev, "Cannot allocate memory for llcc_banks\n");
		return -ENOMEM;
		rc = -ENOMEM;
		goto out_dev;
	}

	banks = devm_kzalloc(&pdev->dev,
		sizeof(u32) * drv->num_banks, GFP_KERNEL);
	if (!banks)
		return -ENOMEM;

	rc = of_property_read_u32_array(dev->parent->of_node,
			"qcom,llcc-banks-off", banks, drv->num_banks);
			"qcom,llcc-banks-off", drv->llcc_banks, drv->num_banks);
	if (rc) {
		dev_err(dev, "Cannot read llcc-banks-off property\n");
		return -EINVAL;
		goto out_dev;
	}

	rc = of_property_read_u32(dev->parent->of_node,
			"qcom,llcc-broadcast-off", &drv->b_off);
	if (rc) {
		dev_err(dev, "Cannot read llcc-broadcast-off property\n");
		return -EINVAL;
		goto out_dev;
	}

	for (i = 0; i < drv->num_banks; i++)
		drv->llcc_banks[i] = banks[i];

	platform_set_drvdata(pdev, edev_ctl);

	rc = edac_device_add_device(edev_ctl);
out:
	if (rc)
	return 0;

out_dev:
	edac_device_del_device(edev_ctl->dev);
out_mem:
	edac_device_free_ctl_info(edev_ctl);

	return rc;