brintos

brintos / linux-shallow public Read only

0
0
Text · 15.6 KiB · 4a97d4a Raw
572 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Error Location Module4 *5 * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/6 */7 8#define DRIVER_NAME	"omap-elm"9 10#include <linux/platform_device.h>11#include <linux/module.h>12#include <linux/interrupt.h>13#include <linux/io.h>14#include <linux/of.h>15#include <linux/sched.h>16#include <linux/pm_runtime.h>17#include <linux/platform_data/elm.h>18 19#define ELM_SYSCONFIG			0x01020#define ELM_IRQSTATUS			0x01821#define ELM_IRQENABLE			0x01c22#define ELM_LOCATION_CONFIG		0x02023#define ELM_PAGE_CTRL			0x08024#define ELM_SYNDROME_FRAGMENT_0		0x40025#define ELM_SYNDROME_FRAGMENT_1		0x40426#define ELM_SYNDROME_FRAGMENT_2		0x40827#define ELM_SYNDROME_FRAGMENT_3		0x40c28#define ELM_SYNDROME_FRAGMENT_4		0x41029#define ELM_SYNDROME_FRAGMENT_5		0x41430#define ELM_SYNDROME_FRAGMENT_6		0x41831#define ELM_LOCATION_STATUS		0x80032#define ELM_ERROR_LOCATION_0		0x88033 34/* ELM Interrupt Status Register */35#define INTR_STATUS_PAGE_VALID		BIT(8)36 37/* ELM Interrupt Enable Register */38#define INTR_EN_PAGE_MASK		BIT(8)39 40/* ELM Location Configuration Register */41#define ECC_BCH_LEVEL_MASK		0x342 43/* ELM syndrome */44#define ELM_SYNDROME_VALID		BIT(16)45 46/* ELM_LOCATION_STATUS Register */47#define ECC_CORRECTABLE_MASK		BIT(8)48#define ECC_NB_ERRORS_MASK		0x1f49 50/* ELM_ERROR_LOCATION_0-15 Registers */51#define ECC_ERROR_LOCATION_MASK		0x1fff52 53#define ELM_ECC_SIZE			0x7ff54 55#define SYNDROME_FRAGMENT_REG_SIZE	0x4056#define ERROR_LOCATION_SIZE		0x10057 58struct elm_registers {59	u32 elm_irqenable;60	u32 elm_sysconfig;61	u32 elm_location_config;62	u32 elm_page_ctrl;63	u32 elm_syndrome_fragment_6[ERROR_VECTOR_MAX];64	u32 elm_syndrome_fragment_5[ERROR_VECTOR_MAX];65	u32 elm_syndrome_fragment_4[ERROR_VECTOR_MAX];66	u32 elm_syndrome_fragment_3[ERROR_VECTOR_MAX];67	u32 elm_syndrome_fragment_2[ERROR_VECTOR_MAX];68	u32 elm_syndrome_fragment_1[ERROR_VECTOR_MAX];69	u32 elm_syndrome_fragment_0[ERROR_VECTOR_MAX];70};71 72struct elm_info {73	struct device *dev;74	void __iomem *elm_base;75	struct completion elm_completion;76	struct list_head list;77	enum bch_ecc bch_type;78	struct elm_registers elm_regs;79	int ecc_steps;80	int ecc_syndrome_size;81};82 83static LIST_HEAD(elm_devices);84 85static void elm_write_reg(struct elm_info *info, int offset, u32 val)86{87	writel(val, info->elm_base + offset);88}89 90static u32 elm_read_reg(struct elm_info *info, int offset)91{92	return readl(info->elm_base + offset);93}94 95/**96 * elm_config - Configure ELM module97 * @dev:	ELM device98 * @bch_type:	Type of BCH ecc99 * @ecc_steps:	ECC steps to assign to config100 * @ecc_step_size:	ECC step size to assign to config101 * @ecc_syndrome_size:	ECC syndrome size to assign to config102 */103int elm_config(struct device *dev, enum bch_ecc bch_type,104	int ecc_steps, int ecc_step_size, int ecc_syndrome_size)105{106	u32 reg_val;107	struct elm_info *info = dev_get_drvdata(dev);108 109	if (!info) {110		dev_err(dev, "Unable to configure elm - device not probed?\n");111		return -EPROBE_DEFER;112	}113	/* ELM cannot detect ECC errors for chunks > 1KB */114	if (ecc_step_size > ((ELM_ECC_SIZE + 1) / 2)) {115		dev_err(dev, "unsupported config ecc-size=%d\n", ecc_step_size);116		return -EINVAL;117	}118	/* ELM support 8 error syndrome process */119	if (ecc_steps > ERROR_VECTOR_MAX && ecc_steps % ERROR_VECTOR_MAX) {120		dev_err(dev, "unsupported config ecc-step=%d\n", ecc_steps);121		return -EINVAL;122	}123 124	reg_val = (bch_type & ECC_BCH_LEVEL_MASK) | (ELM_ECC_SIZE << 16);125	elm_write_reg(info, ELM_LOCATION_CONFIG, reg_val);126	info->bch_type		= bch_type;127	info->ecc_steps		= ecc_steps;128	info->ecc_syndrome_size	= ecc_syndrome_size;129 130	return 0;131}132EXPORT_SYMBOL(elm_config);133 134/**135 * elm_configure_page_mode - Enable/Disable page mode136 * @info:	elm info137 * @index:	index number of syndrome fragment vector138 * @enable:	enable/disable flag for page mode139 *140 * Enable page mode for syndrome fragment index141 */142static void elm_configure_page_mode(struct elm_info *info, int index,143		bool enable)144{145	u32 reg_val;146 147	reg_val = elm_read_reg(info, ELM_PAGE_CTRL);148	if (enable)149		reg_val |= BIT(index);	/* enable page mode */150	else151		reg_val &= ~BIT(index);	/* disable page mode */152 153	elm_write_reg(info, ELM_PAGE_CTRL, reg_val);154}155 156/**157 * elm_load_syndrome - Load ELM syndrome reg158 * @info:	elm info159 * @err_vec:	elm error vectors160 * @ecc:	buffer with calculated ecc161 *162 * Load syndrome fragment registers with calculated ecc in reverse order.163 */164static void elm_load_syndrome(struct elm_info *info,165		struct elm_errorvec *err_vec, u8 *ecc)166{167	int i, offset;168	u32 val;169 170	for (i = 0; i < info->ecc_steps; i++) {171 172		/* Check error reported */173		if (err_vec[i].error_reported) {174			elm_configure_page_mode(info, i, true);175			offset = ELM_SYNDROME_FRAGMENT_0 +176				SYNDROME_FRAGMENT_REG_SIZE * i;177			switch (info->bch_type) {178			case BCH8_ECC:179				/* syndrome fragment 0 = ecc[9-12B] */180				val = (__force u32)cpu_to_be32(*(u32 *)&ecc[9]);181				elm_write_reg(info, offset, val);182 183				/* syndrome fragment 1 = ecc[5-8B] */184				offset += 4;185				val = (__force u32)cpu_to_be32(*(u32 *)&ecc[5]);186				elm_write_reg(info, offset, val);187 188				/* syndrome fragment 2 = ecc[1-4B] */189				offset += 4;190				val = (__force u32)cpu_to_be32(*(u32 *)&ecc[1]);191				elm_write_reg(info, offset, val);192 193				/* syndrome fragment 3 = ecc[0B] */194				offset += 4;195				val = ecc[0];196				elm_write_reg(info, offset, val);197				break;198			case BCH4_ECC:199				/* syndrome fragment 0 = ecc[20-52b] bits */200				val = ((__force u32)cpu_to_be32(*(u32 *)&ecc[3]) >> 4) |201					((ecc[2] & 0xf) << 28);202				elm_write_reg(info, offset, val);203 204				/* syndrome fragment 1 = ecc[0-20b] bits */205				offset += 4;206				val = (__force u32)cpu_to_be32(*(u32 *)&ecc[0]) >> 12;207				elm_write_reg(info, offset, val);208				break;209			case BCH16_ECC:210				val = (__force u32)cpu_to_be32(*(u32 *)&ecc[22]);211				elm_write_reg(info, offset, val);212				offset += 4;213				val = (__force u32)cpu_to_be32(*(u32 *)&ecc[18]);214				elm_write_reg(info, offset, val);215				offset += 4;216				val = (__force u32)cpu_to_be32(*(u32 *)&ecc[14]);217				elm_write_reg(info, offset, val);218				offset += 4;219				val = (__force u32)cpu_to_be32(*(u32 *)&ecc[10]);220				elm_write_reg(info, offset, val);221				offset += 4;222				val = (__force u32)cpu_to_be32(*(u32 *)&ecc[6]);223				elm_write_reg(info, offset, val);224				offset += 4;225				val = (__force u32)cpu_to_be32(*(u32 *)&ecc[2]);226				elm_write_reg(info, offset, val);227				offset += 4;228				val = (__force u32)cpu_to_be32(*(u32 *)&ecc[0]) >> 16;229				elm_write_reg(info, offset, val);230				break;231			default:232				pr_err("invalid config bch_type\n");233			}234		}235 236		/* Update ecc pointer with ecc byte size */237		ecc += info->ecc_syndrome_size;238	}239}240 241/**242 * elm_start_processing - start elm syndrome processing243 * @info:	elm info244 * @err_vec:	elm error vectors245 *246 * Set syndrome valid bit for syndrome fragment registers for which247 * elm syndrome fragment registers are loaded. This enables elm module248 * to start processing syndrome vectors.249 */250static void elm_start_processing(struct elm_info *info,251		struct elm_errorvec *err_vec)252{253	int i, offset;254	u32 reg_val;255 256	/*257	 * Set syndrome vector valid, so that ELM module258	 * will process it for vectors error is reported259	 */260	for (i = 0; i < info->ecc_steps; i++) {261		if (err_vec[i].error_reported) {262			offset = ELM_SYNDROME_FRAGMENT_6 +263				SYNDROME_FRAGMENT_REG_SIZE * i;264			reg_val = elm_read_reg(info, offset);265			reg_val |= ELM_SYNDROME_VALID;266			elm_write_reg(info, offset, reg_val);267		}268	}269}270 271/**272 * elm_error_correction - locate correctable error position273 * @info:	elm info274 * @err_vec:	elm error vectors275 *276 * On completion of processing by elm module, error location status277 * register updated with correctable/uncorrectable error information.278 * In case of correctable errors, number of errors located from279 * elm location status register & read the positions from280 * elm error location register.281 */282static void elm_error_correction(struct elm_info *info,283		struct elm_errorvec *err_vec)284{285	int i, j;286	int offset;287	u32 reg_val;288 289	for (i = 0; i < info->ecc_steps; i++) {290 291		/* Check error reported */292		if (err_vec[i].error_reported) {293			offset = ELM_LOCATION_STATUS + ERROR_LOCATION_SIZE * i;294			reg_val = elm_read_reg(info, offset);295 296			/* Check correctable error or not */297			if (reg_val & ECC_CORRECTABLE_MASK) {298				offset = ELM_ERROR_LOCATION_0 +299					ERROR_LOCATION_SIZE * i;300 301				/* Read count of correctable errors */302				err_vec[i].error_count = reg_val &303					ECC_NB_ERRORS_MASK;304 305				/* Update the error locations in error vector */306				for (j = 0; j < err_vec[i].error_count; j++) {307 308					reg_val = elm_read_reg(info, offset);309					err_vec[i].error_loc[j] = reg_val &310						ECC_ERROR_LOCATION_MASK;311 312					/* Update error location register */313					offset += 4;314				}315			} else {316				err_vec[i].error_uncorrectable = true;317			}318 319			/* Clearing interrupts for processed error vectors */320			elm_write_reg(info, ELM_IRQSTATUS, BIT(i));321 322			/* Disable page mode */323			elm_configure_page_mode(info, i, false);324		}325	}326}327 328/**329 * elm_decode_bch_error_page - Locate error position330 * @dev:	device pointer331 * @ecc_calc:	calculated ECC bytes from GPMC332 * @err_vec:	elm error vectors333 *334 * Called with one or more error reported vectors & vectors with335 * error reported is updated in err_vec[].error_reported336 */337void elm_decode_bch_error_page(struct device *dev, u8 *ecc_calc,338		struct elm_errorvec *err_vec)339{340	struct elm_info *info = dev_get_drvdata(dev);341	u32 reg_val;342 343	/* Enable page mode interrupt */344	reg_val = elm_read_reg(info, ELM_IRQSTATUS);345	elm_write_reg(info, ELM_IRQSTATUS, reg_val & INTR_STATUS_PAGE_VALID);346	elm_write_reg(info, ELM_IRQENABLE, INTR_EN_PAGE_MASK);347 348	/* Load valid ecc byte to syndrome fragment register */349	elm_load_syndrome(info, err_vec, ecc_calc);350 351	/* Enable syndrome processing for which syndrome fragment is updated */352	elm_start_processing(info, err_vec);353 354	/* Wait for ELM module to finish locating error correction */355	wait_for_completion(&info->elm_completion);356 357	/* Disable page mode interrupt */358	reg_val = elm_read_reg(info, ELM_IRQENABLE);359	elm_write_reg(info, ELM_IRQENABLE, reg_val & ~INTR_EN_PAGE_MASK);360	elm_error_correction(info, err_vec);361}362EXPORT_SYMBOL(elm_decode_bch_error_page);363 364static irqreturn_t elm_isr(int this_irq, void *dev_id)365{366	u32 reg_val;367	struct elm_info *info = dev_id;368 369	reg_val = elm_read_reg(info, ELM_IRQSTATUS);370 371	/* All error vectors processed */372	if (reg_val & INTR_STATUS_PAGE_VALID) {373		elm_write_reg(info, ELM_IRQSTATUS,374				reg_val & INTR_STATUS_PAGE_VALID);375		complete(&info->elm_completion);376		return IRQ_HANDLED;377	}378 379	return IRQ_NONE;380}381 382static int elm_probe(struct platform_device *pdev)383{384	int ret = 0;385	struct elm_info *info;386	int irq;387 388	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);389	if (!info)390		return -ENOMEM;391 392	info->dev = &pdev->dev;393 394	irq = platform_get_irq(pdev, 0);395	if (irq < 0)396		return irq;397 398	info->elm_base = devm_platform_ioremap_resource(pdev, 0);399	if (IS_ERR(info->elm_base))400		return PTR_ERR(info->elm_base);401 402	ret = devm_request_irq(&pdev->dev, irq, elm_isr, 0,403			       pdev->name, info);404	if (ret) {405		dev_err(&pdev->dev, "failure requesting %d\n", irq);406		return ret;407	}408 409	pm_runtime_enable(&pdev->dev);410	if (pm_runtime_get_sync(&pdev->dev) < 0) {411		ret = -EINVAL;412		pm_runtime_put_sync(&pdev->dev);413		pm_runtime_disable(&pdev->dev);414		dev_err(&pdev->dev, "can't enable clock\n");415		return ret;416	}417 418	init_completion(&info->elm_completion);419	INIT_LIST_HEAD(&info->list);420	list_add(&info->list, &elm_devices);421	platform_set_drvdata(pdev, info);422	return ret;423}424 425static void elm_remove(struct platform_device *pdev)426{427	pm_runtime_put_sync(&pdev->dev);428	pm_runtime_disable(&pdev->dev);429}430 431#ifdef CONFIG_PM_SLEEP432/*433 * elm_context_save434 * saves ELM configurations to preserve them across Hardware powered-down435 */436static int elm_context_save(struct elm_info *info)437{438	struct elm_registers *regs = &info->elm_regs;439	enum bch_ecc bch_type = info->bch_type;440	u32 offset = 0, i;441 442	regs->elm_irqenable       = elm_read_reg(info, ELM_IRQENABLE);443	regs->elm_sysconfig       = elm_read_reg(info, ELM_SYSCONFIG);444	regs->elm_location_config = elm_read_reg(info, ELM_LOCATION_CONFIG);445	regs->elm_page_ctrl       = elm_read_reg(info, ELM_PAGE_CTRL);446	for (i = 0; i < ERROR_VECTOR_MAX; i++) {447		offset = i * SYNDROME_FRAGMENT_REG_SIZE;448		switch (bch_type) {449		case BCH16_ECC:450			regs->elm_syndrome_fragment_6[i] = elm_read_reg(info,451					ELM_SYNDROME_FRAGMENT_6 + offset);452			regs->elm_syndrome_fragment_5[i] = elm_read_reg(info,453					ELM_SYNDROME_FRAGMENT_5 + offset);454			regs->elm_syndrome_fragment_4[i] = elm_read_reg(info,455					ELM_SYNDROME_FRAGMENT_4 + offset);456			fallthrough;457		case BCH8_ECC:458			regs->elm_syndrome_fragment_3[i] = elm_read_reg(info,459					ELM_SYNDROME_FRAGMENT_3 + offset);460			regs->elm_syndrome_fragment_2[i] = elm_read_reg(info,461					ELM_SYNDROME_FRAGMENT_2 + offset);462			fallthrough;463		case BCH4_ECC:464			regs->elm_syndrome_fragment_1[i] = elm_read_reg(info,465					ELM_SYNDROME_FRAGMENT_1 + offset);466			regs->elm_syndrome_fragment_0[i] = elm_read_reg(info,467					ELM_SYNDROME_FRAGMENT_0 + offset);468			break;469		default:470			return -EINVAL;471		}472		/* ELM SYNDROME_VALID bit in SYNDROME_FRAGMENT_6[] needs473		 * to be saved for all BCH schemes*/474		regs->elm_syndrome_fragment_6[i] = elm_read_reg(info,475					ELM_SYNDROME_FRAGMENT_6 + offset);476	}477	return 0;478}479 480/*481 * elm_context_restore482 * writes configurations saved duing power-down back into ELM registers483 */484static int elm_context_restore(struct elm_info *info)485{486	struct elm_registers *regs = &info->elm_regs;487	enum bch_ecc bch_type = info->bch_type;488	u32 offset = 0, i;489 490	elm_write_reg(info, ELM_IRQENABLE,	 regs->elm_irqenable);491	elm_write_reg(info, ELM_SYSCONFIG,	 regs->elm_sysconfig);492	elm_write_reg(info, ELM_LOCATION_CONFIG, regs->elm_location_config);493	elm_write_reg(info, ELM_PAGE_CTRL,	 regs->elm_page_ctrl);494	for (i = 0; i < ERROR_VECTOR_MAX; i++) {495		offset = i * SYNDROME_FRAGMENT_REG_SIZE;496		switch (bch_type) {497		case BCH16_ECC:498			elm_write_reg(info, ELM_SYNDROME_FRAGMENT_6 + offset,499					regs->elm_syndrome_fragment_6[i]);500			elm_write_reg(info, ELM_SYNDROME_FRAGMENT_5 + offset,501					regs->elm_syndrome_fragment_5[i]);502			elm_write_reg(info, ELM_SYNDROME_FRAGMENT_4 + offset,503					regs->elm_syndrome_fragment_4[i]);504			fallthrough;505		case BCH8_ECC:506			elm_write_reg(info, ELM_SYNDROME_FRAGMENT_3 + offset,507					regs->elm_syndrome_fragment_3[i]);508			elm_write_reg(info, ELM_SYNDROME_FRAGMENT_2 + offset,509					regs->elm_syndrome_fragment_2[i]);510			fallthrough;511		case BCH4_ECC:512			elm_write_reg(info, ELM_SYNDROME_FRAGMENT_1 + offset,513					regs->elm_syndrome_fragment_1[i]);514			elm_write_reg(info, ELM_SYNDROME_FRAGMENT_0 + offset,515					regs->elm_syndrome_fragment_0[i]);516			break;517		default:518			return -EINVAL;519		}520		/* ELM_SYNDROME_VALID bit to be set in last to trigger FSM */521		elm_write_reg(info, ELM_SYNDROME_FRAGMENT_6 + offset,522					regs->elm_syndrome_fragment_6[i] &523							 ELM_SYNDROME_VALID);524	}525	return 0;526}527 528static int elm_suspend(struct device *dev)529{530	struct elm_info *info = dev_get_drvdata(dev);531	elm_context_save(info);532	pm_runtime_put_sync(dev);533	return 0;534}535 536static int elm_resume(struct device *dev)537{538	struct elm_info *info = dev_get_drvdata(dev);539	pm_runtime_get_sync(dev);540	elm_context_restore(info);541	return 0;542}543#endif544 545static SIMPLE_DEV_PM_OPS(elm_pm_ops, elm_suspend, elm_resume);546 547#ifdef CONFIG_OF548static const struct of_device_id elm_of_match[] = {549	{ .compatible = "ti,am3352-elm" },550	{ .compatible = "ti,am64-elm" },551	{},552};553MODULE_DEVICE_TABLE(of, elm_of_match);554#endif555 556static struct platform_driver elm_driver = {557	.driver	= {558		.name	= DRIVER_NAME,559		.of_match_table = of_match_ptr(elm_of_match),560		.pm	= &elm_pm_ops,561	},562	.probe	= elm_probe,563	.remove_new = elm_remove,564};565 566module_platform_driver(elm_driver);567 568MODULE_DESCRIPTION("ELM driver for BCH error correction");569MODULE_AUTHOR("Texas Instruments");570MODULE_ALIAS("platform:" DRIVER_NAME);571MODULE_LICENSE("GPL v2");572