167f4addbSFrank Haverkamp /** 267f4addbSFrank Haverkamp * IBM Accelerator Family 'GenWQE' 367f4addbSFrank Haverkamp * 467f4addbSFrank Haverkamp * (C) Copyright IBM Corp. 2013 567f4addbSFrank Haverkamp * 667f4addbSFrank Haverkamp * Author: Frank Haverkamp <haver@linux.vnet.ibm.com> 767f4addbSFrank Haverkamp * Author: Joerg-Stephan Vogt <jsvogt@de.ibm.com> 867f4addbSFrank Haverkamp * Author: Michael Jung <mijung@de.ibm.com> 967f4addbSFrank Haverkamp * Author: Michael Ruettger <michael@ibmra.de> 1067f4addbSFrank Haverkamp * 1167f4addbSFrank Haverkamp * This program is free software; you can redistribute it and/or modify 1267f4addbSFrank Haverkamp * it under the terms of the GNU General Public License (version 2 only) 1367f4addbSFrank Haverkamp * as published by the Free Software Foundation. 1467f4addbSFrank Haverkamp * 1567f4addbSFrank Haverkamp * This program is distributed in the hope that it will be useful, 1667f4addbSFrank Haverkamp * but WITHOUT ANY WARRANTY; without even the implied warranty of 1767f4addbSFrank Haverkamp * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1867f4addbSFrank Haverkamp * GNU General Public License for more details. 1967f4addbSFrank Haverkamp */ 2067f4addbSFrank Haverkamp 2167f4addbSFrank Haverkamp /* 2267f4addbSFrank Haverkamp * Miscelanous functionality used in the other GenWQE driver parts. 2367f4addbSFrank Haverkamp */ 2467f4addbSFrank Haverkamp 2567f4addbSFrank Haverkamp #include <linux/kernel.h> 2667f4addbSFrank Haverkamp #include <linux/dma-mapping.h> 2767f4addbSFrank Haverkamp #include <linux/sched.h> 2867f4addbSFrank Haverkamp #include <linux/vmalloc.h> 2967f4addbSFrank Haverkamp #include <linux/page-flags.h> 3067f4addbSFrank Haverkamp #include <linux/scatterlist.h> 3167f4addbSFrank Haverkamp #include <linux/hugetlb.h> 3267f4addbSFrank Haverkamp #include <linux/iommu.h> 3367f4addbSFrank Haverkamp #include <linux/delay.h> 3467f4addbSFrank Haverkamp #include <linux/pci.h> 3567f4addbSFrank Haverkamp #include <linux/dma-mapping.h> 3667f4addbSFrank Haverkamp #include <linux/ctype.h> 3767f4addbSFrank Haverkamp #include <linux/module.h> 3867f4addbSFrank Haverkamp #include <linux/platform_device.h> 3967f4addbSFrank Haverkamp #include <linux/delay.h> 4067f4addbSFrank Haverkamp #include <asm/pgtable.h> 4167f4addbSFrank Haverkamp 4267f4addbSFrank Haverkamp #include "genwqe_driver.h" 4367f4addbSFrank Haverkamp #include "card_base.h" 4467f4addbSFrank Haverkamp #include "card_ddcb.h" 4567f4addbSFrank Haverkamp 4667f4addbSFrank Haverkamp /** 4767f4addbSFrank Haverkamp * __genwqe_writeq() - Write 64-bit register 4867f4addbSFrank Haverkamp * @cd: genwqe device descriptor 4967f4addbSFrank Haverkamp * @byte_offs: byte offset within BAR 5067f4addbSFrank Haverkamp * @val: 64-bit value 5167f4addbSFrank Haverkamp * 5267f4addbSFrank Haverkamp * Return: 0 if success; < 0 if error 5367f4addbSFrank Haverkamp */ 5467f4addbSFrank Haverkamp int __genwqe_writeq(struct genwqe_dev *cd, u64 byte_offs, u64 val) 5567f4addbSFrank Haverkamp { 5667f4addbSFrank Haverkamp if (cd->err_inject & GENWQE_INJECT_HARDWARE_FAILURE) 5767f4addbSFrank Haverkamp return -EIO; 5867f4addbSFrank Haverkamp 5967f4addbSFrank Haverkamp if (cd->mmio == NULL) 6067f4addbSFrank Haverkamp return -EIO; 6167f4addbSFrank Haverkamp 62*58d66ce7SFrank Haverkamp __raw_writeq((__force u32)cpu_to_be64(val), cd->mmio + byte_offs); 6367f4addbSFrank Haverkamp return 0; 6467f4addbSFrank Haverkamp } 6567f4addbSFrank Haverkamp 6667f4addbSFrank Haverkamp /** 6767f4addbSFrank Haverkamp * __genwqe_readq() - Read 64-bit register 6867f4addbSFrank Haverkamp * @cd: genwqe device descriptor 6967f4addbSFrank Haverkamp * @byte_offs: offset within BAR 7067f4addbSFrank Haverkamp * 7167f4addbSFrank Haverkamp * Return: value from register 7267f4addbSFrank Haverkamp */ 7367f4addbSFrank Haverkamp u64 __genwqe_readq(struct genwqe_dev *cd, u64 byte_offs) 7467f4addbSFrank Haverkamp { 7567f4addbSFrank Haverkamp if (cd->err_inject & GENWQE_INJECT_HARDWARE_FAILURE) 7667f4addbSFrank Haverkamp return 0xffffffffffffffffull; 7767f4addbSFrank Haverkamp 7867f4addbSFrank Haverkamp if ((cd->err_inject & GENWQE_INJECT_GFIR_FATAL) && 7967f4addbSFrank Haverkamp (byte_offs == IO_SLC_CFGREG_GFIR)) 8067f4addbSFrank Haverkamp return 0x000000000000ffffull; 8167f4addbSFrank Haverkamp 8267f4addbSFrank Haverkamp if ((cd->err_inject & GENWQE_INJECT_GFIR_INFO) && 8367f4addbSFrank Haverkamp (byte_offs == IO_SLC_CFGREG_GFIR)) 8467f4addbSFrank Haverkamp return 0x00000000ffff0000ull; 8567f4addbSFrank Haverkamp 8667f4addbSFrank Haverkamp if (cd->mmio == NULL) 8767f4addbSFrank Haverkamp return 0xffffffffffffffffull; 8867f4addbSFrank Haverkamp 89*58d66ce7SFrank Haverkamp return be64_to_cpu((__force __be64)__raw_readq(cd->mmio + byte_offs)); 9067f4addbSFrank Haverkamp } 9167f4addbSFrank Haverkamp 9267f4addbSFrank Haverkamp /** 9367f4addbSFrank Haverkamp * __genwqe_writel() - Write 32-bit register 9467f4addbSFrank Haverkamp * @cd: genwqe device descriptor 9567f4addbSFrank Haverkamp * @byte_offs: byte offset within BAR 9667f4addbSFrank Haverkamp * @val: 32-bit value 9767f4addbSFrank Haverkamp * 9867f4addbSFrank Haverkamp * Return: 0 if success; < 0 if error 9967f4addbSFrank Haverkamp */ 10067f4addbSFrank Haverkamp int __genwqe_writel(struct genwqe_dev *cd, u64 byte_offs, u32 val) 10167f4addbSFrank Haverkamp { 10267f4addbSFrank Haverkamp if (cd->err_inject & GENWQE_INJECT_HARDWARE_FAILURE) 10367f4addbSFrank Haverkamp return -EIO; 10467f4addbSFrank Haverkamp 10567f4addbSFrank Haverkamp if (cd->mmio == NULL) 10667f4addbSFrank Haverkamp return -EIO; 10767f4addbSFrank Haverkamp 108*58d66ce7SFrank Haverkamp __raw_writel((__force u32)cpu_to_be32(val), cd->mmio + byte_offs); 10967f4addbSFrank Haverkamp return 0; 11067f4addbSFrank Haverkamp } 11167f4addbSFrank Haverkamp 11267f4addbSFrank Haverkamp /** 11367f4addbSFrank Haverkamp * __genwqe_readl() - Read 32-bit register 11467f4addbSFrank Haverkamp * @cd: genwqe device descriptor 11567f4addbSFrank Haverkamp * @byte_offs: offset within BAR 11667f4addbSFrank Haverkamp * 11767f4addbSFrank Haverkamp * Return: Value from register 11867f4addbSFrank Haverkamp */ 11967f4addbSFrank Haverkamp u32 __genwqe_readl(struct genwqe_dev *cd, u64 byte_offs) 12067f4addbSFrank Haverkamp { 12167f4addbSFrank Haverkamp if (cd->err_inject & GENWQE_INJECT_HARDWARE_FAILURE) 12267f4addbSFrank Haverkamp return 0xffffffff; 12367f4addbSFrank Haverkamp 12467f4addbSFrank Haverkamp if (cd->mmio == NULL) 12567f4addbSFrank Haverkamp return 0xffffffff; 12667f4addbSFrank Haverkamp 127*58d66ce7SFrank Haverkamp return be32_to_cpu((__force __be32)__raw_readl(cd->mmio + byte_offs)); 12867f4addbSFrank Haverkamp } 12967f4addbSFrank Haverkamp 13067f4addbSFrank Haverkamp /** 13167f4addbSFrank Haverkamp * genwqe_read_app_id() - Extract app_id 13267f4addbSFrank Haverkamp * 13367f4addbSFrank Haverkamp * app_unitcfg need to be filled with valid data first 13467f4addbSFrank Haverkamp */ 13567f4addbSFrank Haverkamp int genwqe_read_app_id(struct genwqe_dev *cd, char *app_name, int len) 13667f4addbSFrank Haverkamp { 13767f4addbSFrank Haverkamp int i, j; 13867f4addbSFrank Haverkamp u32 app_id = (u32)cd->app_unitcfg; 13967f4addbSFrank Haverkamp 14067f4addbSFrank Haverkamp memset(app_name, 0, len); 14167f4addbSFrank Haverkamp for (i = 0, j = 0; j < min(len, 4); j++) { 14267f4addbSFrank Haverkamp char ch = (char)((app_id >> (24 - j*8)) & 0xff); 14367f4addbSFrank Haverkamp if (ch == ' ') 14467f4addbSFrank Haverkamp continue; 14567f4addbSFrank Haverkamp app_name[i++] = isprint(ch) ? ch : 'X'; 14667f4addbSFrank Haverkamp } 14767f4addbSFrank Haverkamp return i; 14867f4addbSFrank Haverkamp } 14967f4addbSFrank Haverkamp 15067f4addbSFrank Haverkamp /** 15167f4addbSFrank Haverkamp * genwqe_init_crc32() - Prepare a lookup table for fast crc32 calculations 15267f4addbSFrank Haverkamp * 15367f4addbSFrank Haverkamp * Existing kernel functions seem to use a different polynom, 15467f4addbSFrank Haverkamp * therefore we could not use them here. 15567f4addbSFrank Haverkamp * 15667f4addbSFrank Haverkamp * Genwqe's Polynomial = 0x20044009 15767f4addbSFrank Haverkamp */ 15867f4addbSFrank Haverkamp #define CRC32_POLYNOMIAL 0x20044009 15967f4addbSFrank Haverkamp static u32 crc32_tab[256]; /* crc32 lookup table */ 16067f4addbSFrank Haverkamp 16167f4addbSFrank Haverkamp void genwqe_init_crc32(void) 16267f4addbSFrank Haverkamp { 16367f4addbSFrank Haverkamp int i, j; 16467f4addbSFrank Haverkamp u32 crc; 16567f4addbSFrank Haverkamp 16667f4addbSFrank Haverkamp for (i = 0; i < 256; i++) { 16767f4addbSFrank Haverkamp crc = i << 24; 16867f4addbSFrank Haverkamp for (j = 0; j < 8; j++) { 16967f4addbSFrank Haverkamp if (crc & 0x80000000) 17067f4addbSFrank Haverkamp crc = (crc << 1) ^ CRC32_POLYNOMIAL; 17167f4addbSFrank Haverkamp else 17267f4addbSFrank Haverkamp crc = (crc << 1); 17367f4addbSFrank Haverkamp } 17467f4addbSFrank Haverkamp crc32_tab[i] = crc; 17567f4addbSFrank Haverkamp } 17667f4addbSFrank Haverkamp } 17767f4addbSFrank Haverkamp 17867f4addbSFrank Haverkamp /** 17967f4addbSFrank Haverkamp * genwqe_crc32() - Generate 32-bit crc as required for DDCBs 18067f4addbSFrank Haverkamp * @buff: pointer to data buffer 18167f4addbSFrank Haverkamp * @len: length of data for calculation 18267f4addbSFrank Haverkamp * @init: initial crc (0xffffffff at start) 18367f4addbSFrank Haverkamp * 18467f4addbSFrank Haverkamp * polynomial = x^32 * + x^29 + x^18 + x^14 + x^3 + 1 (0x20044009) 18567f4addbSFrank Haverkamp 18667f4addbSFrank Haverkamp * Example: 4 bytes 0x01 0x02 0x03 0x04 with init=0xffffffff should 18767f4addbSFrank Haverkamp * result in a crc32 of 0xf33cb7d3. 18867f4addbSFrank Haverkamp * 18967f4addbSFrank Haverkamp * The existing kernel crc functions did not cover this polynom yet. 19067f4addbSFrank Haverkamp * 19167f4addbSFrank Haverkamp * Return: crc32 checksum. 19267f4addbSFrank Haverkamp */ 19367f4addbSFrank Haverkamp u32 genwqe_crc32(u8 *buff, size_t len, u32 init) 19467f4addbSFrank Haverkamp { 19567f4addbSFrank Haverkamp int i; 19667f4addbSFrank Haverkamp u32 crc; 19767f4addbSFrank Haverkamp 19867f4addbSFrank Haverkamp crc = init; 19967f4addbSFrank Haverkamp while (len--) { 20067f4addbSFrank Haverkamp i = ((crc >> 24) ^ *buff++) & 0xFF; 20167f4addbSFrank Haverkamp crc = (crc << 8) ^ crc32_tab[i]; 20267f4addbSFrank Haverkamp } 20367f4addbSFrank Haverkamp return crc; 20467f4addbSFrank Haverkamp } 20567f4addbSFrank Haverkamp 20667f4addbSFrank Haverkamp void *__genwqe_alloc_consistent(struct genwqe_dev *cd, size_t size, 20767f4addbSFrank Haverkamp dma_addr_t *dma_handle) 20867f4addbSFrank Haverkamp { 20967f4addbSFrank Haverkamp if (get_order(size) > MAX_ORDER) 21067f4addbSFrank Haverkamp return NULL; 21167f4addbSFrank Haverkamp 21267f4addbSFrank Haverkamp return pci_alloc_consistent(cd->pci_dev, size, dma_handle); 21367f4addbSFrank Haverkamp } 21467f4addbSFrank Haverkamp 21567f4addbSFrank Haverkamp void __genwqe_free_consistent(struct genwqe_dev *cd, size_t size, 21667f4addbSFrank Haverkamp void *vaddr, dma_addr_t dma_handle) 21767f4addbSFrank Haverkamp { 21867f4addbSFrank Haverkamp if (vaddr == NULL) 21967f4addbSFrank Haverkamp return; 22067f4addbSFrank Haverkamp 22167f4addbSFrank Haverkamp pci_free_consistent(cd->pci_dev, size, vaddr, dma_handle); 22267f4addbSFrank Haverkamp } 22367f4addbSFrank Haverkamp 22467f4addbSFrank Haverkamp static void genwqe_unmap_pages(struct genwqe_dev *cd, dma_addr_t *dma_list, 22567f4addbSFrank Haverkamp int num_pages) 22667f4addbSFrank Haverkamp { 22767f4addbSFrank Haverkamp int i; 22867f4addbSFrank Haverkamp struct pci_dev *pci_dev = cd->pci_dev; 22967f4addbSFrank Haverkamp 23067f4addbSFrank Haverkamp for (i = 0; (i < num_pages) && (dma_list[i] != 0x0); i++) { 23167f4addbSFrank Haverkamp pci_unmap_page(pci_dev, dma_list[i], 23267f4addbSFrank Haverkamp PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); 23367f4addbSFrank Haverkamp dma_list[i] = 0x0; 23467f4addbSFrank Haverkamp } 23567f4addbSFrank Haverkamp } 23667f4addbSFrank Haverkamp 23767f4addbSFrank Haverkamp static int genwqe_map_pages(struct genwqe_dev *cd, 23867f4addbSFrank Haverkamp struct page **page_list, int num_pages, 23967f4addbSFrank Haverkamp dma_addr_t *dma_list) 24067f4addbSFrank Haverkamp { 24167f4addbSFrank Haverkamp int i; 24267f4addbSFrank Haverkamp struct pci_dev *pci_dev = cd->pci_dev; 24367f4addbSFrank Haverkamp 24467f4addbSFrank Haverkamp /* establish DMA mapping for requested pages */ 24567f4addbSFrank Haverkamp for (i = 0; i < num_pages; i++) { 24667f4addbSFrank Haverkamp dma_addr_t daddr; 24767f4addbSFrank Haverkamp 24867f4addbSFrank Haverkamp dma_list[i] = 0x0; 24967f4addbSFrank Haverkamp daddr = pci_map_page(pci_dev, page_list[i], 25067f4addbSFrank Haverkamp 0, /* map_offs */ 25167f4addbSFrank Haverkamp PAGE_SIZE, 25267f4addbSFrank Haverkamp PCI_DMA_BIDIRECTIONAL); /* FIXME rd/rw */ 25367f4addbSFrank Haverkamp 25467f4addbSFrank Haverkamp if (pci_dma_mapping_error(pci_dev, daddr)) { 25567f4addbSFrank Haverkamp dev_err(&pci_dev->dev, 25667f4addbSFrank Haverkamp "[%s] err: no dma addr daddr=%016llx!\n", 25767f4addbSFrank Haverkamp __func__, (long long)daddr); 25867f4addbSFrank Haverkamp goto err; 25967f4addbSFrank Haverkamp } 26067f4addbSFrank Haverkamp 26167f4addbSFrank Haverkamp dma_list[i] = daddr; 26267f4addbSFrank Haverkamp } 26367f4addbSFrank Haverkamp return 0; 26467f4addbSFrank Haverkamp 26567f4addbSFrank Haverkamp err: 26667f4addbSFrank Haverkamp genwqe_unmap_pages(cd, dma_list, num_pages); 26767f4addbSFrank Haverkamp return -EIO; 26867f4addbSFrank Haverkamp } 26967f4addbSFrank Haverkamp 27067f4addbSFrank Haverkamp static int genwqe_sgl_size(int num_pages) 27167f4addbSFrank Haverkamp { 27267f4addbSFrank Haverkamp int len, num_tlb = num_pages / 7; 27367f4addbSFrank Haverkamp 27467f4addbSFrank Haverkamp len = sizeof(struct sg_entry) * (num_pages+num_tlb + 1); 27567f4addbSFrank Haverkamp return roundup(len, PAGE_SIZE); 27667f4addbSFrank Haverkamp } 27767f4addbSFrank Haverkamp 27867f4addbSFrank Haverkamp struct sg_entry *genwqe_alloc_sgl(struct genwqe_dev *cd, int num_pages, 27967f4addbSFrank Haverkamp dma_addr_t *dma_addr, size_t *sgl_size) 28067f4addbSFrank Haverkamp { 28167f4addbSFrank Haverkamp struct pci_dev *pci_dev = cd->pci_dev; 28267f4addbSFrank Haverkamp struct sg_entry *sgl; 28367f4addbSFrank Haverkamp 28467f4addbSFrank Haverkamp *sgl_size = genwqe_sgl_size(num_pages); 28567f4addbSFrank Haverkamp if (get_order(*sgl_size) > MAX_ORDER) { 28667f4addbSFrank Haverkamp dev_err(&pci_dev->dev, 28767f4addbSFrank Haverkamp "[%s] err: too much memory requested!\n", __func__); 28867f4addbSFrank Haverkamp return NULL; 28967f4addbSFrank Haverkamp } 29067f4addbSFrank Haverkamp 29167f4addbSFrank Haverkamp sgl = __genwqe_alloc_consistent(cd, *sgl_size, dma_addr); 29267f4addbSFrank Haverkamp if (sgl == NULL) { 29367f4addbSFrank Haverkamp dev_err(&pci_dev->dev, 29467f4addbSFrank Haverkamp "[%s] err: no memory available!\n", __func__); 29567f4addbSFrank Haverkamp return NULL; 29667f4addbSFrank Haverkamp } 29767f4addbSFrank Haverkamp 29867f4addbSFrank Haverkamp return sgl; 29967f4addbSFrank Haverkamp } 30067f4addbSFrank Haverkamp 30167f4addbSFrank Haverkamp int genwqe_setup_sgl(struct genwqe_dev *cd, 30267f4addbSFrank Haverkamp unsigned long offs, 30367f4addbSFrank Haverkamp unsigned long size, 30467f4addbSFrank Haverkamp struct sg_entry *sgl, 30567f4addbSFrank Haverkamp dma_addr_t dma_addr, size_t sgl_size, 30667f4addbSFrank Haverkamp dma_addr_t *dma_list, int page_offs, int num_pages) 30767f4addbSFrank Haverkamp { 30867f4addbSFrank Haverkamp int i = 0, j = 0, p; 30967f4addbSFrank Haverkamp unsigned long dma_offs, map_offs; 31067f4addbSFrank Haverkamp struct pci_dev *pci_dev = cd->pci_dev; 31167f4addbSFrank Haverkamp dma_addr_t prev_daddr = 0; 31267f4addbSFrank Haverkamp struct sg_entry *s, *last_s = NULL; 31367f4addbSFrank Haverkamp 31467f4addbSFrank Haverkamp /* sanity checks */ 31567f4addbSFrank Haverkamp if (offs > PAGE_SIZE) { 31667f4addbSFrank Haverkamp dev_err(&pci_dev->dev, 31767f4addbSFrank Haverkamp "[%s] too large start offs %08lx\n", __func__, offs); 31867f4addbSFrank Haverkamp return -EFAULT; 31967f4addbSFrank Haverkamp } 32067f4addbSFrank Haverkamp if (sgl_size < genwqe_sgl_size(num_pages)) { 32167f4addbSFrank Haverkamp dev_err(&pci_dev->dev, 32267f4addbSFrank Haverkamp "[%s] sgl_size too small %08lx for %d pages\n", 32367f4addbSFrank Haverkamp __func__, sgl_size, num_pages); 32467f4addbSFrank Haverkamp return -EFAULT; 32567f4addbSFrank Haverkamp } 32667f4addbSFrank Haverkamp 32767f4addbSFrank Haverkamp dma_offs = 128; /* next block if needed/dma_offset */ 32867f4addbSFrank Haverkamp map_offs = offs; /* offset in first page */ 32967f4addbSFrank Haverkamp 33067f4addbSFrank Haverkamp s = &sgl[0]; /* first set of 8 entries */ 33167f4addbSFrank Haverkamp p = 0; /* page */ 33267f4addbSFrank Haverkamp while (p < num_pages) { 33367f4addbSFrank Haverkamp dma_addr_t daddr; 33467f4addbSFrank Haverkamp unsigned int size_to_map; 33567f4addbSFrank Haverkamp 33667f4addbSFrank Haverkamp /* always write the chaining entry, cleanup is done later */ 33767f4addbSFrank Haverkamp j = 0; 33867f4addbSFrank Haverkamp s[j].target_addr = cpu_to_be64(dma_addr + dma_offs); 33967f4addbSFrank Haverkamp s[j].len = cpu_to_be32(128); 34067f4addbSFrank Haverkamp s[j].flags = cpu_to_be32(SG_CHAINED); 34167f4addbSFrank Haverkamp j++; 34267f4addbSFrank Haverkamp 34367f4addbSFrank Haverkamp while (j < 8) { 34467f4addbSFrank Haverkamp /* DMA mapping for requested page, offs, size */ 34567f4addbSFrank Haverkamp size_to_map = min(size, PAGE_SIZE - map_offs); 34667f4addbSFrank Haverkamp daddr = dma_list[page_offs + p] + map_offs; 34767f4addbSFrank Haverkamp size -= size_to_map; 34867f4addbSFrank Haverkamp map_offs = 0; 34967f4addbSFrank Haverkamp 35067f4addbSFrank Haverkamp if (prev_daddr == daddr) { 35167f4addbSFrank Haverkamp u32 prev_len = be32_to_cpu(last_s->len); 35267f4addbSFrank Haverkamp 35367f4addbSFrank Haverkamp /* pr_info("daddr combining: " 35467f4addbSFrank Haverkamp "%016llx/%08x -> %016llx\n", 35567f4addbSFrank Haverkamp prev_daddr, prev_len, daddr); */ 35667f4addbSFrank Haverkamp 35767f4addbSFrank Haverkamp last_s->len = cpu_to_be32(prev_len + 35867f4addbSFrank Haverkamp size_to_map); 35967f4addbSFrank Haverkamp 36067f4addbSFrank Haverkamp p++; /* process next page */ 36167f4addbSFrank Haverkamp if (p == num_pages) 36267f4addbSFrank Haverkamp goto fixup; /* nothing to do */ 36367f4addbSFrank Haverkamp 36467f4addbSFrank Haverkamp prev_daddr = daddr + size_to_map; 36567f4addbSFrank Haverkamp continue; 36667f4addbSFrank Haverkamp } 36767f4addbSFrank Haverkamp 36867f4addbSFrank Haverkamp /* start new entry */ 36967f4addbSFrank Haverkamp s[j].target_addr = cpu_to_be64(daddr); 37067f4addbSFrank Haverkamp s[j].len = cpu_to_be32(size_to_map); 37167f4addbSFrank Haverkamp s[j].flags = cpu_to_be32(SG_DATA); 37267f4addbSFrank Haverkamp prev_daddr = daddr + size_to_map; 37367f4addbSFrank Haverkamp last_s = &s[j]; 37467f4addbSFrank Haverkamp j++; 37567f4addbSFrank Haverkamp 37667f4addbSFrank Haverkamp p++; /* process next page */ 37767f4addbSFrank Haverkamp if (p == num_pages) 37867f4addbSFrank Haverkamp goto fixup; /* nothing to do */ 37967f4addbSFrank Haverkamp } 38067f4addbSFrank Haverkamp dma_offs += 128; 38167f4addbSFrank Haverkamp s += 8; /* continue 8 elements further */ 38267f4addbSFrank Haverkamp } 38367f4addbSFrank Haverkamp fixup: 38467f4addbSFrank Haverkamp if (j == 1) { /* combining happend on last entry! */ 38567f4addbSFrank Haverkamp s -= 8; /* full shift needed on previous sgl block */ 38667f4addbSFrank Haverkamp j = 7; /* shift all elements */ 38767f4addbSFrank Haverkamp } 38867f4addbSFrank Haverkamp 38967f4addbSFrank Haverkamp for (i = 0; i < j; i++) /* move elements 1 up */ 39067f4addbSFrank Haverkamp s[i] = s[i + 1]; 39167f4addbSFrank Haverkamp 39267f4addbSFrank Haverkamp s[i].target_addr = cpu_to_be64(0); 39367f4addbSFrank Haverkamp s[i].len = cpu_to_be32(0); 39467f4addbSFrank Haverkamp s[i].flags = cpu_to_be32(SG_END_LIST); 39567f4addbSFrank Haverkamp return 0; 39667f4addbSFrank Haverkamp } 39767f4addbSFrank Haverkamp 39867f4addbSFrank Haverkamp void genwqe_free_sgl(struct genwqe_dev *cd, struct sg_entry *sg_list, 39967f4addbSFrank Haverkamp dma_addr_t dma_addr, size_t size) 40067f4addbSFrank Haverkamp { 40167f4addbSFrank Haverkamp __genwqe_free_consistent(cd, size, sg_list, dma_addr); 40267f4addbSFrank Haverkamp } 40367f4addbSFrank Haverkamp 40467f4addbSFrank Haverkamp /** 40567f4addbSFrank Haverkamp * free_user_pages() - Give pinned pages back 40667f4addbSFrank Haverkamp * 40767f4addbSFrank Haverkamp * Documentation of get_user_pages is in mm/memory.c: 40867f4addbSFrank Haverkamp * 40967f4addbSFrank Haverkamp * If the page is written to, set_page_dirty (or set_page_dirty_lock, 41067f4addbSFrank Haverkamp * as appropriate) must be called after the page is finished with, and 41167f4addbSFrank Haverkamp * before put_page is called. 41267f4addbSFrank Haverkamp * 41367f4addbSFrank Haverkamp * FIXME Could be of use to others and might belong in the generic 41467f4addbSFrank Haverkamp * code, if others agree. E.g. 41567f4addbSFrank Haverkamp * ll_free_user_pages in drivers/staging/lustre/lustre/llite/rw26.c 41667f4addbSFrank Haverkamp * ceph_put_page_vector in net/ceph/pagevec.c 41767f4addbSFrank Haverkamp * maybe more? 41867f4addbSFrank Haverkamp */ 41967f4addbSFrank Haverkamp static int free_user_pages(struct page **page_list, unsigned int nr_pages, 42067f4addbSFrank Haverkamp int dirty) 42167f4addbSFrank Haverkamp { 42267f4addbSFrank Haverkamp unsigned int i; 42367f4addbSFrank Haverkamp 42467f4addbSFrank Haverkamp for (i = 0; i < nr_pages; i++) { 42567f4addbSFrank Haverkamp if (page_list[i] != NULL) { 42667f4addbSFrank Haverkamp if (dirty) 42767f4addbSFrank Haverkamp set_page_dirty_lock(page_list[i]); 42867f4addbSFrank Haverkamp put_page(page_list[i]); 42967f4addbSFrank Haverkamp } 43067f4addbSFrank Haverkamp } 43167f4addbSFrank Haverkamp return 0; 43267f4addbSFrank Haverkamp } 43367f4addbSFrank Haverkamp 43467f4addbSFrank Haverkamp /** 43567f4addbSFrank Haverkamp * genwqe_user_vmap() - Map user-space memory to virtual kernel memory 43667f4addbSFrank Haverkamp * @cd: pointer to genwqe device 43767f4addbSFrank Haverkamp * @m: mapping params 43867f4addbSFrank Haverkamp * @uaddr: user virtual address 43967f4addbSFrank Haverkamp * @size: size of memory to be mapped 44067f4addbSFrank Haverkamp * 44167f4addbSFrank Haverkamp * We need to think about how we could speed this up. Of course it is 44267f4addbSFrank Haverkamp * not a good idea to do this over and over again, like we are 44367f4addbSFrank Haverkamp * currently doing it. Nevertheless, I am curious where on the path 44467f4addbSFrank Haverkamp * the performance is spend. Most probably within the memory 44567f4addbSFrank Haverkamp * allocation functions, but maybe also in the DMA mapping code. 44667f4addbSFrank Haverkamp * 44767f4addbSFrank Haverkamp * Restrictions: The maximum size of the possible mapping currently depends 44867f4addbSFrank Haverkamp * on the amount of memory we can get using kzalloc() for the 44967f4addbSFrank Haverkamp * page_list and pci_alloc_consistent for the sg_list. 45067f4addbSFrank Haverkamp * The sg_list is currently itself not scattered, which could 45167f4addbSFrank Haverkamp * be fixed with some effort. The page_list must be split into 45267f4addbSFrank Haverkamp * PAGE_SIZE chunks too. All that will make the complicated 45367f4addbSFrank Haverkamp * code more complicated. 45467f4addbSFrank Haverkamp * 45567f4addbSFrank Haverkamp * Return: 0 if success 45667f4addbSFrank Haverkamp */ 45767f4addbSFrank Haverkamp int genwqe_user_vmap(struct genwqe_dev *cd, struct dma_mapping *m, void *uaddr, 45867f4addbSFrank Haverkamp unsigned long size, struct ddcb_requ *req) 45967f4addbSFrank Haverkamp { 46067f4addbSFrank Haverkamp int rc = -EINVAL; 46167f4addbSFrank Haverkamp unsigned long data, offs; 46267f4addbSFrank Haverkamp struct pci_dev *pci_dev = cd->pci_dev; 46367f4addbSFrank Haverkamp 46467f4addbSFrank Haverkamp if ((uaddr == NULL) || (size == 0)) { 46567f4addbSFrank Haverkamp m->size = 0; /* mark unused and not added */ 46667f4addbSFrank Haverkamp return -EINVAL; 46767f4addbSFrank Haverkamp } 46867f4addbSFrank Haverkamp m->u_vaddr = uaddr; 46967f4addbSFrank Haverkamp m->size = size; 47067f4addbSFrank Haverkamp 47167f4addbSFrank Haverkamp /* determine space needed for page_list. */ 47267f4addbSFrank Haverkamp data = (unsigned long)uaddr; 47367f4addbSFrank Haverkamp offs = offset_in_page(data); 47467f4addbSFrank Haverkamp m->nr_pages = DIV_ROUND_UP(offs + size, PAGE_SIZE); 47567f4addbSFrank Haverkamp 47667f4addbSFrank Haverkamp m->page_list = kcalloc(m->nr_pages, 47767f4addbSFrank Haverkamp sizeof(struct page *) + sizeof(dma_addr_t), 47867f4addbSFrank Haverkamp GFP_KERNEL); 47967f4addbSFrank Haverkamp if (!m->page_list) { 48067f4addbSFrank Haverkamp dev_err(&pci_dev->dev, "err: alloc page_list failed\n"); 48167f4addbSFrank Haverkamp m->nr_pages = 0; 48267f4addbSFrank Haverkamp m->u_vaddr = NULL; 48367f4addbSFrank Haverkamp m->size = 0; /* mark unused and not added */ 48467f4addbSFrank Haverkamp return -ENOMEM; 48567f4addbSFrank Haverkamp } 48667f4addbSFrank Haverkamp m->dma_list = (dma_addr_t *)(m->page_list + m->nr_pages); 48767f4addbSFrank Haverkamp 48867f4addbSFrank Haverkamp /* pin user pages in memory */ 48967f4addbSFrank Haverkamp rc = get_user_pages_fast(data & PAGE_MASK, /* page aligned addr */ 49067f4addbSFrank Haverkamp m->nr_pages, 49167f4addbSFrank Haverkamp 1, /* write by caller */ 49267f4addbSFrank Haverkamp m->page_list); /* ptrs to pages */ 49367f4addbSFrank Haverkamp 49467f4addbSFrank Haverkamp /* assumption: get_user_pages can be killed by signals. */ 49567f4addbSFrank Haverkamp if (rc < m->nr_pages) { 49667f4addbSFrank Haverkamp free_user_pages(m->page_list, rc, 0); 49767f4addbSFrank Haverkamp rc = -EFAULT; 49867f4addbSFrank Haverkamp goto fail_get_user_pages; 49967f4addbSFrank Haverkamp } 50067f4addbSFrank Haverkamp 50167f4addbSFrank Haverkamp rc = genwqe_map_pages(cd, m->page_list, m->nr_pages, m->dma_list); 50267f4addbSFrank Haverkamp if (rc != 0) 50367f4addbSFrank Haverkamp goto fail_free_user_pages; 50467f4addbSFrank Haverkamp 50567f4addbSFrank Haverkamp return 0; 50667f4addbSFrank Haverkamp 50767f4addbSFrank Haverkamp fail_free_user_pages: 50867f4addbSFrank Haverkamp free_user_pages(m->page_list, m->nr_pages, 0); 50967f4addbSFrank Haverkamp 51067f4addbSFrank Haverkamp fail_get_user_pages: 51167f4addbSFrank Haverkamp kfree(m->page_list); 51267f4addbSFrank Haverkamp m->page_list = NULL; 51367f4addbSFrank Haverkamp m->dma_list = NULL; 51467f4addbSFrank Haverkamp m->nr_pages = 0; 51567f4addbSFrank Haverkamp m->u_vaddr = NULL; 51667f4addbSFrank Haverkamp m->size = 0; /* mark unused and not added */ 51767f4addbSFrank Haverkamp return rc; 51867f4addbSFrank Haverkamp } 51967f4addbSFrank Haverkamp 52067f4addbSFrank Haverkamp /** 52167f4addbSFrank Haverkamp * genwqe_user_vunmap() - Undo mapping of user-space mem to virtual kernel 52267f4addbSFrank Haverkamp * memory 52367f4addbSFrank Haverkamp * @cd: pointer to genwqe device 52467f4addbSFrank Haverkamp * @m: mapping params 52567f4addbSFrank Haverkamp */ 52667f4addbSFrank Haverkamp int genwqe_user_vunmap(struct genwqe_dev *cd, struct dma_mapping *m, 52767f4addbSFrank Haverkamp struct ddcb_requ *req) 52867f4addbSFrank Haverkamp { 52967f4addbSFrank Haverkamp struct pci_dev *pci_dev = cd->pci_dev; 53067f4addbSFrank Haverkamp 53167f4addbSFrank Haverkamp if (!dma_mapping_used(m)) { 53267f4addbSFrank Haverkamp dev_err(&pci_dev->dev, "[%s] err: mapping %p not used!\n", 53367f4addbSFrank Haverkamp __func__, m); 53467f4addbSFrank Haverkamp return -EINVAL; 53567f4addbSFrank Haverkamp } 53667f4addbSFrank Haverkamp 53767f4addbSFrank Haverkamp if (m->dma_list) 53867f4addbSFrank Haverkamp genwqe_unmap_pages(cd, m->dma_list, m->nr_pages); 53967f4addbSFrank Haverkamp 54067f4addbSFrank Haverkamp if (m->page_list) { 54167f4addbSFrank Haverkamp free_user_pages(m->page_list, m->nr_pages, 1); 54267f4addbSFrank Haverkamp 54367f4addbSFrank Haverkamp kfree(m->page_list); 54467f4addbSFrank Haverkamp m->page_list = NULL; 54567f4addbSFrank Haverkamp m->dma_list = NULL; 54667f4addbSFrank Haverkamp m->nr_pages = 0; 54767f4addbSFrank Haverkamp } 54867f4addbSFrank Haverkamp 54967f4addbSFrank Haverkamp m->u_vaddr = NULL; 55067f4addbSFrank Haverkamp m->size = 0; /* mark as unused and not added */ 55167f4addbSFrank Haverkamp return 0; 55267f4addbSFrank Haverkamp } 55367f4addbSFrank Haverkamp 55467f4addbSFrank Haverkamp /** 55567f4addbSFrank Haverkamp * genwqe_card_type() - Get chip type SLU Configuration Register 55667f4addbSFrank Haverkamp * @cd: pointer to the genwqe device descriptor 55767f4addbSFrank Haverkamp * Return: 0: Altera Stratix-IV 230 55867f4addbSFrank Haverkamp * 1: Altera Stratix-IV 530 55967f4addbSFrank Haverkamp * 2: Altera Stratix-V A4 56067f4addbSFrank Haverkamp * 3: Altera Stratix-V A7 56167f4addbSFrank Haverkamp */ 56267f4addbSFrank Haverkamp u8 genwqe_card_type(struct genwqe_dev *cd) 56367f4addbSFrank Haverkamp { 56467f4addbSFrank Haverkamp u64 card_type = cd->slu_unitcfg; 56567f4addbSFrank Haverkamp return (u8)((card_type & IO_SLU_UNITCFG_TYPE_MASK) >> 20); 56667f4addbSFrank Haverkamp } 56767f4addbSFrank Haverkamp 56867f4addbSFrank Haverkamp /** 56967f4addbSFrank Haverkamp * genwqe_card_reset() - Reset the card 57067f4addbSFrank Haverkamp * @cd: pointer to the genwqe device descriptor 57167f4addbSFrank Haverkamp */ 57267f4addbSFrank Haverkamp int genwqe_card_reset(struct genwqe_dev *cd) 57367f4addbSFrank Haverkamp { 57467f4addbSFrank Haverkamp u64 softrst; 57567f4addbSFrank Haverkamp struct pci_dev *pci_dev = cd->pci_dev; 57667f4addbSFrank Haverkamp 57767f4addbSFrank Haverkamp if (!genwqe_is_privileged(cd)) 57867f4addbSFrank Haverkamp return -ENODEV; 57967f4addbSFrank Haverkamp 58067f4addbSFrank Haverkamp /* new SL */ 58167f4addbSFrank Haverkamp __genwqe_writeq(cd, IO_SLC_CFGREG_SOFTRESET, 0x1ull); 58267f4addbSFrank Haverkamp msleep(1000); 58367f4addbSFrank Haverkamp __genwqe_readq(cd, IO_HSU_FIR_CLR); 58467f4addbSFrank Haverkamp __genwqe_readq(cd, IO_APP_FIR_CLR); 58567f4addbSFrank Haverkamp __genwqe_readq(cd, IO_SLU_FIR_CLR); 58667f4addbSFrank Haverkamp 58767f4addbSFrank Haverkamp /* 58867f4addbSFrank Haverkamp * Read-modify-write to preserve the stealth bits 58967f4addbSFrank Haverkamp * 59067f4addbSFrank Haverkamp * For SL >= 039, Stealth WE bit allows removing 59167f4addbSFrank Haverkamp * the read-modify-wrote. 59267f4addbSFrank Haverkamp * r-m-w may require a mask 0x3C to avoid hitting hard 59367f4addbSFrank Haverkamp * reset again for error reset (should be 0, chicken). 59467f4addbSFrank Haverkamp */ 59567f4addbSFrank Haverkamp softrst = __genwqe_readq(cd, IO_SLC_CFGREG_SOFTRESET) & 0x3cull; 59667f4addbSFrank Haverkamp __genwqe_writeq(cd, IO_SLC_CFGREG_SOFTRESET, softrst | 0x2ull); 59767f4addbSFrank Haverkamp 59867f4addbSFrank Haverkamp /* give ERRORRESET some time to finish */ 59967f4addbSFrank Haverkamp msleep(50); 60067f4addbSFrank Haverkamp 60167f4addbSFrank Haverkamp if (genwqe_need_err_masking(cd)) { 60267f4addbSFrank Haverkamp dev_info(&pci_dev->dev, 60367f4addbSFrank Haverkamp "[%s] masking errors for old bitstreams\n", __func__); 60467f4addbSFrank Haverkamp __genwqe_writeq(cd, IO_SLC_MISC_DEBUG, 0x0aull); 60567f4addbSFrank Haverkamp } 60667f4addbSFrank Haverkamp return 0; 60767f4addbSFrank Haverkamp } 60867f4addbSFrank Haverkamp 60967f4addbSFrank Haverkamp int genwqe_read_softreset(struct genwqe_dev *cd) 61067f4addbSFrank Haverkamp { 61167f4addbSFrank Haverkamp u64 bitstream; 61267f4addbSFrank Haverkamp 61367f4addbSFrank Haverkamp if (!genwqe_is_privileged(cd)) 61467f4addbSFrank Haverkamp return -ENODEV; 61567f4addbSFrank Haverkamp 61667f4addbSFrank Haverkamp bitstream = __genwqe_readq(cd, IO_SLU_BITSTREAM) & 0x1; 61767f4addbSFrank Haverkamp cd->softreset = (bitstream == 0) ? 0x8ull : 0xcull; 61867f4addbSFrank Haverkamp return 0; 61967f4addbSFrank Haverkamp } 62067f4addbSFrank Haverkamp 62167f4addbSFrank Haverkamp /** 62267f4addbSFrank Haverkamp * genwqe_set_interrupt_capability() - Configure MSI capability structure 62367f4addbSFrank Haverkamp * @cd: pointer to the device 62467f4addbSFrank Haverkamp * Return: 0 if no error 62567f4addbSFrank Haverkamp */ 62667f4addbSFrank Haverkamp int genwqe_set_interrupt_capability(struct genwqe_dev *cd, int count) 62767f4addbSFrank Haverkamp { 62867f4addbSFrank Haverkamp int rc; 62967f4addbSFrank Haverkamp struct pci_dev *pci_dev = cd->pci_dev; 63067f4addbSFrank Haverkamp 63167f4addbSFrank Haverkamp rc = pci_enable_msi_block(pci_dev, count); 63267f4addbSFrank Haverkamp if (rc == 0) 63367f4addbSFrank Haverkamp cd->flags |= GENWQE_FLAG_MSI_ENABLED; 63467f4addbSFrank Haverkamp return rc; 63567f4addbSFrank Haverkamp } 63667f4addbSFrank Haverkamp 63767f4addbSFrank Haverkamp /** 63867f4addbSFrank Haverkamp * genwqe_reset_interrupt_capability() - Undo genwqe_set_interrupt_capability() 63967f4addbSFrank Haverkamp * @cd: pointer to the device 64067f4addbSFrank Haverkamp */ 64167f4addbSFrank Haverkamp void genwqe_reset_interrupt_capability(struct genwqe_dev *cd) 64267f4addbSFrank Haverkamp { 64367f4addbSFrank Haverkamp struct pci_dev *pci_dev = cd->pci_dev; 64467f4addbSFrank Haverkamp 64567f4addbSFrank Haverkamp if (cd->flags & GENWQE_FLAG_MSI_ENABLED) { 64667f4addbSFrank Haverkamp pci_disable_msi(pci_dev); 64767f4addbSFrank Haverkamp cd->flags &= ~GENWQE_FLAG_MSI_ENABLED; 64867f4addbSFrank Haverkamp } 64967f4addbSFrank Haverkamp } 65067f4addbSFrank Haverkamp 65167f4addbSFrank Haverkamp /** 65267f4addbSFrank Haverkamp * set_reg_idx() - Fill array with data. Ignore illegal offsets. 65367f4addbSFrank Haverkamp * @cd: card device 65467f4addbSFrank Haverkamp * @r: debug register array 65567f4addbSFrank Haverkamp * @i: index to desired entry 65667f4addbSFrank Haverkamp * @m: maximum possible entries 65767f4addbSFrank Haverkamp * @addr: addr which is read 65867f4addbSFrank Haverkamp * @index: index in debug array 65967f4addbSFrank Haverkamp * @val: read value 66067f4addbSFrank Haverkamp */ 66167f4addbSFrank Haverkamp static int set_reg_idx(struct genwqe_dev *cd, struct genwqe_reg *r, 66267f4addbSFrank Haverkamp unsigned int *i, unsigned int m, u32 addr, u32 idx, 66367f4addbSFrank Haverkamp u64 val) 66467f4addbSFrank Haverkamp { 66567f4addbSFrank Haverkamp if (WARN_ON_ONCE(*i >= m)) 66667f4addbSFrank Haverkamp return -EFAULT; 66767f4addbSFrank Haverkamp 66867f4addbSFrank Haverkamp r[*i].addr = addr; 66967f4addbSFrank Haverkamp r[*i].idx = idx; 67067f4addbSFrank Haverkamp r[*i].val = val; 67167f4addbSFrank Haverkamp ++*i; 67267f4addbSFrank Haverkamp return 0; 67367f4addbSFrank Haverkamp } 67467f4addbSFrank Haverkamp 67567f4addbSFrank Haverkamp static int set_reg(struct genwqe_dev *cd, struct genwqe_reg *r, 67667f4addbSFrank Haverkamp unsigned int *i, unsigned int m, u32 addr, u64 val) 67767f4addbSFrank Haverkamp { 67867f4addbSFrank Haverkamp return set_reg_idx(cd, r, i, m, addr, 0, val); 67967f4addbSFrank Haverkamp } 68067f4addbSFrank Haverkamp 68167f4addbSFrank Haverkamp int genwqe_read_ffdc_regs(struct genwqe_dev *cd, struct genwqe_reg *regs, 68267f4addbSFrank Haverkamp unsigned int max_regs, int all) 68367f4addbSFrank Haverkamp { 68467f4addbSFrank Haverkamp unsigned int i, j, idx = 0; 68567f4addbSFrank Haverkamp u32 ufir_addr, ufec_addr, sfir_addr, sfec_addr; 68667f4addbSFrank Haverkamp u64 gfir, sluid, appid, ufir, ufec, sfir, sfec; 68767f4addbSFrank Haverkamp 68867f4addbSFrank Haverkamp /* Global FIR */ 68967f4addbSFrank Haverkamp gfir = __genwqe_readq(cd, IO_SLC_CFGREG_GFIR); 69067f4addbSFrank Haverkamp set_reg(cd, regs, &idx, max_regs, IO_SLC_CFGREG_GFIR, gfir); 69167f4addbSFrank Haverkamp 69267f4addbSFrank Haverkamp /* UnitCfg for SLU */ 69367f4addbSFrank Haverkamp sluid = __genwqe_readq(cd, IO_SLU_UNITCFG); /* 0x00000000 */ 69467f4addbSFrank Haverkamp set_reg(cd, regs, &idx, max_regs, IO_SLU_UNITCFG, sluid); 69567f4addbSFrank Haverkamp 69667f4addbSFrank Haverkamp /* UnitCfg for APP */ 69767f4addbSFrank Haverkamp appid = __genwqe_readq(cd, IO_APP_UNITCFG); /* 0x02000000 */ 69867f4addbSFrank Haverkamp set_reg(cd, regs, &idx, max_regs, IO_APP_UNITCFG, appid); 69967f4addbSFrank Haverkamp 70067f4addbSFrank Haverkamp /* Check all chip Units */ 70167f4addbSFrank Haverkamp for (i = 0; i < GENWQE_MAX_UNITS; i++) { 70267f4addbSFrank Haverkamp 70367f4addbSFrank Haverkamp /* Unit FIR */ 70467f4addbSFrank Haverkamp ufir_addr = (i << 24) | 0x008; 70567f4addbSFrank Haverkamp ufir = __genwqe_readq(cd, ufir_addr); 70667f4addbSFrank Haverkamp set_reg(cd, regs, &idx, max_regs, ufir_addr, ufir); 70767f4addbSFrank Haverkamp 70867f4addbSFrank Haverkamp /* Unit FEC */ 70967f4addbSFrank Haverkamp ufec_addr = (i << 24) | 0x018; 71067f4addbSFrank Haverkamp ufec = __genwqe_readq(cd, ufec_addr); 71167f4addbSFrank Haverkamp set_reg(cd, regs, &idx, max_regs, ufec_addr, ufec); 71267f4addbSFrank Haverkamp 71367f4addbSFrank Haverkamp for (j = 0; j < 64; j++) { 71467f4addbSFrank Haverkamp /* wherever there is a primary 1, read the 2ndary */ 71567f4addbSFrank Haverkamp if (!all && (!(ufir & (1ull << j)))) 71667f4addbSFrank Haverkamp continue; 71767f4addbSFrank Haverkamp 71867f4addbSFrank Haverkamp sfir_addr = (i << 24) | (0x100 + 8 * j); 71967f4addbSFrank Haverkamp sfir = __genwqe_readq(cd, sfir_addr); 72067f4addbSFrank Haverkamp set_reg(cd, regs, &idx, max_regs, sfir_addr, sfir); 72167f4addbSFrank Haverkamp 72267f4addbSFrank Haverkamp sfec_addr = (i << 24) | (0x300 + 8 * j); 72367f4addbSFrank Haverkamp sfec = __genwqe_readq(cd, sfec_addr); 72467f4addbSFrank Haverkamp set_reg(cd, regs, &idx, max_regs, sfec_addr, sfec); 72567f4addbSFrank Haverkamp } 72667f4addbSFrank Haverkamp } 72767f4addbSFrank Haverkamp 72867f4addbSFrank Haverkamp /* fill with invalid data until end */ 72967f4addbSFrank Haverkamp for (i = idx; i < max_regs; i++) { 73067f4addbSFrank Haverkamp regs[i].addr = 0xffffffff; 73167f4addbSFrank Haverkamp regs[i].val = 0xffffffffffffffffull; 73267f4addbSFrank Haverkamp } 73367f4addbSFrank Haverkamp return idx; 73467f4addbSFrank Haverkamp } 73567f4addbSFrank Haverkamp 73667f4addbSFrank Haverkamp /** 73767f4addbSFrank Haverkamp * genwqe_ffdc_buff_size() - Calculates the number of dump registers 73867f4addbSFrank Haverkamp */ 73967f4addbSFrank Haverkamp int genwqe_ffdc_buff_size(struct genwqe_dev *cd, int uid) 74067f4addbSFrank Haverkamp { 74167f4addbSFrank Haverkamp int entries = 0, ring, traps, traces, trace_entries; 74267f4addbSFrank Haverkamp u32 eevptr_addr, l_addr, d_len, d_type; 74367f4addbSFrank Haverkamp u64 eevptr, val, addr; 74467f4addbSFrank Haverkamp 74567f4addbSFrank Haverkamp eevptr_addr = GENWQE_UID_OFFS(uid) | IO_EXTENDED_ERROR_POINTER; 74667f4addbSFrank Haverkamp eevptr = __genwqe_readq(cd, eevptr_addr); 74767f4addbSFrank Haverkamp 74867f4addbSFrank Haverkamp if ((eevptr != 0x0) && (eevptr != -1ull)) { 74967f4addbSFrank Haverkamp l_addr = GENWQE_UID_OFFS(uid) | eevptr; 75067f4addbSFrank Haverkamp 75167f4addbSFrank Haverkamp while (1) { 75267f4addbSFrank Haverkamp val = __genwqe_readq(cd, l_addr); 75367f4addbSFrank Haverkamp 75467f4addbSFrank Haverkamp if ((val == 0x0) || (val == -1ull)) 75567f4addbSFrank Haverkamp break; 75667f4addbSFrank Haverkamp 75767f4addbSFrank Haverkamp /* 38:24 */ 75867f4addbSFrank Haverkamp d_len = (val & 0x0000007fff000000ull) >> 24; 75967f4addbSFrank Haverkamp 76067f4addbSFrank Haverkamp /* 39 */ 76167f4addbSFrank Haverkamp d_type = (val & 0x0000008000000000ull) >> 36; 76267f4addbSFrank Haverkamp 76367f4addbSFrank Haverkamp if (d_type) { /* repeat */ 76467f4addbSFrank Haverkamp entries += d_len; 76567f4addbSFrank Haverkamp } else { /* size in bytes! */ 76667f4addbSFrank Haverkamp entries += d_len >> 3; 76767f4addbSFrank Haverkamp } 76867f4addbSFrank Haverkamp 76967f4addbSFrank Haverkamp l_addr += 8; 77067f4addbSFrank Haverkamp } 77167f4addbSFrank Haverkamp } 77267f4addbSFrank Haverkamp 77367f4addbSFrank Haverkamp for (ring = 0; ring < 8; ring++) { 77467f4addbSFrank Haverkamp addr = GENWQE_UID_OFFS(uid) | IO_EXTENDED_DIAG_MAP(ring); 77567f4addbSFrank Haverkamp val = __genwqe_readq(cd, addr); 77667f4addbSFrank Haverkamp 77767f4addbSFrank Haverkamp if ((val == 0x0ull) || (val == -1ull)) 77867f4addbSFrank Haverkamp continue; 77967f4addbSFrank Haverkamp 78067f4addbSFrank Haverkamp traps = (val >> 24) & 0xff; 78167f4addbSFrank Haverkamp traces = (val >> 16) & 0xff; 78267f4addbSFrank Haverkamp trace_entries = val & 0xffff; 78367f4addbSFrank Haverkamp 78467f4addbSFrank Haverkamp entries += traps + (traces * trace_entries); 78567f4addbSFrank Haverkamp } 78667f4addbSFrank Haverkamp return entries; 78767f4addbSFrank Haverkamp } 78867f4addbSFrank Haverkamp 78967f4addbSFrank Haverkamp /** 79067f4addbSFrank Haverkamp * genwqe_ffdc_buff_read() - Implements LogoutExtendedErrorRegisters procedure 79167f4addbSFrank Haverkamp */ 79267f4addbSFrank Haverkamp int genwqe_ffdc_buff_read(struct genwqe_dev *cd, int uid, 79367f4addbSFrank Haverkamp struct genwqe_reg *regs, unsigned int max_regs) 79467f4addbSFrank Haverkamp { 79567f4addbSFrank Haverkamp int i, traps, traces, trace, trace_entries, trace_entry, ring; 79667f4addbSFrank Haverkamp unsigned int idx = 0; 79767f4addbSFrank Haverkamp u32 eevptr_addr, l_addr, d_addr, d_len, d_type; 79867f4addbSFrank Haverkamp u64 eevptr, e, val, addr; 79967f4addbSFrank Haverkamp 80067f4addbSFrank Haverkamp eevptr_addr = GENWQE_UID_OFFS(uid) | IO_EXTENDED_ERROR_POINTER; 80167f4addbSFrank Haverkamp eevptr = __genwqe_readq(cd, eevptr_addr); 80267f4addbSFrank Haverkamp 80367f4addbSFrank Haverkamp if ((eevptr != 0x0) && (eevptr != 0xffffffffffffffffull)) { 80467f4addbSFrank Haverkamp l_addr = GENWQE_UID_OFFS(uid) | eevptr; 80567f4addbSFrank Haverkamp while (1) { 80667f4addbSFrank Haverkamp e = __genwqe_readq(cd, l_addr); 80767f4addbSFrank Haverkamp if ((e == 0x0) || (e == 0xffffffffffffffffull)) 80867f4addbSFrank Haverkamp break; 80967f4addbSFrank Haverkamp 81067f4addbSFrank Haverkamp d_addr = (e & 0x0000000000ffffffull); /* 23:0 */ 81167f4addbSFrank Haverkamp d_len = (e & 0x0000007fff000000ull) >> 24; /* 38:24 */ 81267f4addbSFrank Haverkamp d_type = (e & 0x0000008000000000ull) >> 36; /* 39 */ 81367f4addbSFrank Haverkamp d_addr |= GENWQE_UID_OFFS(uid); 81467f4addbSFrank Haverkamp 81567f4addbSFrank Haverkamp if (d_type) { 81667f4addbSFrank Haverkamp for (i = 0; i < (int)d_len; i++) { 81767f4addbSFrank Haverkamp val = __genwqe_readq(cd, d_addr); 81867f4addbSFrank Haverkamp set_reg_idx(cd, regs, &idx, max_regs, 81967f4addbSFrank Haverkamp d_addr, i, val); 82067f4addbSFrank Haverkamp } 82167f4addbSFrank Haverkamp } else { 82267f4addbSFrank Haverkamp d_len >>= 3; /* Size in bytes! */ 82367f4addbSFrank Haverkamp for (i = 0; i < (int)d_len; i++, d_addr += 8) { 82467f4addbSFrank Haverkamp val = __genwqe_readq(cd, d_addr); 82567f4addbSFrank Haverkamp set_reg_idx(cd, regs, &idx, max_regs, 82667f4addbSFrank Haverkamp d_addr, 0, val); 82767f4addbSFrank Haverkamp } 82867f4addbSFrank Haverkamp } 82967f4addbSFrank Haverkamp l_addr += 8; 83067f4addbSFrank Haverkamp } 83167f4addbSFrank Haverkamp } 83267f4addbSFrank Haverkamp 83367f4addbSFrank Haverkamp /* 83467f4addbSFrank Haverkamp * To save time, there are only 6 traces poplulated on Uid=2, 83567f4addbSFrank Haverkamp * Ring=1. each with iters=512. 83667f4addbSFrank Haverkamp */ 83767f4addbSFrank Haverkamp for (ring = 0; ring < 8; ring++) { /* 0 is fls, 1 is fds, 83867f4addbSFrank Haverkamp 2...7 are ASI rings */ 83967f4addbSFrank Haverkamp addr = GENWQE_UID_OFFS(uid) | IO_EXTENDED_DIAG_MAP(ring); 84067f4addbSFrank Haverkamp val = __genwqe_readq(cd, addr); 84167f4addbSFrank Haverkamp 84267f4addbSFrank Haverkamp if ((val == 0x0ull) || (val == -1ull)) 84367f4addbSFrank Haverkamp continue; 84467f4addbSFrank Haverkamp 84567f4addbSFrank Haverkamp traps = (val >> 24) & 0xff; /* Number of Traps */ 84667f4addbSFrank Haverkamp traces = (val >> 16) & 0xff; /* Number of Traces */ 84767f4addbSFrank Haverkamp trace_entries = val & 0xffff; /* Entries per trace */ 84867f4addbSFrank Haverkamp 84967f4addbSFrank Haverkamp /* Note: This is a combined loop that dumps both the traps */ 85067f4addbSFrank Haverkamp /* (for the trace == 0 case) as well as the traces 1 to */ 85167f4addbSFrank Haverkamp /* 'traces'. */ 85267f4addbSFrank Haverkamp for (trace = 0; trace <= traces; trace++) { 85367f4addbSFrank Haverkamp u32 diag_sel = 85467f4addbSFrank Haverkamp GENWQE_EXTENDED_DIAG_SELECTOR(ring, trace); 85567f4addbSFrank Haverkamp 85667f4addbSFrank Haverkamp addr = (GENWQE_UID_OFFS(uid) | 85767f4addbSFrank Haverkamp IO_EXTENDED_DIAG_SELECTOR); 85867f4addbSFrank Haverkamp __genwqe_writeq(cd, addr, diag_sel); 85967f4addbSFrank Haverkamp 86067f4addbSFrank Haverkamp for (trace_entry = 0; 86167f4addbSFrank Haverkamp trace_entry < (trace ? trace_entries : traps); 86267f4addbSFrank Haverkamp trace_entry++) { 86367f4addbSFrank Haverkamp addr = (GENWQE_UID_OFFS(uid) | 86467f4addbSFrank Haverkamp IO_EXTENDED_DIAG_READ_MBX); 86567f4addbSFrank Haverkamp val = __genwqe_readq(cd, addr); 86667f4addbSFrank Haverkamp set_reg_idx(cd, regs, &idx, max_regs, addr, 86767f4addbSFrank Haverkamp (diag_sel<<16) | trace_entry, val); 86867f4addbSFrank Haverkamp } 86967f4addbSFrank Haverkamp } 87067f4addbSFrank Haverkamp } 87167f4addbSFrank Haverkamp return 0; 87267f4addbSFrank Haverkamp } 87367f4addbSFrank Haverkamp 87467f4addbSFrank Haverkamp /** 87567f4addbSFrank Haverkamp * genwqe_write_vreg() - Write register in virtual window 87667f4addbSFrank Haverkamp * 87767f4addbSFrank Haverkamp * Note, these registers are only accessible to the PF through the 87867f4addbSFrank Haverkamp * VF-window. It is not intended for the VF to access. 87967f4addbSFrank Haverkamp */ 88067f4addbSFrank Haverkamp int genwqe_write_vreg(struct genwqe_dev *cd, u32 reg, u64 val, int func) 88167f4addbSFrank Haverkamp { 88267f4addbSFrank Haverkamp __genwqe_writeq(cd, IO_PF_SLC_VIRTUAL_WINDOW, func & 0xf); 88367f4addbSFrank Haverkamp __genwqe_writeq(cd, reg, val); 88467f4addbSFrank Haverkamp return 0; 88567f4addbSFrank Haverkamp } 88667f4addbSFrank Haverkamp 88767f4addbSFrank Haverkamp /** 88867f4addbSFrank Haverkamp * genwqe_read_vreg() - Read register in virtual window 88967f4addbSFrank Haverkamp * 89067f4addbSFrank Haverkamp * Note, these registers are only accessible to the PF through the 89167f4addbSFrank Haverkamp * VF-window. It is not intended for the VF to access. 89267f4addbSFrank Haverkamp */ 89367f4addbSFrank Haverkamp u64 genwqe_read_vreg(struct genwqe_dev *cd, u32 reg, int func) 89467f4addbSFrank Haverkamp { 89567f4addbSFrank Haverkamp __genwqe_writeq(cd, IO_PF_SLC_VIRTUAL_WINDOW, func & 0xf); 89667f4addbSFrank Haverkamp return __genwqe_readq(cd, reg); 89767f4addbSFrank Haverkamp } 89867f4addbSFrank Haverkamp 89967f4addbSFrank Haverkamp /** 90067f4addbSFrank Haverkamp * genwqe_base_clock_frequency() - Deteremine base clock frequency of the card 90167f4addbSFrank Haverkamp * 90267f4addbSFrank Haverkamp * Note: From a design perspective it turned out to be a bad idea to 90367f4addbSFrank Haverkamp * use codes here to specifiy the frequency/speed values. An old 90467f4addbSFrank Haverkamp * driver cannot understand new codes and is therefore always a 90567f4addbSFrank Haverkamp * problem. Better is to measure out the value or put the 90667f4addbSFrank Haverkamp * speed/frequency directly into a register which is always a valid 90767f4addbSFrank Haverkamp * value for old as well as for new software. 90867f4addbSFrank Haverkamp * 90967f4addbSFrank Haverkamp * Return: Card clock in MHz 91067f4addbSFrank Haverkamp */ 91167f4addbSFrank Haverkamp int genwqe_base_clock_frequency(struct genwqe_dev *cd) 91267f4addbSFrank Haverkamp { 91367f4addbSFrank Haverkamp u16 speed; /* MHz MHz MHz MHz */ 91467f4addbSFrank Haverkamp static const int speed_grade[] = { 250, 200, 166, 175 }; 91567f4addbSFrank Haverkamp 91667f4addbSFrank Haverkamp speed = (u16)((cd->slu_unitcfg >> 28) & 0x0full); 91767f4addbSFrank Haverkamp if (speed >= ARRAY_SIZE(speed_grade)) 91867f4addbSFrank Haverkamp return 0; /* illegal value */ 91967f4addbSFrank Haverkamp 92067f4addbSFrank Haverkamp return speed_grade[speed]; 92167f4addbSFrank Haverkamp } 92267f4addbSFrank Haverkamp 92367f4addbSFrank Haverkamp /** 92467f4addbSFrank Haverkamp * genwqe_stop_traps() - Stop traps 92567f4addbSFrank Haverkamp * 92667f4addbSFrank Haverkamp * Before reading out the analysis data, we need to stop the traps. 92767f4addbSFrank Haverkamp */ 92867f4addbSFrank Haverkamp void genwqe_stop_traps(struct genwqe_dev *cd) 92967f4addbSFrank Haverkamp { 93067f4addbSFrank Haverkamp __genwqe_writeq(cd, IO_SLC_MISC_DEBUG_SET, 0xcull); 93167f4addbSFrank Haverkamp } 93267f4addbSFrank Haverkamp 93367f4addbSFrank Haverkamp /** 93467f4addbSFrank Haverkamp * genwqe_start_traps() - Start traps 93567f4addbSFrank Haverkamp * 93667f4addbSFrank Haverkamp * After having read the data, we can/must enable the traps again. 93767f4addbSFrank Haverkamp */ 93867f4addbSFrank Haverkamp void genwqe_start_traps(struct genwqe_dev *cd) 93967f4addbSFrank Haverkamp { 94067f4addbSFrank Haverkamp __genwqe_writeq(cd, IO_SLC_MISC_DEBUG_CLR, 0xcull); 94167f4addbSFrank Haverkamp 94267f4addbSFrank Haverkamp if (genwqe_need_err_masking(cd)) 94367f4addbSFrank Haverkamp __genwqe_writeq(cd, IO_SLC_MISC_DEBUG, 0x0aull); 94467f4addbSFrank Haverkamp } 945