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

dcss-ctxld.c (10228B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright 2019 NXP.
      4 */
      5
      6#include <linux/delay.h>
      7#include <linux/dma-mapping.h>
      8#include <linux/interrupt.h>
      9#include <linux/platform_device.h>
     10#include <linux/slab.h>
     11
     12#include "dcss-dev.h"
     13
     14#define DCSS_CTXLD_CONTROL_STATUS	0x0
     15#define   CTXLD_ENABLE			BIT(0)
     16#define   ARB_SEL			BIT(1)
     17#define   RD_ERR_EN			BIT(2)
     18#define   DB_COMP_EN			BIT(3)
     19#define   SB_HP_COMP_EN			BIT(4)
     20#define   SB_LP_COMP_EN			BIT(5)
     21#define   DB_PEND_SB_REC_EN		BIT(6)
     22#define   SB_PEND_DISP_ACTIVE_EN	BIT(7)
     23#define   AHB_ERR_EN			BIT(8)
     24#define   RD_ERR			BIT(16)
     25#define   DB_COMP			BIT(17)
     26#define   SB_HP_COMP			BIT(18)
     27#define   SB_LP_COMP			BIT(19)
     28#define   DB_PEND_SB_REC		BIT(20)
     29#define   SB_PEND_DISP_ACTIVE		BIT(21)
     30#define   AHB_ERR			BIT(22)
     31#define DCSS_CTXLD_DB_BASE_ADDR		0x10
     32#define DCSS_CTXLD_DB_COUNT		0x14
     33#define DCSS_CTXLD_SB_BASE_ADDR		0x18
     34#define DCSS_CTXLD_SB_COUNT		0x1C
     35#define   SB_HP_COUNT_POS		0
     36#define   SB_HP_COUNT_MASK		0xffff
     37#define   SB_LP_COUNT_POS		16
     38#define   SB_LP_COUNT_MASK		0xffff0000
     39#define DCSS_AHB_ERR_ADDR		0x20
     40
     41#define CTXLD_IRQ_COMPLETION		(DB_COMP | SB_HP_COMP | SB_LP_COMP)
     42#define CTXLD_IRQ_ERROR			(RD_ERR | DB_PEND_SB_REC | AHB_ERR)
     43
     44/* The following sizes are in context loader entries, 8 bytes each. */
     45#define CTXLD_DB_CTX_ENTRIES		1024	/* max 65536 */
     46#define CTXLD_SB_LP_CTX_ENTRIES		10240	/* max 65536 */
     47#define CTXLD_SB_HP_CTX_ENTRIES		20000	/* max 65536 */
     48#define CTXLD_SB_CTX_ENTRIES		(CTXLD_SB_LP_CTX_ENTRIES + \
     49					 CTXLD_SB_HP_CTX_ENTRIES)
     50
     51/* Sizes, in entries, of the DB, SB_HP and SB_LP context regions. */
     52static u16 dcss_ctxld_ctx_size[3] = {
     53	CTXLD_DB_CTX_ENTRIES,
     54	CTXLD_SB_HP_CTX_ENTRIES,
     55	CTXLD_SB_LP_CTX_ENTRIES
     56};
     57
     58/* this represents an entry in the context loader map */
     59struct dcss_ctxld_item {
     60	u32 val;
     61	u32 ofs;
     62};
     63
     64#define CTX_ITEM_SIZE			sizeof(struct dcss_ctxld_item)
     65
     66struct dcss_ctxld {
     67	struct device *dev;
     68	void __iomem *ctxld_reg;
     69	int irq;
     70	bool irq_en;
     71
     72	struct dcss_ctxld_item *db[2];
     73	struct dcss_ctxld_item *sb_hp[2];
     74	struct dcss_ctxld_item *sb_lp[2];
     75
     76	dma_addr_t db_paddr[2];
     77	dma_addr_t sb_paddr[2];
     78
     79	u16 ctx_size[2][3]; /* holds the sizes of DB, SB_HP and SB_LP ctx */
     80	u8 current_ctx;
     81
     82	bool in_use;
     83	bool armed;
     84
     85	spinlock_t lock; /* protects concurent access to private data */
     86};
     87
     88static irqreturn_t dcss_ctxld_irq_handler(int irq, void *data)
     89{
     90	struct dcss_ctxld *ctxld = data;
     91	struct dcss_dev *dcss = dcss_drv_dev_to_dcss(ctxld->dev);
     92	u32 irq_status;
     93
     94	irq_status = dcss_readl(ctxld->ctxld_reg + DCSS_CTXLD_CONTROL_STATUS);
     95
     96	if (irq_status & CTXLD_IRQ_COMPLETION &&
     97	    !(irq_status & CTXLD_ENABLE) && ctxld->in_use) {
     98		ctxld->in_use = false;
     99
    100		if (dcss && dcss->disable_callback)
    101			dcss->disable_callback(dcss);
    102	} else if (irq_status & CTXLD_IRQ_ERROR) {
    103		/*
    104		 * Except for throwing an error message and clearing the status
    105		 * register, there's not much we can do here.
    106		 */
    107		dev_err(ctxld->dev, "ctxld: error encountered: %08x\n",
    108			irq_status);
    109		dev_err(ctxld->dev, "ctxld: db=%d, sb_hp=%d, sb_lp=%d\n",
    110			ctxld->ctx_size[ctxld->current_ctx ^ 1][CTX_DB],
    111			ctxld->ctx_size[ctxld->current_ctx ^ 1][CTX_SB_HP],
    112			ctxld->ctx_size[ctxld->current_ctx ^ 1][CTX_SB_LP]);
    113	}
    114
    115	dcss_clr(irq_status & (CTXLD_IRQ_ERROR | CTXLD_IRQ_COMPLETION),
    116		 ctxld->ctxld_reg + DCSS_CTXLD_CONTROL_STATUS);
    117
    118	return IRQ_HANDLED;
    119}
    120
    121static int dcss_ctxld_irq_config(struct dcss_ctxld *ctxld,
    122				 struct platform_device *pdev)
    123{
    124	int ret;
    125
    126	ctxld->irq = platform_get_irq_byname(pdev, "ctxld");
    127	if (ctxld->irq < 0)
    128		return ctxld->irq;
    129
    130	ret = request_irq(ctxld->irq, dcss_ctxld_irq_handler,
    131			  0, "dcss_ctxld", ctxld);
    132	if (ret) {
    133		dev_err(ctxld->dev, "ctxld: irq request failed.\n");
    134		return ret;
    135	}
    136
    137	ctxld->irq_en = true;
    138
    139	return 0;
    140}
    141
    142static void dcss_ctxld_hw_cfg(struct dcss_ctxld *ctxld)
    143{
    144	dcss_writel(RD_ERR_EN | SB_HP_COMP_EN |
    145		    DB_PEND_SB_REC_EN | AHB_ERR_EN | RD_ERR | AHB_ERR,
    146		    ctxld->ctxld_reg + DCSS_CTXLD_CONTROL_STATUS);
    147}
    148
    149static void dcss_ctxld_free_ctx(struct dcss_ctxld *ctxld)
    150{
    151	struct dcss_ctxld_item *ctx;
    152	int i;
    153
    154	for (i = 0; i < 2; i++) {
    155		if (ctxld->db[i]) {
    156			dma_free_coherent(ctxld->dev,
    157					  CTXLD_DB_CTX_ENTRIES * sizeof(*ctx),
    158					  ctxld->db[i], ctxld->db_paddr[i]);
    159			ctxld->db[i] = NULL;
    160			ctxld->db_paddr[i] = 0;
    161		}
    162
    163		if (ctxld->sb_hp[i]) {
    164			dma_free_coherent(ctxld->dev,
    165					  CTXLD_SB_CTX_ENTRIES * sizeof(*ctx),
    166					  ctxld->sb_hp[i], ctxld->sb_paddr[i]);
    167			ctxld->sb_hp[i] = NULL;
    168			ctxld->sb_paddr[i] = 0;
    169		}
    170	}
    171}
    172
    173static int dcss_ctxld_alloc_ctx(struct dcss_ctxld *ctxld)
    174{
    175	struct dcss_ctxld_item *ctx;
    176	int i;
    177
    178	for (i = 0; i < 2; i++) {
    179		ctx = dma_alloc_coherent(ctxld->dev,
    180					 CTXLD_DB_CTX_ENTRIES * sizeof(*ctx),
    181					 &ctxld->db_paddr[i], GFP_KERNEL);
    182		if (!ctx)
    183			return -ENOMEM;
    184
    185		ctxld->db[i] = ctx;
    186
    187		ctx = dma_alloc_coherent(ctxld->dev,
    188					 CTXLD_SB_CTX_ENTRIES * sizeof(*ctx),
    189					 &ctxld->sb_paddr[i], GFP_KERNEL);
    190		if (!ctx)
    191			return -ENOMEM;
    192
    193		ctxld->sb_hp[i] = ctx;
    194		ctxld->sb_lp[i] = ctx + CTXLD_SB_HP_CTX_ENTRIES;
    195	}
    196
    197	return 0;
    198}
    199
    200int dcss_ctxld_init(struct dcss_dev *dcss, unsigned long ctxld_base)
    201{
    202	struct dcss_ctxld *ctxld;
    203	int ret;
    204
    205	ctxld = kzalloc(sizeof(*ctxld), GFP_KERNEL);
    206	if (!ctxld)
    207		return -ENOMEM;
    208
    209	dcss->ctxld = ctxld;
    210	ctxld->dev = dcss->dev;
    211
    212	spin_lock_init(&ctxld->lock);
    213
    214	ret = dcss_ctxld_alloc_ctx(ctxld);
    215	if (ret) {
    216		dev_err(dcss->dev, "ctxld: cannot allocate context memory.\n");
    217		goto err;
    218	}
    219
    220	ctxld->ctxld_reg = ioremap(ctxld_base, SZ_4K);
    221	if (!ctxld->ctxld_reg) {
    222		dev_err(dcss->dev, "ctxld: unable to remap ctxld base\n");
    223		ret = -ENOMEM;
    224		goto err;
    225	}
    226
    227	ret = dcss_ctxld_irq_config(ctxld, to_platform_device(dcss->dev));
    228	if (ret)
    229		goto err_irq;
    230
    231	dcss_ctxld_hw_cfg(ctxld);
    232
    233	return 0;
    234
    235err_irq:
    236	iounmap(ctxld->ctxld_reg);
    237
    238err:
    239	dcss_ctxld_free_ctx(ctxld);
    240	kfree(ctxld);
    241
    242	return ret;
    243}
    244
    245void dcss_ctxld_exit(struct dcss_ctxld *ctxld)
    246{
    247	free_irq(ctxld->irq, ctxld);
    248
    249	if (ctxld->ctxld_reg)
    250		iounmap(ctxld->ctxld_reg);
    251
    252	dcss_ctxld_free_ctx(ctxld);
    253	kfree(ctxld);
    254}
    255
    256static int dcss_ctxld_enable_locked(struct dcss_ctxld *ctxld)
    257{
    258	int curr_ctx = ctxld->current_ctx;
    259	u32 db_base, sb_base, sb_count;
    260	u32 sb_hp_cnt, sb_lp_cnt, db_cnt;
    261	struct dcss_dev *dcss = dcss_drv_dev_to_dcss(ctxld->dev);
    262
    263	if (!dcss)
    264		return 0;
    265
    266	dcss_dpr_write_sysctrl(dcss->dpr);
    267
    268	dcss_scaler_write_sclctrl(dcss->scaler);
    269
    270	sb_hp_cnt = ctxld->ctx_size[curr_ctx][CTX_SB_HP];
    271	sb_lp_cnt = ctxld->ctx_size[curr_ctx][CTX_SB_LP];
    272	db_cnt = ctxld->ctx_size[curr_ctx][CTX_DB];
    273
    274	/* make sure SB_LP context area comes after SB_HP */
    275	if (sb_lp_cnt &&
    276	    ctxld->sb_lp[curr_ctx] != ctxld->sb_hp[curr_ctx] + sb_hp_cnt) {
    277		struct dcss_ctxld_item *sb_lp_adjusted;
    278
    279		sb_lp_adjusted = ctxld->sb_hp[curr_ctx] + sb_hp_cnt;
    280
    281		memcpy(sb_lp_adjusted, ctxld->sb_lp[curr_ctx],
    282		       sb_lp_cnt * CTX_ITEM_SIZE);
    283	}
    284
    285	db_base = db_cnt ? ctxld->db_paddr[curr_ctx] : 0;
    286
    287	dcss_writel(db_base, ctxld->ctxld_reg + DCSS_CTXLD_DB_BASE_ADDR);
    288	dcss_writel(db_cnt, ctxld->ctxld_reg + DCSS_CTXLD_DB_COUNT);
    289
    290	if (sb_hp_cnt)
    291		sb_count = ((sb_hp_cnt << SB_HP_COUNT_POS) & SB_HP_COUNT_MASK) |
    292			   ((sb_lp_cnt << SB_LP_COUNT_POS) & SB_LP_COUNT_MASK);
    293	else
    294		sb_count = (sb_lp_cnt << SB_HP_COUNT_POS) & SB_HP_COUNT_MASK;
    295
    296	sb_base = sb_count ? ctxld->sb_paddr[curr_ctx] : 0;
    297
    298	dcss_writel(sb_base, ctxld->ctxld_reg + DCSS_CTXLD_SB_BASE_ADDR);
    299	dcss_writel(sb_count, ctxld->ctxld_reg + DCSS_CTXLD_SB_COUNT);
    300
    301	/* enable the context loader */
    302	dcss_set(CTXLD_ENABLE, ctxld->ctxld_reg + DCSS_CTXLD_CONTROL_STATUS);
    303
    304	ctxld->in_use = true;
    305
    306	/*
    307	 * Toggle the current context to the alternate one so that any updates
    308	 * in the modules' settings take place there.
    309	 */
    310	ctxld->current_ctx ^= 1;
    311
    312	ctxld->ctx_size[ctxld->current_ctx][CTX_DB] = 0;
    313	ctxld->ctx_size[ctxld->current_ctx][CTX_SB_HP] = 0;
    314	ctxld->ctx_size[ctxld->current_ctx][CTX_SB_LP] = 0;
    315
    316	return 0;
    317}
    318
    319int dcss_ctxld_enable(struct dcss_ctxld *ctxld)
    320{
    321	spin_lock_irq(&ctxld->lock);
    322	ctxld->armed = true;
    323	spin_unlock_irq(&ctxld->lock);
    324
    325	return 0;
    326}
    327
    328void dcss_ctxld_kick(struct dcss_ctxld *ctxld)
    329{
    330	unsigned long flags;
    331
    332	spin_lock_irqsave(&ctxld->lock, flags);
    333	if (ctxld->armed && !ctxld->in_use) {
    334		ctxld->armed = false;
    335		dcss_ctxld_enable_locked(ctxld);
    336	}
    337	spin_unlock_irqrestore(&ctxld->lock, flags);
    338}
    339
    340void dcss_ctxld_write_irqsafe(struct dcss_ctxld *ctxld, u32 ctx_id, u32 val,
    341			      u32 reg_ofs)
    342{
    343	int curr_ctx = ctxld->current_ctx;
    344	struct dcss_ctxld_item *ctx[] = {
    345		[CTX_DB] = ctxld->db[curr_ctx],
    346		[CTX_SB_HP] = ctxld->sb_hp[curr_ctx],
    347		[CTX_SB_LP] = ctxld->sb_lp[curr_ctx]
    348	};
    349	int item_idx = ctxld->ctx_size[curr_ctx][ctx_id];
    350
    351	if (item_idx + 1 > dcss_ctxld_ctx_size[ctx_id]) {
    352		WARN_ON(1);
    353		return;
    354	}
    355
    356	ctx[ctx_id][item_idx].val = val;
    357	ctx[ctx_id][item_idx].ofs = reg_ofs;
    358	ctxld->ctx_size[curr_ctx][ctx_id] += 1;
    359}
    360
    361void dcss_ctxld_write(struct dcss_ctxld *ctxld, u32 ctx_id,
    362		      u32 val, u32 reg_ofs)
    363{
    364	spin_lock_irq(&ctxld->lock);
    365	dcss_ctxld_write_irqsafe(ctxld, ctx_id, val, reg_ofs);
    366	spin_unlock_irq(&ctxld->lock);
    367}
    368
    369bool dcss_ctxld_is_flushed(struct dcss_ctxld *ctxld)
    370{
    371	return ctxld->ctx_size[ctxld->current_ctx][CTX_DB] == 0 &&
    372		ctxld->ctx_size[ctxld->current_ctx][CTX_SB_HP] == 0 &&
    373		ctxld->ctx_size[ctxld->current_ctx][CTX_SB_LP] == 0;
    374}
    375
    376int dcss_ctxld_resume(struct dcss_ctxld *ctxld)
    377{
    378	dcss_ctxld_hw_cfg(ctxld);
    379
    380	if (!ctxld->irq_en) {
    381		enable_irq(ctxld->irq);
    382		ctxld->irq_en = true;
    383	}
    384
    385	return 0;
    386}
    387
    388int dcss_ctxld_suspend(struct dcss_ctxld *ctxld)
    389{
    390	int ret = 0;
    391	unsigned long timeout = jiffies + msecs_to_jiffies(500);
    392
    393	if (!dcss_ctxld_is_flushed(ctxld)) {
    394		dcss_ctxld_kick(ctxld);
    395
    396		while (!time_after(jiffies, timeout) && ctxld->in_use)
    397			msleep(20);
    398
    399		if (time_after(jiffies, timeout))
    400			return -ETIMEDOUT;
    401	}
    402
    403	spin_lock_irq(&ctxld->lock);
    404
    405	if (ctxld->irq_en) {
    406		disable_irq_nosync(ctxld->irq);
    407		ctxld->irq_en = false;
    408	}
    409
    410	/* reset context region and sizes */
    411	ctxld->current_ctx = 0;
    412	ctxld->ctx_size[0][CTX_DB] = 0;
    413	ctxld->ctx_size[0][CTX_SB_HP] = 0;
    414	ctxld->ctx_size[0][CTX_SB_LP] = 0;
    415
    416	spin_unlock_irq(&ctxld->lock);
    417
    418	return ret;
    419}
    420
    421void dcss_ctxld_assert_locked(struct dcss_ctxld *ctxld)
    422{
    423	lockdep_assert_held(&ctxld->lock);
    424}