cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

omap_elm.c (15794B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Error Location Module
      4 *
      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			0x010
     20#define ELM_IRQSTATUS			0x018
     21#define ELM_IRQENABLE			0x01c
     22#define ELM_LOCATION_CONFIG		0x020
     23#define ELM_PAGE_CTRL			0x080
     24#define ELM_SYNDROME_FRAGMENT_0		0x400
     25#define ELM_SYNDROME_FRAGMENT_1		0x404
     26#define ELM_SYNDROME_FRAGMENT_2		0x408
     27#define ELM_SYNDROME_FRAGMENT_3		0x40c
     28#define ELM_SYNDROME_FRAGMENT_4		0x410
     29#define ELM_SYNDROME_FRAGMENT_5		0x414
     30#define ELM_SYNDROME_FRAGMENT_6		0x418
     31#define ELM_LOCATION_STATUS		0x800
     32#define ELM_ERROR_LOCATION_0		0x880
     33
     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		0x3
     42
     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		0x1f
     49
     50/* ELM_ERROR_LOCATION_0-15 Registers */
     51#define ECC_ERROR_LOCATION_MASK		0x1fff
     52
     53#define ELM_ECC_SIZE			0x7ff
     54
     55#define SYNDROME_FRAGMENT_REG_SIZE	0x40
     56#define ERROR_LOCATION_SIZE		0x100
     57
     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 module
     97 * @dev:	ELM device
     98 * @bch_type:	Type of BCH ecc
     99 * @ecc_steps:	ECC steps to assign to config
    100 * @ecc_step_size:	ECC step size to assign to config
    101 * @ecc_syndrome_size:	ECC syndrome size to assign to config
    102 */
    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 mode
    136 * @info:	elm info
    137 * @index:	index number of syndrome fragment vector
    138 * @enable:	enable/disable flag for page mode
    139 *
    140 * Enable page mode for syndrome fragment index
    141 */
    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	else
    151		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 reg
    158 * @info:	elm info
    159 * @err_vec:	elm error vectors
    160 * @ecc:	buffer with calculated ecc
    161 *
    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 = 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 = 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 = 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 = (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 = cpu_to_be32(*(u32 *) &ecc[0]) >> 12;
    207				elm_write_reg(info, offset, val);
    208				break;
    209			case BCH16_ECC:
    210				val = cpu_to_be32(*(u32 *) &ecc[22]);
    211				elm_write_reg(info, offset, val);
    212				offset += 4;
    213				val = cpu_to_be32(*(u32 *) &ecc[18]);
    214				elm_write_reg(info, offset, val);
    215				offset += 4;
    216				val = cpu_to_be32(*(u32 *) &ecc[14]);
    217				elm_write_reg(info, offset, val);
    218				offset += 4;
    219				val = cpu_to_be32(*(u32 *) &ecc[10]);
    220				elm_write_reg(info, offset, val);
    221				offset += 4;
    222				val = cpu_to_be32(*(u32 *) &ecc[6]);
    223				elm_write_reg(info, offset, val);
    224				offset += 4;
    225				val = cpu_to_be32(*(u32 *) &ecc[2]);
    226				elm_write_reg(info, offset, val);
    227				offset += 4;
    228				val = 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 processing
    243 * @info:	elm info
    244 * @err_vec:	elm error vectors
    245 *
    246 * Set syndrome valid bit for syndrome fragment registers for which
    247 * elm syndrome fragment registers are loaded. This enables elm module
    248 * 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 module
    258	 * will process it for vectors error is reported
    259	 */
    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 position
    273 * @info:	elm info
    274 * @err_vec:	elm error vectors
    275 *
    276 * On completion of processing by elm module, error location status
    277 * register updated with correctable/uncorrectable error information.
    278 * In case of correctable errors, number of errors located from
    279 * elm location status register & read the positions from
    280 * 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 position
    330 * @dev:	device pointer
    331 * @ecc_calc:	calculated ECC bytes from GPMC
    332 * @err_vec:	elm error vectors
    333 *
    334 * Called with one or more error reported vectors & vectors with
    335 * error reported is updated in err_vec[].error_reported
    336 */
    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 int elm_remove(struct platform_device *pdev)
    426{
    427	pm_runtime_put_sync(&pdev->dev);
    428	pm_runtime_disable(&pdev->dev);
    429	return 0;
    430}
    431
    432#ifdef CONFIG_PM_SLEEP
    433/*
    434 * elm_context_save
    435 * saves ELM configurations to preserve them across Hardware powered-down
    436 */
    437static int elm_context_save(struct elm_info *info)
    438{
    439	struct elm_registers *regs = &info->elm_regs;
    440	enum bch_ecc bch_type = info->bch_type;
    441	u32 offset = 0, i;
    442
    443	regs->elm_irqenable       = elm_read_reg(info, ELM_IRQENABLE);
    444	regs->elm_sysconfig       = elm_read_reg(info, ELM_SYSCONFIG);
    445	regs->elm_location_config = elm_read_reg(info, ELM_LOCATION_CONFIG);
    446	regs->elm_page_ctrl       = elm_read_reg(info, ELM_PAGE_CTRL);
    447	for (i = 0; i < ERROR_VECTOR_MAX; i++) {
    448		offset = i * SYNDROME_FRAGMENT_REG_SIZE;
    449		switch (bch_type) {
    450		case BCH16_ECC:
    451			regs->elm_syndrome_fragment_6[i] = elm_read_reg(info,
    452					ELM_SYNDROME_FRAGMENT_6 + offset);
    453			regs->elm_syndrome_fragment_5[i] = elm_read_reg(info,
    454					ELM_SYNDROME_FRAGMENT_5 + offset);
    455			regs->elm_syndrome_fragment_4[i] = elm_read_reg(info,
    456					ELM_SYNDROME_FRAGMENT_4 + offset);
    457			fallthrough;
    458		case BCH8_ECC:
    459			regs->elm_syndrome_fragment_3[i] = elm_read_reg(info,
    460					ELM_SYNDROME_FRAGMENT_3 + offset);
    461			regs->elm_syndrome_fragment_2[i] = elm_read_reg(info,
    462					ELM_SYNDROME_FRAGMENT_2 + offset);
    463			fallthrough;
    464		case BCH4_ECC:
    465			regs->elm_syndrome_fragment_1[i] = elm_read_reg(info,
    466					ELM_SYNDROME_FRAGMENT_1 + offset);
    467			regs->elm_syndrome_fragment_0[i] = elm_read_reg(info,
    468					ELM_SYNDROME_FRAGMENT_0 + offset);
    469			break;
    470		default:
    471			return -EINVAL;
    472		}
    473		/* ELM SYNDROME_VALID bit in SYNDROME_FRAGMENT_6[] needs
    474		 * to be saved for all BCH schemes*/
    475		regs->elm_syndrome_fragment_6[i] = elm_read_reg(info,
    476					ELM_SYNDROME_FRAGMENT_6 + offset);
    477	}
    478	return 0;
    479}
    480
    481/*
    482 * elm_context_restore
    483 * writes configurations saved duing power-down back into ELM registers
    484 */
    485static int elm_context_restore(struct elm_info *info)
    486{
    487	struct elm_registers *regs = &info->elm_regs;
    488	enum bch_ecc bch_type = info->bch_type;
    489	u32 offset = 0, i;
    490
    491	elm_write_reg(info, ELM_IRQENABLE,	 regs->elm_irqenable);
    492	elm_write_reg(info, ELM_SYSCONFIG,	 regs->elm_sysconfig);
    493	elm_write_reg(info, ELM_LOCATION_CONFIG, regs->elm_location_config);
    494	elm_write_reg(info, ELM_PAGE_CTRL,	 regs->elm_page_ctrl);
    495	for (i = 0; i < ERROR_VECTOR_MAX; i++) {
    496		offset = i * SYNDROME_FRAGMENT_REG_SIZE;
    497		switch (bch_type) {
    498		case BCH16_ECC:
    499			elm_write_reg(info, ELM_SYNDROME_FRAGMENT_6 + offset,
    500					regs->elm_syndrome_fragment_6[i]);
    501			elm_write_reg(info, ELM_SYNDROME_FRAGMENT_5 + offset,
    502					regs->elm_syndrome_fragment_5[i]);
    503			elm_write_reg(info, ELM_SYNDROME_FRAGMENT_4 + offset,
    504					regs->elm_syndrome_fragment_4[i]);
    505			fallthrough;
    506		case BCH8_ECC:
    507			elm_write_reg(info, ELM_SYNDROME_FRAGMENT_3 + offset,
    508					regs->elm_syndrome_fragment_3[i]);
    509			elm_write_reg(info, ELM_SYNDROME_FRAGMENT_2 + offset,
    510					regs->elm_syndrome_fragment_2[i]);
    511			fallthrough;
    512		case BCH4_ECC:
    513			elm_write_reg(info, ELM_SYNDROME_FRAGMENT_1 + offset,
    514					regs->elm_syndrome_fragment_1[i]);
    515			elm_write_reg(info, ELM_SYNDROME_FRAGMENT_0 + offset,
    516					regs->elm_syndrome_fragment_0[i]);
    517			break;
    518		default:
    519			return -EINVAL;
    520		}
    521		/* ELM_SYNDROME_VALID bit to be set in last to trigger FSM */
    522		elm_write_reg(info, ELM_SYNDROME_FRAGMENT_6 + offset,
    523					regs->elm_syndrome_fragment_6[i] &
    524							 ELM_SYNDROME_VALID);
    525	}
    526	return 0;
    527}
    528
    529static int elm_suspend(struct device *dev)
    530{
    531	struct elm_info *info = dev_get_drvdata(dev);
    532	elm_context_save(info);
    533	pm_runtime_put_sync(dev);
    534	return 0;
    535}
    536
    537static int elm_resume(struct device *dev)
    538{
    539	struct elm_info *info = dev_get_drvdata(dev);
    540	pm_runtime_get_sync(dev);
    541	elm_context_restore(info);
    542	return 0;
    543}
    544#endif
    545
    546static SIMPLE_DEV_PM_OPS(elm_pm_ops, elm_suspend, elm_resume);
    547
    548#ifdef CONFIG_OF
    549static const struct of_device_id elm_of_match[] = {
    550	{ .compatible = "ti,am3352-elm" },
    551	{ .compatible = "ti,am64-elm" },
    552	{},
    553};
    554MODULE_DEVICE_TABLE(of, elm_of_match);
    555#endif
    556
    557static struct platform_driver elm_driver = {
    558	.driver	= {
    559		.name	= DRIVER_NAME,
    560		.of_match_table = of_match_ptr(elm_of_match),
    561		.pm	= &elm_pm_ops,
    562	},
    563	.probe	= elm_probe,
    564	.remove	= elm_remove,
    565};
    566
    567module_platform_driver(elm_driver);
    568
    569MODULE_DESCRIPTION("ELM driver for BCH error correction");
    570MODULE_AUTHOR("Texas Instruments");
    571MODULE_ALIAS("platform:" DRIVER_NAME);
    572MODULE_LICENSE("GPL v2");