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

meson_rdma.c (3521B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * Copyright (C) 2019 BayLibre, SAS
      4 * Author: Neil Armstrong <narmstrong@baylibre.com>
      5 */
      6
      7#include <linux/bitfield.h>
      8#include <linux/dma-mapping.h>
      9
     10#include "meson_drv.h"
     11#include "meson_registers.h"
     12#include "meson_rdma.h"
     13
     14/*
     15 * The VPU embeds a "Register DMA" that can write a sequence of registers
     16 * on the VPU AHB bus, either manually or triggered by an internal IRQ
     17 * event like VSYNC or a line input counter.
     18 * The initial implementation handles a single channel (over 8), triggered
     19 * by the VSYNC irq and does not handle the RDMA irq.
     20 */
     21
     22#define RDMA_DESC_SIZE	(sizeof(uint32_t) * 2)
     23
     24int meson_rdma_init(struct meson_drm *priv)
     25{
     26	if (!priv->rdma.addr) {
     27		/* Allocate a PAGE buffer */
     28		priv->rdma.addr =
     29			dma_alloc_coherent(priv->dev, SZ_4K,
     30					   &priv->rdma.addr_dma,
     31					   GFP_KERNEL);
     32		if (!priv->rdma.addr)
     33			return -ENOMEM;
     34	}
     35
     36	priv->rdma.offset = 0;
     37
     38	writel_relaxed(RDMA_CTRL_SW_RESET,
     39		       priv->io_base + _REG(RDMA_CTRL));
     40	writel_relaxed(RDMA_DEFAULT_CONFIG |
     41		       FIELD_PREP(RDMA_CTRL_AHB_WR_BURST, 3) |
     42		       FIELD_PREP(RDMA_CTRL_AHB_RD_BURST, 0),
     43		       priv->io_base + _REG(RDMA_CTRL));
     44
     45	return 0;
     46}
     47
     48void meson_rdma_free(struct meson_drm *priv)
     49{
     50	if (!priv->rdma.addr && !priv->rdma.addr_dma)
     51		return;
     52
     53	meson_rdma_stop(priv);
     54
     55	dma_free_coherent(priv->dev, SZ_4K,
     56			  priv->rdma.addr, priv->rdma.addr_dma);
     57
     58	priv->rdma.addr = NULL;
     59	priv->rdma.addr_dma = (dma_addr_t)0;
     60}
     61
     62void meson_rdma_setup(struct meson_drm *priv)
     63{
     64	/* Channel 1: Write Flag, No Address Increment */
     65	writel_bits_relaxed(RDMA_ACCESS_RW_FLAG_CHAN1 |
     66			    RDMA_ACCESS_ADDR_INC_CHAN1,
     67			    RDMA_ACCESS_RW_FLAG_CHAN1,
     68			    priv->io_base + _REG(RDMA_ACCESS_AUTO));
     69}
     70
     71void meson_rdma_stop(struct meson_drm *priv)
     72{
     73	writel_bits_relaxed(RDMA_IRQ_CLEAR_CHAN1,
     74			    RDMA_IRQ_CLEAR_CHAN1,
     75			    priv->io_base + _REG(RDMA_CTRL));
     76
     77	/* Stop Channel 1 */
     78	writel_bits_relaxed(RDMA_ACCESS_TRIGGER_CHAN1,
     79			    FIELD_PREP(RDMA_ACCESS_ADDR_INC_CHAN1,
     80				       RDMA_ACCESS_TRIGGER_STOP),
     81			    priv->io_base + _REG(RDMA_ACCESS_AUTO));
     82}
     83
     84void meson_rdma_reset(struct meson_drm *priv)
     85{
     86	meson_rdma_stop(priv);
     87
     88	priv->rdma.offset = 0;
     89}
     90
     91static void meson_rdma_writel(struct meson_drm *priv, uint32_t val,
     92			      uint32_t reg)
     93{
     94	if (priv->rdma.offset >= (SZ_4K / RDMA_DESC_SIZE)) {
     95		dev_warn_once(priv->dev, "%s: overflow\n", __func__);
     96		return;
     97	}
     98
     99	priv->rdma.addr[priv->rdma.offset++] = reg;
    100	priv->rdma.addr[priv->rdma.offset++] = val;
    101}
    102
    103/*
    104 * This will add the register to the RDMA buffer and write it to the
    105 * hardware at the same time.
    106 * When meson_rdma_flush is called, the RDMA will replay the register
    107 * writes in order.
    108 */
    109void meson_rdma_writel_sync(struct meson_drm *priv, uint32_t val, uint32_t reg)
    110{
    111	meson_rdma_writel(priv, val, reg);
    112
    113	writel_relaxed(val, priv->io_base + _REG(reg));
    114}
    115
    116void meson_rdma_flush(struct meson_drm *priv)
    117{
    118	meson_rdma_stop(priv);
    119
    120	/* Start of Channel 1 register writes buffer */
    121	writel(priv->rdma.addr_dma,
    122	       priv->io_base + _REG(RDMA_AHB_START_ADDR_1));
    123
    124	/* Last byte on Channel 1 register writes buffer */
    125	writel(priv->rdma.addr_dma + (priv->rdma.offset * RDMA_DESC_SIZE) - 1,
    126	       priv->io_base + _REG(RDMA_AHB_END_ADDR_1));
    127
    128	/* Trigger Channel 1 on VSYNC event */
    129	writel_bits_relaxed(RDMA_ACCESS_TRIGGER_CHAN1,
    130			    FIELD_PREP(RDMA_ACCESS_TRIGGER_CHAN1,
    131				       RDMA_ACCESS_TRIGGER_VSYNC),
    132			    priv->io_base + _REG(RDMA_ACCESS_AUTO));
    133
    134	priv->rdma.offset = 0;
    135}