162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Omnitek Scatter-Gather DMA Controller 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates. 662306a36Sopenharmony_ci * All rights reserved. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/string.h> 1062306a36Sopenharmony_ci#include <linux/io.h> 1162306a36Sopenharmony_ci#include <linux/pci_regs.h> 1262306a36Sopenharmony_ci#include <linux/spinlock.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "cobalt-driver.h" 1562306a36Sopenharmony_ci#include "cobalt-omnitek.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* descriptor */ 1862306a36Sopenharmony_ci#define END_OF_CHAIN (1 << 1) 1962306a36Sopenharmony_ci#define INTERRUPT_ENABLE (1 << 2) 2062306a36Sopenharmony_ci#define WRITE_TO_PCI (1 << 3) 2162306a36Sopenharmony_ci#define READ_FROM_PCI (0 << 3) 2262306a36Sopenharmony_ci#define DESCRIPTOR_FLAG_MSK (END_OF_CHAIN | INTERRUPT_ENABLE | WRITE_TO_PCI) 2362306a36Sopenharmony_ci#define NEXT_ADRS_MSK 0xffffffe0 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* control/status register */ 2662306a36Sopenharmony_ci#define ENABLE (1 << 0) 2762306a36Sopenharmony_ci#define START (1 << 1) 2862306a36Sopenharmony_ci#define ABORT (1 << 2) 2962306a36Sopenharmony_ci#define DONE (1 << 4) 3062306a36Sopenharmony_ci#define SG_INTERRUPT (1 << 5) 3162306a36Sopenharmony_ci#define EVENT_INTERRUPT (1 << 6) 3262306a36Sopenharmony_ci#define SCATTER_GATHER_MODE (1 << 8) 3362306a36Sopenharmony_ci#define DISABLE_VIDEO_RESYNC (1 << 9) 3462306a36Sopenharmony_ci#define EVENT_INTERRUPT_ENABLE (1 << 10) 3562306a36Sopenharmony_ci#define DIRECTIONAL_MSK (3 << 16) 3662306a36Sopenharmony_ci#define INPUT_ONLY (0 << 16) 3762306a36Sopenharmony_ci#define OUTPUT_ONLY (1 << 16) 3862306a36Sopenharmony_ci#define BIDIRECTIONAL (2 << 16) 3962306a36Sopenharmony_ci#define DMA_TYPE_MEMORY (0 << 18) 4062306a36Sopenharmony_ci#define DMA_TYPE_FIFO (1 << 18) 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define BASE (cobalt->bar0) 4362306a36Sopenharmony_ci#define CAPABILITY_HEADER (BASE) 4462306a36Sopenharmony_ci#define CAPABILITY_REGISTER (BASE + 0x04) 4562306a36Sopenharmony_ci#define PCI_64BIT (1 << 8) 4662306a36Sopenharmony_ci#define LOCAL_64BIT (1 << 9) 4762306a36Sopenharmony_ci#define INTERRUPT_STATUS (BASE + 0x08) 4862306a36Sopenharmony_ci#define PCI(c) (BASE + 0x40 + ((c) * 0x40)) 4962306a36Sopenharmony_ci#define SIZE(c) (BASE + 0x58 + ((c) * 0x40)) 5062306a36Sopenharmony_ci#define DESCRIPTOR(c) (BASE + 0x50 + ((c) * 0x40)) 5162306a36Sopenharmony_ci#define CS_REG(c) (BASE + 0x60 + ((c) * 0x40)) 5262306a36Sopenharmony_ci#define BYTES_TRANSFERRED(c) (BASE + 0x64 + ((c) * 0x40)) 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic char *get_dma_direction(u32 status) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci switch (status & DIRECTIONAL_MSK) { 5862306a36Sopenharmony_ci case INPUT_ONLY: return "Input"; 5962306a36Sopenharmony_ci case OUTPUT_ONLY: return "Output"; 6062306a36Sopenharmony_ci case BIDIRECTIONAL: return "Bidirectional"; 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci return ""; 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic void show_dma_capability(struct cobalt *cobalt) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci u32 header = ioread32(CAPABILITY_HEADER); 6862306a36Sopenharmony_ci u32 capa = ioread32(CAPABILITY_REGISTER); 6962306a36Sopenharmony_ci u32 i; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci cobalt_info("Omnitek DMA capability: ID 0x%02x Version 0x%02x Next 0x%x Size 0x%x\n", 7262306a36Sopenharmony_ci header & 0xff, (header >> 8) & 0xff, 7362306a36Sopenharmony_ci (header >> 16) & 0xffff, (capa >> 24) & 0xff); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci switch ((capa >> 8) & 0x3) { 7662306a36Sopenharmony_ci case 0: 7762306a36Sopenharmony_ci cobalt_info("Omnitek DMA: 32 bits PCIe and Local\n"); 7862306a36Sopenharmony_ci break; 7962306a36Sopenharmony_ci case 1: 8062306a36Sopenharmony_ci cobalt_info("Omnitek DMA: 64 bits PCIe, 32 bits Local\n"); 8162306a36Sopenharmony_ci break; 8262306a36Sopenharmony_ci case 3: 8362306a36Sopenharmony_ci cobalt_info("Omnitek DMA: 64 bits PCIe and Local\n"); 8462306a36Sopenharmony_ci break; 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci for (i = 0; i < (capa & 0xf); i++) { 8862306a36Sopenharmony_ci u32 status = ioread32(CS_REG(i)); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci cobalt_info("Omnitek DMA channel #%d: %s %s\n", i, 9162306a36Sopenharmony_ci status & DMA_TYPE_FIFO ? "FIFO" : "MEMORY", 9262306a36Sopenharmony_ci get_dma_direction(status)); 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_civoid omni_sg_dma_start(struct cobalt_stream *s, struct sg_dma_desc_info *desc) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci struct cobalt *cobalt = s->cobalt; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci iowrite32((u32)((u64)desc->bus >> 32), DESCRIPTOR(s->dma_channel) + 4); 10162306a36Sopenharmony_ci iowrite32((u32)desc->bus & NEXT_ADRS_MSK, DESCRIPTOR(s->dma_channel)); 10262306a36Sopenharmony_ci iowrite32(ENABLE | SCATTER_GATHER_MODE | START, CS_REG(s->dma_channel)); 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cibool is_dma_done(struct cobalt_stream *s) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct cobalt *cobalt = s->cobalt; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci if (ioread32(CS_REG(s->dma_channel)) & DONE) 11062306a36Sopenharmony_ci return true; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci return false; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_civoid omni_sg_dma_abort_channel(struct cobalt_stream *s) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct cobalt *cobalt = s->cobalt; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (!is_dma_done(s)) 12062306a36Sopenharmony_ci iowrite32(ABORT, CS_REG(s->dma_channel)); 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ciint omni_sg_dma_init(struct cobalt *cobalt) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci u32 capa = ioread32(CAPABILITY_REGISTER); 12662306a36Sopenharmony_ci int i; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci cobalt->first_fifo_channel = 0; 12962306a36Sopenharmony_ci cobalt->dma_channels = capa & 0xf; 13062306a36Sopenharmony_ci if (capa & PCI_64BIT) 13162306a36Sopenharmony_ci cobalt->pci_32_bit = false; 13262306a36Sopenharmony_ci else 13362306a36Sopenharmony_ci cobalt->pci_32_bit = true; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci for (i = 0; i < cobalt->dma_channels; i++) { 13662306a36Sopenharmony_ci u32 status = ioread32(CS_REG(i)); 13762306a36Sopenharmony_ci u32 ctrl = ioread32(CS_REG(i)); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (!(ctrl & DONE)) 14062306a36Sopenharmony_ci iowrite32(ABORT, CS_REG(i)); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (!(status & DMA_TYPE_FIFO)) 14362306a36Sopenharmony_ci cobalt->first_fifo_channel++; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci show_dma_capability(cobalt); 14662306a36Sopenharmony_ci return 0; 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ciint descriptor_list_create(struct cobalt *cobalt, 15062306a36Sopenharmony_ci struct scatterlist *scatter_list, bool to_pci, unsigned sglen, 15162306a36Sopenharmony_ci unsigned size, unsigned width, unsigned stride, 15262306a36Sopenharmony_ci struct sg_dma_desc_info *desc) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci struct sg_dma_descriptor *d = (struct sg_dma_descriptor *)desc->virt; 15562306a36Sopenharmony_ci dma_addr_t next = desc->bus; 15662306a36Sopenharmony_ci unsigned offset = 0; 15762306a36Sopenharmony_ci unsigned copy_bytes = width; 15862306a36Sopenharmony_ci unsigned copied = 0; 15962306a36Sopenharmony_ci bool first = true; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* Must be 4-byte aligned */ 16262306a36Sopenharmony_ci WARN_ON(sg_dma_address(scatter_list) & 3); 16362306a36Sopenharmony_ci WARN_ON(size & 3); 16462306a36Sopenharmony_ci WARN_ON(next & 3); 16562306a36Sopenharmony_ci WARN_ON(stride & 3); 16662306a36Sopenharmony_ci WARN_ON(stride < width); 16762306a36Sopenharmony_ci if (width >= stride) 16862306a36Sopenharmony_ci copy_bytes = stride = size; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci while (size) { 17162306a36Sopenharmony_ci dma_addr_t addr = sg_dma_address(scatter_list) + offset; 17262306a36Sopenharmony_ci unsigned bytes; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (addr == 0) 17562306a36Sopenharmony_ci return -EFAULT; 17662306a36Sopenharmony_ci if (cobalt->pci_32_bit) { 17762306a36Sopenharmony_ci WARN_ON((u64)addr >> 32); 17862306a36Sopenharmony_ci if ((u64)addr >> 32) 17962306a36Sopenharmony_ci return -EFAULT; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci /* PCIe address */ 18362306a36Sopenharmony_ci d->pci_l = addr & 0xffffffff; 18462306a36Sopenharmony_ci /* If dma_addr_t is 32 bits, then addr >> 32 is actually the 18562306a36Sopenharmony_ci equivalent of addr >> 0 in gcc. So must cast to u64. */ 18662306a36Sopenharmony_ci d->pci_h = (u64)addr >> 32; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci /* Sync to start of streaming frame */ 18962306a36Sopenharmony_ci d->local = 0; 19062306a36Sopenharmony_ci d->reserved0 = 0; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci /* Transfer bytes */ 19362306a36Sopenharmony_ci bytes = min(sg_dma_len(scatter_list) - offset, 19462306a36Sopenharmony_ci copy_bytes - copied); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (first) { 19762306a36Sopenharmony_ci if (to_pci) 19862306a36Sopenharmony_ci d->local = 0x11111111; 19962306a36Sopenharmony_ci first = false; 20062306a36Sopenharmony_ci if (sglen == 1) { 20162306a36Sopenharmony_ci /* Make sure there are always at least two 20262306a36Sopenharmony_ci * descriptors */ 20362306a36Sopenharmony_ci d->bytes = (bytes / 2) & ~3; 20462306a36Sopenharmony_ci d->reserved1 = 0; 20562306a36Sopenharmony_ci size -= d->bytes; 20662306a36Sopenharmony_ci copied += d->bytes; 20762306a36Sopenharmony_ci offset += d->bytes; 20862306a36Sopenharmony_ci addr += d->bytes; 20962306a36Sopenharmony_ci next += sizeof(struct sg_dma_descriptor); 21062306a36Sopenharmony_ci d->next_h = (u32)((u64)next >> 32); 21162306a36Sopenharmony_ci d->next_l = (u32)next | 21262306a36Sopenharmony_ci (to_pci ? WRITE_TO_PCI : 0); 21362306a36Sopenharmony_ci bytes -= d->bytes; 21462306a36Sopenharmony_ci d++; 21562306a36Sopenharmony_ci /* PCIe address */ 21662306a36Sopenharmony_ci d->pci_l = addr & 0xffffffff; 21762306a36Sopenharmony_ci /* If dma_addr_t is 32 bits, then addr >> 32 21862306a36Sopenharmony_ci * is actually the equivalent of addr >> 0 in 21962306a36Sopenharmony_ci * gcc. So must cast to u64. */ 22062306a36Sopenharmony_ci d->pci_h = (u64)addr >> 32; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci /* Sync to start of streaming frame */ 22362306a36Sopenharmony_ci d->local = 0; 22462306a36Sopenharmony_ci d->reserved0 = 0; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci d->bytes = bytes; 22962306a36Sopenharmony_ci d->reserved1 = 0; 23062306a36Sopenharmony_ci size -= bytes; 23162306a36Sopenharmony_ci copied += bytes; 23262306a36Sopenharmony_ci offset += bytes; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if (copied == copy_bytes) { 23562306a36Sopenharmony_ci while (copied < stride) { 23662306a36Sopenharmony_ci bytes = min(sg_dma_len(scatter_list) - offset, 23762306a36Sopenharmony_ci stride - copied); 23862306a36Sopenharmony_ci copied += bytes; 23962306a36Sopenharmony_ci offset += bytes; 24062306a36Sopenharmony_ci size -= bytes; 24162306a36Sopenharmony_ci if (sg_dma_len(scatter_list) == offset) { 24262306a36Sopenharmony_ci offset = 0; 24362306a36Sopenharmony_ci scatter_list = sg_next(scatter_list); 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci copied = 0; 24762306a36Sopenharmony_ci } else { 24862306a36Sopenharmony_ci offset = 0; 24962306a36Sopenharmony_ci scatter_list = sg_next(scatter_list); 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci /* Next descriptor + control bits */ 25362306a36Sopenharmony_ci next += sizeof(struct sg_dma_descriptor); 25462306a36Sopenharmony_ci if (size == 0) { 25562306a36Sopenharmony_ci /* Loopback to the first descriptor */ 25662306a36Sopenharmony_ci d->next_h = (u32)((u64)desc->bus >> 32); 25762306a36Sopenharmony_ci d->next_l = (u32)desc->bus | 25862306a36Sopenharmony_ci (to_pci ? WRITE_TO_PCI : 0) | INTERRUPT_ENABLE; 25962306a36Sopenharmony_ci if (!to_pci) 26062306a36Sopenharmony_ci d->local = 0x22222222; 26162306a36Sopenharmony_ci desc->last_desc_virt = d; 26262306a36Sopenharmony_ci } else { 26362306a36Sopenharmony_ci d->next_h = (u32)((u64)next >> 32); 26462306a36Sopenharmony_ci d->next_l = (u32)next | (to_pci ? WRITE_TO_PCI : 0); 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci d++; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci return 0; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_civoid descriptor_list_chain(struct sg_dma_desc_info *this, 27262306a36Sopenharmony_ci struct sg_dma_desc_info *next) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci struct sg_dma_descriptor *d = this->last_desc_virt; 27562306a36Sopenharmony_ci u32 direction = d->next_l & WRITE_TO_PCI; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci if (next == NULL) { 27862306a36Sopenharmony_ci d->next_h = 0; 27962306a36Sopenharmony_ci d->next_l = direction | INTERRUPT_ENABLE | END_OF_CHAIN; 28062306a36Sopenharmony_ci } else { 28162306a36Sopenharmony_ci d->next_h = (u32)((u64)next->bus >> 32); 28262306a36Sopenharmony_ci d->next_l = (u32)next->bus | direction | INTERRUPT_ENABLE; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_civoid *descriptor_list_allocate(struct sg_dma_desc_info *desc, size_t bytes) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci desc->size = bytes; 28962306a36Sopenharmony_ci desc->virt = dma_alloc_coherent(desc->dev, bytes, 29062306a36Sopenharmony_ci &desc->bus, GFP_KERNEL); 29162306a36Sopenharmony_ci return desc->virt; 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_civoid descriptor_list_free(struct sg_dma_desc_info *desc) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci if (desc->virt) 29762306a36Sopenharmony_ci dma_free_coherent(desc->dev, desc->size, 29862306a36Sopenharmony_ci desc->virt, desc->bus); 29962306a36Sopenharmony_ci desc->virt = NULL; 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_civoid descriptor_list_interrupt_enable(struct sg_dma_desc_info *desc) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci struct sg_dma_descriptor *d = desc->last_desc_virt; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci d->next_l |= INTERRUPT_ENABLE; 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_civoid descriptor_list_interrupt_disable(struct sg_dma_desc_info *desc) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci struct sg_dma_descriptor *d = desc->last_desc_virt; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci d->next_l &= ~INTERRUPT_ENABLE; 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_civoid descriptor_list_loopback(struct sg_dma_desc_info *desc) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci struct sg_dma_descriptor *d = desc->last_desc_virt; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci d->next_h = (u32)((u64)desc->bus >> 32); 32162306a36Sopenharmony_ci d->next_l = (u32)desc->bus | (d->next_l & DESCRIPTOR_FLAG_MSK); 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_civoid descriptor_list_end_of_chain(struct sg_dma_desc_info *desc) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci struct sg_dma_descriptor *d = desc->last_desc_virt; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci d->next_l |= END_OF_CHAIN; 32962306a36Sopenharmony_ci} 330