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

jz4725b_bch.c (7319B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * JZ4725B BCH controller driver
      4 *
      5 * Copyright (C) 2019 Paul Cercueil <paul@crapouillou.net>
      6 *
      7 * Based on jz4780_bch.c
      8 */
      9
     10#include <linux/bitops.h>
     11#include <linux/device.h>
     12#include <linux/io.h>
     13#include <linux/iopoll.h>
     14#include <linux/module.h>
     15#include <linux/mutex.h>
     16#include <linux/of_platform.h>
     17#include <linux/platform_device.h>
     18
     19#include "ingenic_ecc.h"
     20
     21#define BCH_BHCR			0x0
     22#define BCH_BHCSR			0x4
     23#define BCH_BHCCR			0x8
     24#define BCH_BHCNT			0xc
     25#define BCH_BHDR			0x10
     26#define BCH_BHPAR0			0x14
     27#define BCH_BHERR0			0x28
     28#define BCH_BHINT			0x24
     29#define BCH_BHINTES			0x3c
     30#define BCH_BHINTEC			0x40
     31#define BCH_BHINTE			0x38
     32
     33#define BCH_BHCR_ENCE			BIT(3)
     34#define BCH_BHCR_BSEL			BIT(2)
     35#define BCH_BHCR_INIT			BIT(1)
     36#define BCH_BHCR_BCHE			BIT(0)
     37
     38#define BCH_BHCNT_DEC_COUNT_SHIFT	16
     39#define BCH_BHCNT_DEC_COUNT_MASK	(0x3ff << BCH_BHCNT_DEC_COUNT_SHIFT)
     40#define BCH_BHCNT_ENC_COUNT_SHIFT	0
     41#define BCH_BHCNT_ENC_COUNT_MASK	(0x3ff << BCH_BHCNT_ENC_COUNT_SHIFT)
     42
     43#define BCH_BHERR_INDEX0_SHIFT		0
     44#define BCH_BHERR_INDEX0_MASK		(0x1fff << BCH_BHERR_INDEX0_SHIFT)
     45#define BCH_BHERR_INDEX1_SHIFT		16
     46#define BCH_BHERR_INDEX1_MASK		(0x1fff << BCH_BHERR_INDEX1_SHIFT)
     47
     48#define BCH_BHINT_ERRC_SHIFT		28
     49#define BCH_BHINT_ERRC_MASK		(0xf << BCH_BHINT_ERRC_SHIFT)
     50#define BCH_BHINT_TERRC_SHIFT		16
     51#define BCH_BHINT_TERRC_MASK		(0x7f << BCH_BHINT_TERRC_SHIFT)
     52#define BCH_BHINT_ALL_0			BIT(5)
     53#define BCH_BHINT_ALL_F			BIT(4)
     54#define BCH_BHINT_DECF			BIT(3)
     55#define BCH_BHINT_ENCF			BIT(2)
     56#define BCH_BHINT_UNCOR			BIT(1)
     57#define BCH_BHINT_ERR			BIT(0)
     58
     59/* Timeout for BCH calculation/correction. */
     60#define BCH_TIMEOUT_US			100000
     61
     62static inline void jz4725b_bch_config_set(struct ingenic_ecc *bch, u32 cfg)
     63{
     64	writel(cfg, bch->base + BCH_BHCSR);
     65}
     66
     67static inline void jz4725b_bch_config_clear(struct ingenic_ecc *bch, u32 cfg)
     68{
     69	writel(cfg, bch->base + BCH_BHCCR);
     70}
     71
     72static int jz4725b_bch_reset(struct ingenic_ecc *bch,
     73			     struct ingenic_ecc_params *params, bool calc_ecc)
     74{
     75	u32 reg, max_value;
     76
     77	/* Clear interrupt status. */
     78	writel(readl(bch->base + BCH_BHINT), bch->base + BCH_BHINT);
     79
     80	/* Initialise and enable BCH. */
     81	jz4725b_bch_config_clear(bch, 0x1f);
     82	jz4725b_bch_config_set(bch, BCH_BHCR_BCHE);
     83
     84	if (params->strength == 8)
     85		jz4725b_bch_config_set(bch, BCH_BHCR_BSEL);
     86	else
     87		jz4725b_bch_config_clear(bch, BCH_BHCR_BSEL);
     88
     89	if (calc_ecc) /* calculate ECC from data */
     90		jz4725b_bch_config_set(bch, BCH_BHCR_ENCE);
     91	else /* correct data from ECC */
     92		jz4725b_bch_config_clear(bch, BCH_BHCR_ENCE);
     93
     94	jz4725b_bch_config_set(bch, BCH_BHCR_INIT);
     95
     96	max_value = BCH_BHCNT_ENC_COUNT_MASK >> BCH_BHCNT_ENC_COUNT_SHIFT;
     97	if (params->size > max_value)
     98		return -EINVAL;
     99
    100	max_value = BCH_BHCNT_DEC_COUNT_MASK >> BCH_BHCNT_DEC_COUNT_SHIFT;
    101	if (params->size + params->bytes > max_value)
    102		return -EINVAL;
    103
    104	/* Set up BCH count register. */
    105	reg = params->size << BCH_BHCNT_ENC_COUNT_SHIFT;
    106	reg |= (params->size + params->bytes) << BCH_BHCNT_DEC_COUNT_SHIFT;
    107	writel(reg, bch->base + BCH_BHCNT);
    108
    109	return 0;
    110}
    111
    112static void jz4725b_bch_disable(struct ingenic_ecc *bch)
    113{
    114	/* Clear interrupts */
    115	writel(readl(bch->base + BCH_BHINT), bch->base + BCH_BHINT);
    116
    117	/* Disable the hardware */
    118	jz4725b_bch_config_clear(bch, BCH_BHCR_BCHE);
    119}
    120
    121static void jz4725b_bch_write_data(struct ingenic_ecc *bch, const u8 *buf,
    122				   size_t size)
    123{
    124	while (size--)
    125		writeb(*buf++, bch->base + BCH_BHDR);
    126}
    127
    128static void jz4725b_bch_read_parity(struct ingenic_ecc *bch, u8 *buf,
    129				    size_t size)
    130{
    131	size_t size32 = size / sizeof(u32);
    132	size_t size8 = size % sizeof(u32);
    133	u32 *dest32;
    134	u8 *dest8;
    135	u32 val, offset = 0;
    136
    137	dest32 = (u32 *)buf;
    138	while (size32--) {
    139		*dest32++ = readl_relaxed(bch->base + BCH_BHPAR0 + offset);
    140		offset += sizeof(u32);
    141	}
    142
    143	dest8 = (u8 *)dest32;
    144	val = readl_relaxed(bch->base + BCH_BHPAR0 + offset);
    145	switch (size8) {
    146	case 3:
    147		dest8[2] = (val >> 16) & 0xff;
    148		fallthrough;
    149	case 2:
    150		dest8[1] = (val >> 8) & 0xff;
    151		fallthrough;
    152	case 1:
    153		dest8[0] = val & 0xff;
    154		break;
    155	}
    156}
    157
    158static int jz4725b_bch_wait_complete(struct ingenic_ecc *bch, unsigned int irq,
    159				     u32 *status)
    160{
    161	u32 reg;
    162	int ret;
    163
    164	/*
    165	 * While we could use interrupts here and sleep until the operation
    166	 * completes, the controller works fairly quickly (usually a few
    167	 * microseconds) and so the overhead of sleeping until we get an
    168	 * interrupt quite noticeably decreases performance.
    169	 */
    170	ret = readl_relaxed_poll_timeout(bch->base + BCH_BHINT, reg,
    171					 reg & irq, 0, BCH_TIMEOUT_US);
    172	if (ret)
    173		return ret;
    174
    175	if (status)
    176		*status = reg;
    177
    178	writel(reg, bch->base + BCH_BHINT);
    179
    180	return 0;
    181}
    182
    183static int jz4725b_calculate(struct ingenic_ecc *bch,
    184			     struct ingenic_ecc_params *params,
    185			     const u8 *buf, u8 *ecc_code)
    186{
    187	int ret;
    188
    189	mutex_lock(&bch->lock);
    190
    191	ret = jz4725b_bch_reset(bch, params, true);
    192	if (ret) {
    193		dev_err(bch->dev, "Unable to init BCH with given parameters\n");
    194		goto out_disable;
    195	}
    196
    197	jz4725b_bch_write_data(bch, buf, params->size);
    198
    199	ret = jz4725b_bch_wait_complete(bch, BCH_BHINT_ENCF, NULL);
    200	if (ret) {
    201		dev_err(bch->dev, "timed out while calculating ECC\n");
    202		goto out_disable;
    203	}
    204
    205	jz4725b_bch_read_parity(bch, ecc_code, params->bytes);
    206
    207out_disable:
    208	jz4725b_bch_disable(bch);
    209	mutex_unlock(&bch->lock);
    210
    211	return ret;
    212}
    213
    214static int jz4725b_correct(struct ingenic_ecc *bch,
    215			   struct ingenic_ecc_params *params,
    216			   u8 *buf, u8 *ecc_code)
    217{
    218	u32 reg, errors, bit;
    219	unsigned int i;
    220	int ret;
    221
    222	mutex_lock(&bch->lock);
    223
    224	ret = jz4725b_bch_reset(bch, params, false);
    225	if (ret) {
    226		dev_err(bch->dev, "Unable to init BCH with given parameters\n");
    227		goto out;
    228	}
    229
    230	jz4725b_bch_write_data(bch, buf, params->size);
    231	jz4725b_bch_write_data(bch, ecc_code, params->bytes);
    232
    233	ret = jz4725b_bch_wait_complete(bch, BCH_BHINT_DECF, &reg);
    234	if (ret) {
    235		dev_err(bch->dev, "timed out while correcting data\n");
    236		goto out;
    237	}
    238
    239	if (reg & (BCH_BHINT_ALL_F | BCH_BHINT_ALL_0)) {
    240		/* Data and ECC is all 0xff or 0x00 - nothing to correct */
    241		ret = 0;
    242		goto out;
    243	}
    244
    245	if (reg & BCH_BHINT_UNCOR) {
    246		/* Uncorrectable ECC error */
    247		ret = -EBADMSG;
    248		goto out;
    249	}
    250
    251	errors = (reg & BCH_BHINT_ERRC_MASK) >> BCH_BHINT_ERRC_SHIFT;
    252
    253	/* Correct any detected errors. */
    254	for (i = 0; i < errors; i++) {
    255		if (i & 1) {
    256			bit = (reg & BCH_BHERR_INDEX1_MASK) >> BCH_BHERR_INDEX1_SHIFT;
    257		} else {
    258			reg = readl(bch->base + BCH_BHERR0 + (i * 4));
    259			bit = (reg & BCH_BHERR_INDEX0_MASK) >> BCH_BHERR_INDEX0_SHIFT;
    260		}
    261
    262		buf[(bit >> 3)] ^= BIT(bit & 0x7);
    263	}
    264
    265out:
    266	jz4725b_bch_disable(bch);
    267	mutex_unlock(&bch->lock);
    268
    269	return ret;
    270}
    271
    272static const struct ingenic_ecc_ops jz4725b_bch_ops = {
    273	.disable = jz4725b_bch_disable,
    274	.calculate = jz4725b_calculate,
    275	.correct = jz4725b_correct,
    276};
    277
    278static const struct of_device_id jz4725b_bch_dt_match[] = {
    279	{ .compatible = "ingenic,jz4725b-bch", .data = &jz4725b_bch_ops },
    280	{},
    281};
    282MODULE_DEVICE_TABLE(of, jz4725b_bch_dt_match);
    283
    284static struct platform_driver jz4725b_bch_driver = {
    285	.probe		= ingenic_ecc_probe,
    286	.driver	= {
    287		.name	= "jz4725b-bch",
    288		.of_match_table = jz4725b_bch_dt_match,
    289	},
    290};
    291module_platform_driver(jz4725b_bch_driver);
    292
    293MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
    294MODULE_DESCRIPTION("Ingenic JZ4725B BCH controller driver");
    295MODULE_LICENSE("GPL v2");