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

cobalt-omnitek.c (8444B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 *  Omnitek Scatter-Gather DMA Controller
      4 *
      5 *  Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates.
      6 *  All rights reserved.
      7 */
      8
      9#include <linux/string.h>
     10#include <linux/io.h>
     11#include <linux/pci_regs.h>
     12#include <linux/spinlock.h>
     13
     14#include "cobalt-driver.h"
     15#include "cobalt-omnitek.h"
     16
     17/* descriptor */
     18#define END_OF_CHAIN		(1 << 1)
     19#define INTERRUPT_ENABLE	(1 << 2)
     20#define WRITE_TO_PCI		(1 << 3)
     21#define READ_FROM_PCI		(0 << 3)
     22#define DESCRIPTOR_FLAG_MSK	(END_OF_CHAIN | INTERRUPT_ENABLE | WRITE_TO_PCI)
     23#define NEXT_ADRS_MSK		0xffffffe0
     24
     25/* control/status register */
     26#define ENABLE                  (1 << 0)
     27#define START                   (1 << 1)
     28#define ABORT                   (1 << 2)
     29#define DONE                    (1 << 4)
     30#define SG_INTERRUPT            (1 << 5)
     31#define EVENT_INTERRUPT         (1 << 6)
     32#define SCATTER_GATHER_MODE     (1 << 8)
     33#define DISABLE_VIDEO_RESYNC    (1 << 9)
     34#define EVENT_INTERRUPT_ENABLE  (1 << 10)
     35#define DIRECTIONAL_MSK         (3 << 16)
     36#define INPUT_ONLY              (0 << 16)
     37#define OUTPUT_ONLY             (1 << 16)
     38#define BIDIRECTIONAL           (2 << 16)
     39#define DMA_TYPE_MEMORY         (0 << 18)
     40#define DMA_TYPE_FIFO		(1 << 18)
     41
     42#define BASE			(cobalt->bar0)
     43#define CAPABILITY_HEADER	(BASE)
     44#define CAPABILITY_REGISTER	(BASE + 0x04)
     45#define PCI_64BIT		(1 << 8)
     46#define LOCAL_64BIT		(1 << 9)
     47#define INTERRUPT_STATUS	(BASE + 0x08)
     48#define PCI(c)			(BASE + 0x40 + ((c) * 0x40))
     49#define SIZE(c)			(BASE + 0x58 + ((c) * 0x40))
     50#define DESCRIPTOR(c)		(BASE + 0x50 + ((c) * 0x40))
     51#define CS_REG(c)		(BASE + 0x60 + ((c) * 0x40))
     52#define BYTES_TRANSFERRED(c)	(BASE + 0x64 + ((c) * 0x40))
     53
     54
     55static char *get_dma_direction(u32 status)
     56{
     57	switch (status & DIRECTIONAL_MSK) {
     58	case INPUT_ONLY: return "Input";
     59	case OUTPUT_ONLY: return "Output";
     60	case BIDIRECTIONAL: return "Bidirectional";
     61	}
     62	return "";
     63}
     64
     65static void show_dma_capability(struct cobalt *cobalt)
     66{
     67	u32 header = ioread32(CAPABILITY_HEADER);
     68	u32 capa = ioread32(CAPABILITY_REGISTER);
     69	u32 i;
     70
     71	cobalt_info("Omnitek DMA capability: ID 0x%02x Version 0x%02x Next 0x%x Size 0x%x\n",
     72		    header & 0xff, (header >> 8) & 0xff,
     73		    (header >> 16) & 0xffff, (capa >> 24) & 0xff);
     74
     75	switch ((capa >> 8) & 0x3) {
     76	case 0:
     77		cobalt_info("Omnitek DMA: 32 bits PCIe and Local\n");
     78		break;
     79	case 1:
     80		cobalt_info("Omnitek DMA: 64 bits PCIe, 32 bits Local\n");
     81		break;
     82	case 3:
     83		cobalt_info("Omnitek DMA: 64 bits PCIe and Local\n");
     84		break;
     85	}
     86
     87	for (i = 0;  i < (capa & 0xf);  i++) {
     88		u32 status = ioread32(CS_REG(i));
     89
     90		cobalt_info("Omnitek DMA channel #%d: %s %s\n", i,
     91			    status & DMA_TYPE_FIFO ? "FIFO" : "MEMORY",
     92			    get_dma_direction(status));
     93	}
     94}
     95
     96void omni_sg_dma_start(struct cobalt_stream *s, struct sg_dma_desc_info *desc)
     97{
     98	struct cobalt *cobalt = s->cobalt;
     99
    100	iowrite32((u32)((u64)desc->bus >> 32), DESCRIPTOR(s->dma_channel) + 4);
    101	iowrite32((u32)desc->bus & NEXT_ADRS_MSK, DESCRIPTOR(s->dma_channel));
    102	iowrite32(ENABLE | SCATTER_GATHER_MODE | START, CS_REG(s->dma_channel));
    103}
    104
    105bool is_dma_done(struct cobalt_stream *s)
    106{
    107	struct cobalt *cobalt = s->cobalt;
    108
    109	if (ioread32(CS_REG(s->dma_channel)) & DONE)
    110		return true;
    111
    112	return false;
    113}
    114
    115void omni_sg_dma_abort_channel(struct cobalt_stream *s)
    116{
    117	struct cobalt *cobalt = s->cobalt;
    118
    119	if (!is_dma_done(s))
    120		iowrite32(ABORT, CS_REG(s->dma_channel));
    121}
    122
    123int omni_sg_dma_init(struct cobalt *cobalt)
    124{
    125	u32 capa = ioread32(CAPABILITY_REGISTER);
    126	int i;
    127
    128	cobalt->first_fifo_channel = 0;
    129	cobalt->dma_channels = capa & 0xf;
    130	if (capa & PCI_64BIT)
    131		cobalt->pci_32_bit = false;
    132	else
    133		cobalt->pci_32_bit = true;
    134
    135	for (i = 0; i < cobalt->dma_channels; i++) {
    136		u32 status = ioread32(CS_REG(i));
    137		u32 ctrl = ioread32(CS_REG(i));
    138
    139		if (!(ctrl & DONE))
    140			iowrite32(ABORT, CS_REG(i));
    141
    142		if (!(status & DMA_TYPE_FIFO))
    143			cobalt->first_fifo_channel++;
    144	}
    145	show_dma_capability(cobalt);
    146	return 0;
    147}
    148
    149int descriptor_list_create(struct cobalt *cobalt,
    150		struct scatterlist *scatter_list, bool to_pci, unsigned sglen,
    151		unsigned size, unsigned width, unsigned stride,
    152		struct sg_dma_desc_info *desc)
    153{
    154	struct sg_dma_descriptor *d = (struct sg_dma_descriptor *)desc->virt;
    155	dma_addr_t next = desc->bus;
    156	unsigned offset = 0;
    157	unsigned copy_bytes = width;
    158	unsigned copied = 0;
    159	bool first = true;
    160
    161	/* Must be 4-byte aligned */
    162	WARN_ON(sg_dma_address(scatter_list) & 3);
    163	WARN_ON(size & 3);
    164	WARN_ON(next & 3);
    165	WARN_ON(stride & 3);
    166	WARN_ON(stride < width);
    167	if (width >= stride)
    168		copy_bytes = stride = size;
    169
    170	while (size) {
    171		dma_addr_t addr = sg_dma_address(scatter_list) + offset;
    172		unsigned bytes;
    173
    174		if (addr == 0)
    175			return -EFAULT;
    176		if (cobalt->pci_32_bit) {
    177			WARN_ON((u64)addr >> 32);
    178			if ((u64)addr >> 32)
    179				return -EFAULT;
    180		}
    181
    182		/* PCIe address */
    183		d->pci_l = addr & 0xffffffff;
    184		/* If dma_addr_t is 32 bits, then addr >> 32 is actually the
    185		   equivalent of addr >> 0 in gcc. So must cast to u64. */
    186		d->pci_h = (u64)addr >> 32;
    187
    188		/* Sync to start of streaming frame */
    189		d->local = 0;
    190		d->reserved0 = 0;
    191
    192		/* Transfer bytes */
    193		bytes = min(sg_dma_len(scatter_list) - offset,
    194				copy_bytes - copied);
    195
    196		if (first) {
    197			if (to_pci)
    198				d->local = 0x11111111;
    199			first = false;
    200			if (sglen == 1) {
    201				/* Make sure there are always at least two
    202				 * descriptors */
    203				d->bytes = (bytes / 2) & ~3;
    204				d->reserved1 = 0;
    205				size -= d->bytes;
    206				copied += d->bytes;
    207				offset += d->bytes;
    208				addr += d->bytes;
    209				next += sizeof(struct sg_dma_descriptor);
    210				d->next_h = (u32)((u64)next >> 32);
    211				d->next_l = (u32)next |
    212					(to_pci ? WRITE_TO_PCI : 0);
    213				bytes -= d->bytes;
    214				d++;
    215				/* PCIe address */
    216				d->pci_l = addr & 0xffffffff;
    217				/* If dma_addr_t is 32 bits, then addr >> 32
    218				 * is actually the equivalent of addr >> 0 in
    219				 * gcc. So must cast to u64. */
    220				d->pci_h = (u64)addr >> 32;
    221
    222				/* Sync to start of streaming frame */
    223				d->local = 0;
    224				d->reserved0 = 0;
    225			}
    226		}
    227
    228		d->bytes = bytes;
    229		d->reserved1 = 0;
    230		size -= bytes;
    231		copied += bytes;
    232		offset += bytes;
    233
    234		if (copied == copy_bytes) {
    235			while (copied < stride) {
    236				bytes = min(sg_dma_len(scatter_list) - offset,
    237						stride - copied);
    238				copied += bytes;
    239				offset += bytes;
    240				size -= bytes;
    241				if (sg_dma_len(scatter_list) == offset) {
    242					offset = 0;
    243					scatter_list = sg_next(scatter_list);
    244				}
    245			}
    246			copied = 0;
    247		} else {
    248			offset = 0;
    249			scatter_list = sg_next(scatter_list);
    250		}
    251
    252		/* Next descriptor + control bits */
    253		next += sizeof(struct sg_dma_descriptor);
    254		if (size == 0) {
    255			/* Loopback to the first descriptor */
    256			d->next_h = (u32)((u64)desc->bus >> 32);
    257			d->next_l = (u32)desc->bus |
    258				(to_pci ? WRITE_TO_PCI : 0) | INTERRUPT_ENABLE;
    259			if (!to_pci)
    260				d->local = 0x22222222;
    261			desc->last_desc_virt = d;
    262		} else {
    263			d->next_h = (u32)((u64)next >> 32);
    264			d->next_l = (u32)next | (to_pci ? WRITE_TO_PCI : 0);
    265		}
    266		d++;
    267	}
    268	return 0;
    269}
    270
    271void descriptor_list_chain(struct sg_dma_desc_info *this,
    272			   struct sg_dma_desc_info *next)
    273{
    274	struct sg_dma_descriptor *d = this->last_desc_virt;
    275	u32 direction = d->next_l & WRITE_TO_PCI;
    276
    277	if (next == NULL) {
    278		d->next_h = 0;
    279		d->next_l = direction | INTERRUPT_ENABLE | END_OF_CHAIN;
    280	} else {
    281		d->next_h = (u32)((u64)next->bus >> 32);
    282		d->next_l = (u32)next->bus | direction | INTERRUPT_ENABLE;
    283	}
    284}
    285
    286void *descriptor_list_allocate(struct sg_dma_desc_info *desc, size_t bytes)
    287{
    288	desc->size = bytes;
    289	desc->virt = dma_alloc_coherent(desc->dev, bytes,
    290					&desc->bus, GFP_KERNEL);
    291	return desc->virt;
    292}
    293
    294void descriptor_list_free(struct sg_dma_desc_info *desc)
    295{
    296	if (desc->virt)
    297		dma_free_coherent(desc->dev, desc->size,
    298				  desc->virt, desc->bus);
    299	desc->virt = NULL;
    300}
    301
    302void descriptor_list_interrupt_enable(struct sg_dma_desc_info *desc)
    303{
    304	struct sg_dma_descriptor *d = desc->last_desc_virt;
    305
    306	d->next_l |= INTERRUPT_ENABLE;
    307}
    308
    309void descriptor_list_interrupt_disable(struct sg_dma_desc_info *desc)
    310{
    311	struct sg_dma_descriptor *d = desc->last_desc_virt;
    312
    313	d->next_l &= ~INTERRUPT_ENABLE;
    314}
    315
    316void descriptor_list_loopback(struct sg_dma_desc_info *desc)
    317{
    318	struct sg_dma_descriptor *d = desc->last_desc_virt;
    319
    320	d->next_h = (u32)((u64)desc->bus >> 32);
    321	d->next_l = (u32)desc->bus | (d->next_l & DESCRIPTOR_FLAG_MSK);
    322}
    323
    324void descriptor_list_end_of_chain(struct sg_dma_desc_info *desc)
    325{
    326	struct sg_dma_descriptor *d = desc->last_desc_virt;
    327
    328	d->next_l |= END_OF_CHAIN;
    329}