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

Commit 79d42bd7 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "drivers: llcc: Update llcc driver based on broadcast behavior" into msm-4.9

parents 274f7bc5 c0a72e77
Loading
Loading
Loading
Loading
+120 −51
Original line number Diff line number Diff line
@@ -39,6 +39,10 @@

#define DRP_SYN_REG_CNT	8

#define LLCC_COMMON_STATUS0		0x0003000C
#define LLCC_LB_CNT_MASK		0xf0000000
#define LLCC_LB_CNT_SHIFT		28

/* single & Double Bit syndrome register offsets */
#define TRP_ECC_SB_ERR_SYN0		0x0002304C
#define TRP_ECC_DB_ERR_SYN0		0x00020370
@@ -97,7 +101,10 @@ struct errors_edac {

struct erp_drvdata {
	struct regmap *llcc_map;
	phys_addr_t *llcc_banks;
	u32 ecc_irq;
	u32 num_banks;
	u32 b_off;
};

static const struct errors_edac errors[] = {
@@ -108,29 +115,32 @@ static const struct errors_edac errors[] = {
};

/* Clear the error interrupt and counter registers */
static void qcom_llcc_clear_errors(int err_type, struct regmap *llcc_map)
static void qcom_llcc_clear_errors(int err_type, struct erp_drvdata *drv)
{
	switch (err_type) {
	case LLCC_DRAM_CE:
	case LLCC_DRAM_UE:
		/* Clear the interrupt */
		regmap_write(llcc_map, DRP_INTERRUPT_CLEAR, DRP_TRP_INT_CLEAR);
		regmap_write(drv->llcc_map, drv->b_off + DRP_INTERRUPT_CLEAR,
			DRP_TRP_INT_CLEAR);
		/* Clear the counters */
		regmap_write(llcc_map, DRP_ECC_ERROR_CNTR_CLEAR,
		regmap_write(drv->llcc_map,
			drv->b_off + DRP_ECC_ERROR_CNTR_CLEAR,
			DRP_TRP_CNT_CLEAR);
		break;
	case LLCC_TRAM_CE:
	case LLCC_TRAM_UE:
		regmap_write(llcc_map, TRP_INTERRUPT_0_CLEAR,
		regmap_write(drv->llcc_map, drv->b_off + TRP_INTERRUPT_0_CLEAR,
			     DRP_TRP_INT_CLEAR);
		regmap_write(llcc_map, TRP_ECC_ERROR_CNTR_CLEAR,
		regmap_write(drv->llcc_map,
			drv->b_off + TRP_ECC_ERROR_CNTR_CLEAR,
			DRP_TRP_CNT_CLEAR);
		break;
	}
}

/* Dump syndrome registers for tag Ram Double bit errors */
static void dump_trp_db_syn_reg(struct regmap *llcc_map)
static void dump_trp_db_syn_reg(struct erp_drvdata *drv, u32 bank)
{
	int i;
	int db_err_cnt;
@@ -140,17 +150,20 @@ static void dump_trp_db_syn_reg(struct regmap *llcc_map)

	for (i = 0; i < TRP_SYN_REG_CNT; i++) {
		synd_reg = TRP_ECC_DB_ERR_SYN0 + (i * 4);
		regmap_read(llcc_map, synd_reg, &synd_val);
		regmap_read(drv->llcc_map, drv->llcc_banks[bank] + synd_reg,
			&synd_val);
		edac_printk(KERN_CRIT, EDAC_LLCC, "TRP_ECC_SYN%d: 0x%8x\n",
			i, synd_val);
	}

	regmap_read(llcc_map, TRP_ECC_ERROR_STATUS1, &db_err_cnt);
	regmap_read(drv->llcc_map,
		drv->llcc_banks[bank] + TRP_ECC_ERROR_STATUS1, &db_err_cnt);
	db_err_cnt = (db_err_cnt & ECC_DB_ERR_COUNT_MASK);
	edac_printk(KERN_CRIT, EDAC_LLCC, "Double-Bit error count: 0x%4x\n",
		db_err_cnt);

	regmap_read(llcc_map, TRP_ECC_ERROR_STATUS0, &db_err_ways);
	regmap_read(drv->llcc_map,
		drv->llcc_banks[bank] + TRP_ECC_ERROR_STATUS0, &db_err_ways);
	db_err_ways = (db_err_ways & ECC_DB_ERR_WAYS_MASK);
	db_err_ways >>= ECC_DB_ERR_WAYS_SHIFT;

@@ -159,7 +172,7 @@ static void dump_trp_db_syn_reg(struct regmap *llcc_map)
}

/* Dump syndrome register for tag Ram Single Bit Errors */
static void dump_trp_sb_syn_reg(struct regmap *llcc_map)
static void dump_trp_sb_syn_reg(struct erp_drvdata *drv, u32 bank)
{
	int i;
	int sb_err_cnt;
@@ -169,18 +182,21 @@ static void dump_trp_sb_syn_reg(struct regmap *llcc_map)

	for (i = 0; i < TRP_SYN_REG_CNT; i++) {
		synd_reg = TRP_ECC_SB_ERR_SYN0 + (i * 4);
		regmap_read(llcc_map, synd_reg, &synd_val);
		regmap_read(drv->llcc_map, drv->llcc_banks[bank] + synd_reg,
			&synd_val);
		edac_printk(KERN_CRIT, EDAC_LLCC, "TRP_ECC_SYN%d: 0x%8x\n",
			i, synd_val);
	}

	regmap_read(llcc_map, TRP_ECC_ERROR_STATUS1, &sb_err_cnt);
	regmap_read(drv->llcc_map,
		drv->llcc_banks[bank] + TRP_ECC_ERROR_STATUS1, &sb_err_cnt);
	sb_err_cnt = (sb_err_cnt & ECC_SB_ERR_COUNT_MASK);
	sb_err_cnt >>= ECC_SB_ERR_COUNT_SHIFT;
	edac_printk(KERN_CRIT, EDAC_LLCC, "Single-Bit error count: 0x%4x\n",
		sb_err_cnt);

	regmap_read(llcc_map, TRP_ECC_ERROR_STATUS0, &sb_err_ways);
	regmap_read(drv->llcc_map,
		drv->llcc_banks[bank] + TRP_ECC_ERROR_STATUS0, &sb_err_ways);
	sb_err_ways = sb_err_ways & ECC_SB_ERR_WAYS_MASK;

	edac_printk(KERN_CRIT, EDAC_LLCC, "Single-Bit error ways: 0x%4x\n",
@@ -188,7 +204,7 @@ static void dump_trp_sb_syn_reg(struct regmap *llcc_map)
}

/* Dump syndrome registers for Data Ram Double bit errors */
static void dump_drp_db_syn_reg(struct regmap *llcc_map)
static void dump_drp_db_syn_reg(struct erp_drvdata *drv, u32 bank)
{
	int i;
	int db_err_cnt;
@@ -198,17 +214,20 @@ static void dump_drp_db_syn_reg(struct regmap *llcc_map)

	for (i = 0; i < DRP_SYN_REG_CNT; i++) {
		synd_reg = DRP_ECC_DB_ERR_SYN0 + (i * 4);
		regmap_read(llcc_map, synd_reg, &synd_val);
		regmap_read(drv->llcc_map, drv->llcc_banks[bank] + synd_reg,
			&synd_val);
		edac_printk(KERN_CRIT, EDAC_LLCC, "DRP_ECC_SYN%d: 0x%8x\n",
			i, synd_val);
	}

	regmap_read(llcc_map, DRP_ECC_ERROR_STATUS1, &db_err_cnt);
	regmap_read(drv->llcc_map,
		drv->llcc_banks[bank] + DRP_ECC_ERROR_STATUS1, &db_err_cnt);
	db_err_cnt = (db_err_cnt & ECC_DB_ERR_COUNT_MASK);
	edac_printk(KERN_CRIT, EDAC_LLCC, "Double-Bit error count: 0x%4x\n",
		db_err_cnt);

	regmap_read(llcc_map, DRP_ECC_ERROR_STATUS0, &db_err_ways);
	regmap_read(drv->llcc_map,
		drv->llcc_banks[bank] + DRP_ECC_ERROR_STATUS0, &db_err_ways);
	db_err_ways &= ECC_DB_ERR_WAYS_MASK;
	db_err_ways >>= ECC_DB_ERR_WAYS_SHIFT;
	edac_printk(KERN_CRIT, EDAC_LLCC, "Double-Bit error ways: 0x%4x\n",
@@ -216,7 +235,7 @@ static void dump_drp_db_syn_reg(struct regmap *llcc_map)
}

/* Dump Syndrome registers for Data Ram Single bit errors*/
static void dump_drp_sb_syn_reg(struct regmap *llcc_map)
static void dump_drp_sb_syn_reg(struct erp_drvdata *drv, u32 bank)
{
	int i;
	int sb_err_cnt;
@@ -226,18 +245,21 @@ static void dump_drp_sb_syn_reg(struct regmap *llcc_map)

	for (i = 0; i < DRP_SYN_REG_CNT; i++) {
		synd_reg = DRP_ECC_SB_ERR_SYN0 + (i * 4);
		regmap_read(llcc_map, synd_reg, &synd_val);
		regmap_read(drv->llcc_map, drv->llcc_banks[bank] + synd_reg,
			&synd_val);
		edac_printk(KERN_CRIT, EDAC_LLCC, "DRP_ECC_SYN%d: 0x%8x\n",
			i, synd_val);
	}

	regmap_read(llcc_map, DRP_ECC_ERROR_STATUS1, &sb_err_cnt);
	regmap_read(drv->llcc_map,
		drv->llcc_banks[bank] + DRP_ECC_ERROR_STATUS1, &sb_err_cnt);
	sb_err_cnt &= ECC_SB_ERR_COUNT_MASK;
	sb_err_cnt >>= ECC_SB_ERR_COUNT_SHIFT;
	edac_printk(KERN_CRIT, EDAC_LLCC, "Single-Bit error count: 0x%4x\n",
		sb_err_cnt);

	regmap_read(llcc_map, DRP_ECC_ERROR_STATUS0, &sb_err_ways);
	regmap_read(drv->llcc_map,
		drv->llcc_banks[bank] + DRP_ECC_ERROR_STATUS0, &sb_err_ways);
	sb_err_ways = sb_err_ways & ECC_SB_ERR_WAYS_MASK;

	edac_printk(KERN_CRIT, EDAC_LLCC, "Single-Bit error ways: 0x%4x\n",
@@ -246,24 +268,26 @@ static void dump_drp_sb_syn_reg(struct regmap *llcc_map)


static void dump_syn_reg(struct edac_device_ctl_info *edev_ctl,
			 int err_type, struct regmap *llcc_map)
			 int err_type, u32 bank)
{
	struct erp_drvdata *drv = edev_ctl->pvt_info;

	switch (err_type) {
	case LLCC_DRAM_CE:
		dump_drp_sb_syn_reg(llcc_map);
		dump_drp_sb_syn_reg(drv, bank);
		break;
	case LLCC_DRAM_UE:
		dump_drp_db_syn_reg(llcc_map);
		dump_drp_db_syn_reg(drv, bank);
		break;
	case LLCC_TRAM_CE:
		dump_trp_sb_syn_reg(llcc_map);
		dump_trp_sb_syn_reg(drv, bank);
		break;
	case LLCC_TRAM_UE:
		dump_trp_db_syn_reg(llcc_map);
		dump_trp_db_syn_reg(drv, bank);
		break;
	}

	qcom_llcc_clear_errors(err_type, llcc_map);
	qcom_llcc_clear_errors(err_type, drv);

	errors[err_type].func(edev_ctl, 0, 0, errors[err_type].msg);
}
@@ -274,30 +298,36 @@ static void qcom_llcc_check_cache_errors
	u32 drp_error;
	u32 trp_error;
	struct erp_drvdata *drv = edev_ctl->pvt_info;
	u32 i;

	for (i = 0; i < drv->num_banks; i++) {
		/* Look for Data RAM errors */
	regmap_read(drv->llcc_map, DRP_INTERRUPT_STATUS, &drp_error);
		regmap_read(drv->llcc_map,
			drv->llcc_banks[i] + DRP_INTERRUPT_STATUS, &drp_error);

		if (drp_error & SB_ECC_ERROR) {
			edac_printk(KERN_CRIT, EDAC_LLCC,
				"Single Bit Error detected in Data Ram\n");
		dump_syn_reg(edev_ctl, LLCC_DRAM_CE, drv->llcc_map);
			dump_syn_reg(edev_ctl, LLCC_DRAM_CE, i);
		} else if (drp_error & DB_ECC_ERROR) {
			edac_printk(KERN_CRIT, EDAC_LLCC,
				"Double Bit Error detected in Data Ram\n");
		dump_syn_reg(edev_ctl, LLCC_DRAM_UE, drv->llcc_map);
			dump_syn_reg(edev_ctl, LLCC_DRAM_UE, i);
		}

		/* Look for Tag RAM errors */
	regmap_read(drv->llcc_map, TRP_INTERRUPT_0_STATUS, &trp_error);
		regmap_read(drv->llcc_map,
			drv->llcc_banks[i] + TRP_INTERRUPT_0_STATUS,
			&trp_error);
		if (trp_error & SB_ECC_ERROR) {
			edac_printk(KERN_CRIT, EDAC_LLCC,
				"Single Bit Error detected in Tag Ram\n");
		dump_syn_reg(edev_ctl, LLCC_TRAM_CE, drv->llcc_map);
			dump_syn_reg(edev_ctl, LLCC_TRAM_CE, i);
		} else if (trp_error & DB_ECC_ERROR) {
			edac_printk(KERN_CRIT, EDAC_LLCC,
				"Double Bit Error detected in Tag Ram\n");
		dump_syn_reg(edev_ctl, LLCC_TRAM_UE, drv->llcc_map);
			dump_syn_reg(edev_ctl, LLCC_TRAM_UE, i);
		}
	}
}

@@ -319,6 +349,8 @@ 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;

	/* Allocate edac control info */
	edev_ctl = edac_device_alloc_ctl_info(sizeof(*drv), "qcom-llcc", 1,
@@ -358,6 +390,43 @@ static int qcom_llcc_erp_probe(struct platform_device *pdev)
		}
	}

	/* 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);

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

	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);
	if (rc) {
		dev_err(dev, "Cannot read llcc-banks-off property\n");
		return -EINVAL;
	}

	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;
	}

	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);
+16 −12
Original line number Diff line number Diff line
@@ -33,42 +33,39 @@
#define SB_DB_TRP_INTERRUPT_ENABLE	0x3
#define TRP0_INTERRUPT_ENABLE	0x1
#define DRP0_INTERRUPT_ENABLE	BIT(6)
#define COMMON_INTERRUPT_0_AMON BIT(8)
#define SB_DB_DRP_INTERRUPT_ENABLE	0x3

static void qcom_llcc_core_setup(struct regmap *llcc_regmap)
static void qcom_llcc_core_setup(struct regmap *llcc_regmap, uint32_t b_off)
{
	u32 sb_err_threshold;

	/* Enable TRP in instance 2 of common interrupt enable register */
	regmap_update_bits(llcc_regmap, CMN_INTERRUPT_2_ENABLE,
	regmap_update_bits(llcc_regmap, b_off + CMN_INTERRUPT_2_ENABLE,
			   TRP0_INTERRUPT_ENABLE, TRP0_INTERRUPT_ENABLE);

	/* Enable ECC interrupts on Tag Ram */
	regmap_update_bits(llcc_regmap, TRP_INTERRUPT_0_ENABLE,
	regmap_update_bits(llcc_regmap, b_off + TRP_INTERRUPT_0_ENABLE,
		SB_DB_TRP_INTERRUPT_ENABLE, SB_DB_TRP_INTERRUPT_ENABLE);

	/* Enable SB error for Data RAM */
	sb_err_threshold = (SB_ERROR_THRESHOLD << SB_ERROR_THRESHOLD_SHIFT);
	regmap_write(llcc_regmap, DRP_ECC_ERROR_CFG, sb_err_threshold);
	regmap_write(llcc_regmap, b_off + DRP_ECC_ERROR_CFG, sb_err_threshold);

	/* Enable DRP in instance 2 of common interrupt enable register */
	regmap_update_bits(llcc_regmap, CMN_INTERRUPT_2_ENABLE,
	regmap_update_bits(llcc_regmap, b_off + CMN_INTERRUPT_2_ENABLE,
			   DRP0_INTERRUPT_ENABLE, DRP0_INTERRUPT_ENABLE);

	/* Enable ECC interrupts on Data Ram */
	regmap_write(llcc_regmap, DRP_INTERRUPT_ENABLE,
	regmap_write(llcc_regmap, b_off + DRP_INTERRUPT_ENABLE,
		     SB_DB_DRP_INTERRUPT_ENABLE);

	/* Enable AMON interrupt in the common interrupt register */
	regmap_update_bits(llcc_regmap, CMN_INTERRUPT_0_ENABLE,
			COMMON_INTERRUPT_0_AMON, COMMON_INTERRUPT_0_AMON);
}

static int qcom_llcc_core_probe(struct platform_device *pdev)
{
	struct regmap *llcc_regmap;
	struct device *dev = &pdev->dev;
	u32 b_off = 0;
	int ret = 0;

	llcc_regmap = syscon_node_to_regmap(dev->of_node);

@@ -77,7 +74,14 @@ static int qcom_llcc_core_probe(struct platform_device *pdev)
		return PTR_ERR(llcc_regmap);
	}

	qcom_llcc_core_setup(llcc_regmap);
	ret = of_property_read_u32(dev->of_node,
			"qcom,llcc-broadcast-off", &b_off);
	if (ret) {
		dev_err(&pdev->dev, "Unable to read broadcast-off\n");
		return -EINVAL;
	}

	qcom_llcc_core_setup(llcc_regmap, b_off);

	return 0;
}
+16 −5
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@

#define CACHE_LINE_SIZE_SHIFT 6
#define SIZE_PER_LLCC_SHIFT   2

#define MAX_CAP_TO_BYTES(n) (n * 1024)
#define LLCC_TRP_ACT_CTRLn(n) (n * 0x1000)
#define LLCC_TRP_STATUSn(n)   (4 + n * 0x1000)
@@ -63,6 +64,7 @@ struct llcc_drv_data {
	struct mutex slice_mutex;
	u32 llcc_config_data_sz;
	u32 max_slices;
	u32 b_off;
	unsigned long *llcc_slice_map;
};

@@ -172,8 +174,8 @@ static int llcc_update_act_ctrl(struct llcc_drv_data *drv, u32 sid,
	u32 slice_status;
	unsigned long timeout;

	act_ctrl_reg = LLCC_TRP_ACT_CTRLn(sid);
	status_reg = LLCC_TRP_STATUSn(sid);
	act_ctrl_reg = drv->b_off + LLCC_TRP_ACT_CTRLn(sid);
	status_reg = drv->b_off + LLCC_TRP_STATUSn(sid);

	regmap_write(drv->llcc_map, act_ctrl_reg, act_ctrl_reg_val);

@@ -323,13 +325,14 @@ static void qcom_llcc_cfg_program(struct platform_device *pdev)
	const struct llcc_slice_config *llcc_table;
	struct llcc_drv_data *drv = platform_get_drvdata(pdev);
	struct llcc_slice_desc desc;
	u32 b_off = drv->b_off;

	sz = drv->llcc_config_data_sz;
	llcc_table = drv->slice_data;

	for (i = 0; i < sz; i++) {
		attr1_cfg = LLCC_TRP_ATTR1_CFGn(llcc_table[i].slice_id);
		attr0_cfg = LLCC_TRP_ATTR0_CFGn(llcc_table[i].slice_id);
		attr1_cfg = b_off + LLCC_TRP_ATTR1_CFGn(llcc_table[i].slice_id);
		attr0_cfg = b_off + LLCC_TRP_ATTR0_CFGn(llcc_table[i].slice_id);

		attr1_val = llcc_table[i].cache_mode;
		attr1_val |= (llcc_table[i].probe_target_ways <<
@@ -386,7 +389,15 @@ int qcom_llcc_probe(struct platform_device *pdev,
	rc = of_property_read_u32(pdev->dev.of_node, "max-slices",
				  &drv_data->max_slices);
	if (rc) {
		dev_info(&pdev->dev, "Invalid max-slices dt entry\n");
		dev_err(&pdev->dev, "Invalid max-slices dt entry\n");
		devm_kfree(&pdev->dev, drv_data);
		return rc;
	}

	rc = of_property_read_u32(pdev->dev.parent->of_node,
			"qcom,llcc-broadcast-off", &drv_data->b_off);
	if (rc) {
		dev_err(&pdev->dev, "Invalid qcom,broadcast-off entry\n");
		devm_kfree(&pdev->dev, drv_data);
		return rc;
	}