* Copyright 2010-2017 Intel Corporation.
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
* as published by the Free Software Foundation.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
* Disclaimer: The codes contained in these modules may be specific to
* the Intel Software Development Platform codenamed Knights Ferry,
* and the Intel product codenamed Knights Corner, and are not backward
* compatible with other Intel products. Additionally, Intel will NOT
* support the codes or instruction set in future products.
* Intel offers no warranty of any kind regarding the code. This code is
* licensed on an "AS IS" basis and Intel is not obligated to provide
* any support, assistance, installation, training, or other services
* of any kind. Intel is also not obligated to provide any updates,
* enhancements or extensions. Intel specifically disclaims any warranty
* of merchantability, non-infringement, fitness for any particular
* purpose, and any other warranty.
* Further, Intel disclaims all liability of any kind, including but
* not limited to liability for infringement of any proprietary rights,
* relating to the use of the code, even if Intel is notified of the
* possibility of such liability. Except as expressly stated in an Intel
* license agreement provided with this code and agreed upon with Intel,
* no license, express or implied, by estoppel or otherwise, to any
* intellectual property rights is granted herein.
/* contains code to download uos on MIC card */
#include <mic/ringbuffer.h>
#include <linux/virtio_ring.h>
#include <linux/virtio_blk.h>
#include "mic/mic_virtio.h"
#include <linux/proc_fs.h>
#define APERTURE_SEGMENT_SIZE ((1) * 1024 * 1024 * 1024ULL)
#define UOS_RESERVE_SIZE_MIN ((128) * 1024 * 1024)
#define OS_RESERVE_SIZE_MIN ((32) * 1024 * 1024)
#define UOS_RESERVE_SIZE_MAX (((4) * 1024 * 1024 * 1024ULL) - ((4) * 1024))
#define UOS_RESERVE_PERCENT 50
#define UOS_WATCHDOG_TIMEOUT 5000 // default watchdog timeout in milliseconds
#define PCIE_CLASS_CODE(x) ((x) >> 24 )
/* zombie class code as per the HAS is 0xFF
* but on KNC, we found it as 0x03
#define ZOMBIE_CLASS_CODE 0x03
#define RESET_FAILED_F2 12870
#define RESET_FAILED_F4 13382
void ramoops_remove(mic_ctx_t
*mic_ctx
);
static struct proc_dir_entry
*ramoops_dir
;
struct proc_dir_entry
*vmcore_dir
;
static void adapter_dpc(unsigned long dpc
);
extern int mic_vhost_blk_probe(bd_info_t
*bd_info
);
extern void mic_vhost_blk_remove(bd_info_t
*bd_info
);
/* driver wide global common data */
extern int usagemode_param
;
extern bool mic_crash_dump_enabled
;
extern bool mic_watchdog_auto_reboot
;
static int64_t etc_comp
= 0;
etc_read(uint8_t *mmio_va
)
hi1
= SBOX_READ(mmio_va
, SBOX_ELAPSED_TIME_HIGH
);
low
= SBOX_READ(mmio_va
, SBOX_ELAPSED_TIME_LOW
);
hi2
= SBOX_READ(mmio_va
, SBOX_ELAPSED_TIME_HIGH
);
return((uint64_t)((((uint64_t)hi1
<< 32) | low
) >> 5));
calc_deltaf(mic_ctx_t
*mic_ctx
)
const int64_t ETC_CLK_FREQ
= 15625000;
const uint32_t TIME_DELAY_IN_SEC
= 10;
const int64_t etc_cnt1
= ETC_CLK_FREQ
* TIME_DELAY_IN_SEC
;
int64_t deltaf_in_ppm
, deltaf
;
* (etc_freq2 / etc_freq1) = (etc_count2 / etc_count1)
* etc_freq1 = ETC_CLK_FREQ
* => etc_count1 = TIME_DELAY_IN_SEC * ETC_CLK_FREQ
* (etc_freq2 / etc_freq1) = (etc_count2 / etc_count1)
* etc_freq2 = etc_freq1 * (etc_count2 / etc_count1)
* etc_freq2 - etc_freq1 = etc_freq1((etc_count2 / etc_count1) - 1)
* deltaf = etc_freq1(etc_count2 - etc_count1)/etc_count1
* deltaf_in_ppm = deltaf * 10 ^ 6 / etc_freq1
* deltaf_in_ppm = ((etc_count2 - etc_count1) * 10 ^ 6) / etc_count1
/* Need to implement the monotonic/irqsave logic for windows */
struct timespec ts1
, ts2
;
cnt1
= etc_read(mic_ctx
->mmio
.va
);
local_irq_restore(flags
);
mdelay(TIME_DELAY_IN_SEC
* 1000);
cnt2
= etc_read(mic_ctx
->mmio
.va
);
local_irq_restore(flags
);
ts2
= timespec_sub(ts2
, ts1
);
mono_ns
= timespec_to_ns(&ts2
);
/* Recalculate etc_cnt2 based on getrawmonotonic */
etc_cnt2
= (etc_cnt2
* TIME_DELAY_IN_SEC
* 1000 * 1000 * 1000) / mono_ns
;
deltaf
= ( ETC_CLK_FREQ
* (etc_cnt2
- etc_cnt1
)) / etc_cnt1
;
deltaf_in_ppm
= (1000 * 1000 * (etc_cnt2
- etc_cnt1
)) / etc_cnt1
;
* On some of the systems deltaf_in_ppm is turning out
* way higher than expected. The only reasons I can think of
* i) mmio traffic cauing variable delays for mmio read
* ii) NMIs affecting this code
} while (i
< 10 && (deltaf_in_ppm
> 2700 || deltaf_in_ppm
< -2700));
pr_debug("etc deltaf: %lld\n", deltaf
);
* For intel chipsets, Spread Spectrum Clocking (SSC) (in the limit)
* is downspread with a frequency of 30hz and an amplitude of 0.5%
* which translates to 2500ppm. This is also the ppm observed on KNC + CrownPass
* Hence, if ppm > 2500, the code would need to retry to eliminate any chance of error
* Added an error margin of 1ppm (etc mmio reads can take really long time)
if (deltaf_in_ppm
> 2700 || deltaf_in_ppm
< -2700) {
printk(KERN_ERR
"ETC timer compensation(%lldppm) is much higher"
"than expected\n", deltaf_in_ppm
);
* Clamp etc compensation to 2500ppm
if (deltaf_in_ppm
> 2700)
deltaf
= (ETC_CLK_FREQ
* deltaf_in_ppm
) / (1000 * 1000);
if (deltaf
> 0 && deltaf
<= 10)
calculate_etc_compensation(mic_ctx_t
*mic_ctx
)
if (mic_ctx
->bi_family
== FAMILY_KNC
) {
etc_comp
= calc_deltaf(mic_ctx
);
mic_ctx
->etc_comp
= etc_comp
;
DESCRIPTION:: waits for bootstrap loader is finished
[in]void *mmio_va - virtual address to access MMIO registers
RETURN_VALUE:: 0 if successful, non-zero if failure
wait_for_bootstrap(uint8_t *mmio_va
)
// Wait until the boot loader is finished
while (!SCRATCH2_DOWNLOAD_STATUS(scratch2
)) {
printk("Firmware is not responding with ready bit\n");
/* We don't want to be polling too often on the emulator, it is SLOW! */
pr_debug("Wait for bootstrap: %d min(s) \n", wait_time
++);
scratch2
= SBOX_READ(mmio_va
, SBOX_SCRATCH2
);
DESCRIPTION::gets adapter memory size. calculates size based on scratch register 0
[in]void *mmio_va - virtual address to access MMIO registers
[out]uint32_t *adapter_mem_size - adapter memory size
get_adapter_memsize(uint8_t *mmio_va
, uint32_t *adapter_mem_size
)
scratch0
= SBOX_READ(mmio_va
, SBOX_SCRATCH0
);
memsize
= SCRATCH0_MEM_SIZE_KB(scratch0
) * ((1) * 1024);
// Adjust the memory size based on the memory usage
switch (SCRATCH0_MEM_USAGE(scratch0
)) {
// DBG_ASSERT_MSG(false, "Invalid memory usage specified by the bootstrap.\n");
*adapter_mem_size
= memsize
;
DESCRIPTION:: gets uos load offset from scratch register 2
[in]void *mmio_va - virtual address to access MMIO registers
[out]uint32_t *uos_load_offset - offset at which uos will be loaded
get_uos_loadoffset(uint8_t *mmio_va
, uint32_t *uos_load_offset
)
scratch2
= SBOX_READ(mmio_va
, SBOX_SCRATCH2
);
*uos_load_offset
= SCRATCH2_DOWNLOAD_ADDR(scratch2
);
DESCRIPTION:: gets reserved size for uos
[out]uint32_t *uos_reserve_size - reserved uos size
get_uos_reserved_size(uint8_t* mmio_va
, uint32_t adapter_memsize
, uint32_t *uos_reserve_size
)
uint32_t reserve_size
= 0;
// Only calculate if not explicitly specified by the user
reserve_size
= (uint32_t)(adapter_memsize
* UOS_RESERVE_PERCENT
/ 100);
// Make sure there is at least WINDOWS_RESERVE_SIZE_MIN bytes
reserve_size
= GET_MIN(reserve_size
, adapter_memsize
- OS_RESERVE_SIZE_MIN
);
// Keep in mind maximum uos reserve size is uint32_t, so we never overflow
reserve_size
= GET_MIN(reserve_size
, UOS_RESERVE_SIZE_MAX
);
reserve_size
= GET_MAX(reserve_size
, UOS_RESERVE_SIZE_MIN
);
// Always align uos reserve size to a page
reserve_size
= (uint32_t)AlignLow(reserve_size
, ((4) * 1024));
*uos_reserve_size
= reserve_size
;
DESCRIPTION:: gets APIC ID from scratch register 2
[in]void *mmio_va - virtual address to access MMIO registers
[out]uint32_t *apic_id - apic id
get_apic_id(uint8_t *mmio_va
, uint32_t *apic_id
)
scratch2
= SBOX_READ(mmio_va
, SBOX_SCRATCH2
);
*apic_id
= SCRATCH2_APIC_ID(scratch2
);
DESCRIPTION::program the PCI aperture as a contiguous window. (only supports upto 4GB memory)
[in]mic_ctx_t *mic_ctx - mic ctx
[in]int gtt_index - beginning gtt entry index
[in]uint64_t phy_addr - physical address for PCI aperture
[in]uint32_t num_bytes - size of PCI aperture
set_pci_aperture(mic_ctx_t
*mic_ctx
, uint32_t gtt_index
, uint64_t phy_addr
, uint32_t num_bytes
)
num_pages
= ALIGN(num_bytes
, PAGE_SIZE
) >> PAGE_SHIFT
;
for (i
= 0; i
< num_pages
; i
++) {
gtt_entry
= ((uint32_t)(phy_addr
>> PAGE_SHIFT
) + i
) << 1 | 0x1u
;
GTT_WRITE(gtt_entry
, mic_ctx
->mmio
.va
, (gtt_index
+ i
)*sizeof(gtt_entry
));
// Writing GttTlbFlushReg DOES NOT flush all write transactions from SBOX to GDDR
// because GttTlbFlushReg is an SBOX register and transaction terminates in SBOX
// MMIO write must use MIC ringbus to be serializing.
// Writing GTT itself DOES serialize: GTT is in MMIO space, and write goes to the ringbus
// MemoryBarrier makes sure all writes make it to GDDR before tlbFlush write
smp_mb(); // FIXME: only needs SFENCE
// write any value to cause a flush
SBOX_WRITE(1, mic_ctx
->mmio
.va
, SBOX_TLB_FLUSH
);
DESCRIPTION:: Programs a scratch register that the bootstrap reads to determine
[in]void *mmio_va - virtual address to mmio register,
[in]uint32_t uos_size - size of uos image
set_uos_size(uint8_t *mmio_va
, uint32_t uos_size
)
// XPU_RACE_CONDITION: write to MMIO space is uncached and flushes WC buffers
SBOX_WRITE(scratch5
, mmio_va
, SBOX_SCRATCH5
);
DESCRIPTION:: Programs a scratch register that the uOS reads to determine how
[in]void *mmio_va - virtual address to mmio register,
[in]uint32_t uos_reserved_size - size of memory to be reserved by uos.
set_uos_reserved_size(uint8_t *mmio_va
, uint32_t uos_reserved_size
)
scratch3
= uos_reserved_size
;
// XPU_RACE_CONDITION: write to MMIO space is uncached and flushes WC buffers
SBOX_WRITE(scratch3
, mmio_va
, SBOX_SCRATCH3
);
[in]uint32_t device_id - device ID,
RETURN_VALUE:: family type
get_product_family(uint32_t device_id
)
product_family_t product_family
;
case PCI_DEVICE_ABR_2249
:
case PCI_DEVICE_ABR_224a
:
product_family
= FAMILY_ABR
;
case PCI_DEVICE_KNC_2250
:
case PCI_DEVICE_KNC_2251
:
case PCI_DEVICE_KNC_2252
:
case PCI_DEVICE_KNC_2253
:
case PCI_DEVICE_KNC_2254
:
case PCI_DEVICE_KNC_2255
:
case PCI_DEVICE_KNC_2256
:
case PCI_DEVICE_KNC_2257
:
case PCI_DEVICE_KNC_2258
:
case PCI_DEVICE_KNC_2259
:
case PCI_DEVICE_KNC_225a
:
case PCI_DEVICE_KNC_225b
:
case PCI_DEVICE_KNC_225c
:
case PCI_DEVICE_KNC_225d
:
case PCI_DEVICE_KNC_225e
:
product_family
= FAMILY_KNC
;
pr_debug( "Invalid/Unknown device ID %d\r\n", device_id
);
product_family
= FAMILY_UNKNOWN
;
DESCRIPTION:: loads uos image at given path into gddr
[in]mic_ctx_t *mic_ctx - mic context
[in]imgname - file path for uos file to be loaded
[out]uos_size - size of uos image
load_uos_into_gddr(mic_ctx_t
*mic_ctx
, char *imgname
, uint32_t* uos_size
, uint64_t *uos_cmd_offset
)
uint32_t uos_load_offset
= 0;
uint32_t adapter_memsize
= 0;
aperture_va
= mic_ctx
->aper
.va
;
mmio_va
= mic_ctx
->mmio
.va
;
if (mic_ctx
->state
!= MIC_BOOT
) {
printk("Not in booting state\n");
status
= mic_get_file_size(imgname
, uos_size
);
mic_ctx
->state
= MIC_BOOTFAIL
;
printk("Linux image not found at %s , status returned %d\n", imgname
, status
);
get_uos_loadoffset(mmio_va
, &uos_load_offset
);
// Determine the uOS reserve size after we have the m_pXpu interface
get_adapter_memsize(mmio_va
, &adapter_memsize
);
get_apic_id(mmio_va
, &apic_id
);
// store apic_id in adapter context for later use
mic_ctx
->apic_id
= apic_id
;
if (mic_ctx
->bi_family
== FAMILY_ABR
){
// Program the PCI aperture as a contiguous window
// Need an extra page to provide enough buffer space for command line arguments.
set_pci_aperture(mic_ctx
, 0, uos_load_offset
, *uos_size
+ PAGE_SIZE
);
// transfer uOs image file to gddr
status
= mic_load_file(imgname
, ((uint8_t*)aperture_va
) + uos_load_offset
, *uos_size
);
// for the emulator we want to skip "downloading" the file
*uos_cmd_offset
= (uint64_t)uos_load_offset
+ *uos_size
;
// This only applies to KNF bootstrap, it is NOT needed for KNC
if (mic_ctx
->bi_family
== FAMILY_ABR
) {
// clear UOS load offset register after uOS was uploaded
SBOX_WRITE(0, mmio_va
, SBOX_SCRATCH2
);
SBOX_READ(mmio_va
, SBOX_SCRATCH2
);
DESCRIPTION:: loads uos initramfs image at given path into gddr for KNC.
[in]mic_ctx_t *mic_ctx - mic context
[in]initramfsname - file path for uos initramfs file to be loaded
load_initramfs(mic_ctx_t
*mic_ctx
, char *initramfsname
, uint32_t *initramfs_image
, uint32_t *initramfs_size
)
uint32_t uos_load_offset
= 0;
uint32_t file_load_offset
= 0;
uint32_t adapter_memsize
= 0;
uint32_t *ramfs_addr_ptr
;
aperture_va
= mic_ctx
->aper
.va
;
mmio_va
= mic_ctx
->mmio
.va
;
if (mic_ctx
->state
!= MIC_BOOT
) {
printk("Not in booting state\n");
status
= mic_get_file_size(initramfsname
, &file_size
);
mic_ctx
->state
= MIC_BOOTFAIL
;
printk("Init ram disk image not found at %s , status returned %d\n", initramfsname
, status
);
get_uos_loadoffset(mmio_va
, &uos_load_offset
);
file_load_offset
= uos_load_offset
<< 1; /* Place initramfs higher than kernel; 128MB is ok */
*initramfs_size
= file_size
;
*initramfs_image
= file_load_offset
;
// Determine the uOS reserve size after we have the m_pXpu interface
get_adapter_memsize(mmio_va
, &adapter_memsize
);
get_apic_id(mmio_va
, &apic_id
);
// store apic_id in adapter context for later use
mic_ctx
->apic_id
= apic_id
;
// transfer uOs image file to gddr
status
= mic_load_file(initramfsname
, aperture_va
+ file_load_offset
, file_size
);
// write the initramfs load address and size to the fields in the kernel header
ramfs_addr_ptr
= (uint32_t *)(aperture_va
+ uos_load_offset
+ 0x218);
*ramfs_addr_ptr
= file_load_offset
;
ramfs_addr_ptr
= (uint32_t *)(aperture_va
+ uos_load_offset
+ 0x21c);
*ramfs_addr_ptr
= *initramfs_size
;
load_command_line(mic_ctx_t
*mic_ctx
, uint64_t uos_cmd_offset
)
void *cmd_line_va
= mic_ctx
->aper
.va
+ uos_cmd_offset
;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) || defined(RHEL_RELEASE_CODE)
struct board_info
*bi
= mic_ctx
->bd_info
;
micvcons_t
*vcons
= &mic_ctx
->bi_vcons
;
dma_addr_t vc_hdr_dma_addr
= 0;
* mic_ctx->boot_mem will also be set in IOCTL to boot the card in restricted memory
* FIXME::This code is added to keep the backward compatibility with IOCTLs
if (mic_ctx
->bi_family
== FAMILY_KNC
)
if (mic_ctx
->boot_mem
== 0 || mic_ctx
->boot_mem
> mic_ctx
->aper
.len
>> 20)
mic_ctx
->boot_mem
= (uint32_t)(mic_ctx
->aper
.len
>> 20);
if (!(buf
= kzalloc(MIC_CMDLINE_BUFSIZE
, GFP_KERNEL
))) {
printk(KERN_ERR
"failed to allocate %d bytes for uOS command line\n",
cmdlen
= snprintf(buf
, MIC_CMDLINE_BUFSIZE
, "card=%d vnet=%s scif_id=%d scif_addr=0x%llx",
mic_ctx
->bi_id
, mic_vnet_modes
[mic_vnet_mode
],
mic_ctx
->bi_id
+ 1, mic_ctx
->bi_scif
.si_pa
);
if (mic_vnet_mode
== VNET_MODE_DMA
) {
struct micvnet_info
*vnet_info
= mic_ctx
->bi_vethinfo
;
cmdlen
+= snprintf(buf
+ cmdlen
, MIC_CMDLINE_BUFSIZE
- cmdlen
,
" vnet_addr=0x%llx", vnet_info
->vi_rp_phys
);
vc_hdr_dma_addr
= vcons
->dc_hdr_dma_addr
;
cmdlen
+= snprintf(buf
+ cmdlen
, MIC_CMDLINE_BUFSIZE
- cmdlen
,
" vcons_hdr_addr=0x%llx", vc_hdr_dma_addr
);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) || defined(RHEL_RELEASE_CODE)
cmdlen
+= snprintf(buf
+ cmdlen
, MIC_CMDLINE_BUFSIZE
- cmdlen
, " virtio_addr=0x%llx",
mic_ctx_map_single(mic_ctx
, bi
->bi_virtio
, sizeof(struct vb_shared
)));
cmdlen
+= snprintf(buf
+ cmdlen
, MIC_CMDLINE_BUFSIZE
- cmdlen
,
" mem=%dM", mic_ctx
->boot_mem
);
if (mic_ctx
->ramoops_size
)
cmdlen
+= snprintf(buf
+ cmdlen
, MIC_CMDLINE_BUFSIZE
- cmdlen
,
" ramoops_size=%d ramoops_addr=0x%llx",
mic_ctx
->ramoops_size
, mic_ctx
->ramoops_pa
[0]);
cmdlen
+= snprintf(buf
+ cmdlen
, MIC_CMDLINE_BUFSIZE
- cmdlen
,
" p2p=%d p2p_proxy=%d", mic_p2p_enable
, mic_p2p_proxy_enable
);
if (mic_ctx
->bi_family
== FAMILY_KNC
)
cmdlen
+= snprintf(buf
+ cmdlen
, MIC_CMDLINE_BUFSIZE
- cmdlen
,
" etc_comp=%lld", mic_ctx
->etc_comp
);
cmdlen
+= snprintf(buf
+ cmdlen
, MIC_CMDLINE_BUFSIZE
- cmdlen
,
" reg_cache=%d", mic_reg_cache_enable
);
cmdlen
+= snprintf(buf
+ cmdlen
, MIC_CMDLINE_BUFSIZE
- cmdlen
,
" ulimit=%d", mic_ulimit_check
);
cmdlen
+= snprintf(buf
+ cmdlen
, MIC_CMDLINE_BUFSIZE
- cmdlen
,
" huge_page=%d", mic_huge_page_enable
);
if (mic_crash_dump_enabled
)
cmdlen
+= snprintf(buf
+ cmdlen
, MIC_CMDLINE_BUFSIZE
- cmdlen
,
* Limitations in the Intel Jaketown and Ivytown platforms require SCIF
* to proxy P2P DMA read transfers in order to convert them into a P2P DMA
* write for better performance. The SCIF module on MIC needs the
* numa node the MIC is connected to on the host to make decisions
* about whether to proxy P2P DMA reads or not based on whether the two MIC
* devices are connected to the same QPI/socket/numa node or not.
* The assumption here is that a socket/QPI will have a unique
pr_debug("CPU family = %d, CPU model = %d\n", boot_cpu_data
.x86
, boot_cpu_data
.x86_model
);
if (mic_p2p_proxy_enable
&& (boot_cpu_data
.x86
==6) &&
(boot_cpu_data
.x86_model
== 45 || boot_cpu_data
.x86_model
== 62)) {
int numa_node
= dev_to_node(&mic_ctx
->bi_pdev
->dev
);
if (boot_cpu_data
.x86_model
== 45)
ms_info
.mi_proxy_dma_threshold
= SCIF_PROXY_DMA_THRESHOLD_JKT
;
if (boot_cpu_data
.x86_model
== 62)
ms_info
.mi_proxy_dma_threshold
= SCIF_PROXY_DMA_THRESHOLD_IVT
;
cmdlen
+= snprintf(buf
+ cmdlen
, MIC_CMDLINE_BUFSIZE
- cmdlen
,
" numa_node=%d", numa_node
);
cmdlen
+= snprintf(buf
+ cmdlen
, MIC_CMDLINE_BUFSIZE
- cmdlen
,
" p2p_proxy_thresh=%lld", ms_info
.mi_proxy_dma_threshold
);
if (mic_ctx
->sysfs_info
.cmdline
!= NULL
)
snprintf(buf
+ cmdlen
, MIC_CMDLINE_BUFSIZE
- cmdlen
,
" %s", mic_ctx
->sysfs_info
.cmdline
);
snprintf(buf
+ cmdlen
, MIC_CMDLINE_BUFSIZE
- cmdlen
,
" hostname=mic%d ipaddr=171.31.%d.2 quiet console=ttyS0,115200n8",
mic_ctx
->bi_id
, mic_ctx
->bi_id
+ 1);
memcpy_toio(cmd_line_va
, buf
, strlen(buf
) + 1);
if (mic_ctx
->sysfs_info
.kernel_cmdline
!= NULL
)
kfree(mic_ctx
->sysfs_info
.kernel_cmdline
);
if ((mic_ctx
->sysfs_info
.kernel_cmdline
= kmalloc(strlen(buf
) + 1, GFP_KERNEL
)) != NULL
)
strcpy(mic_ctx
->sysfs_info
.kernel_cmdline
, buf
);
DESCRIPTION:: method responsible for programming scratch register with uos image size
and notifying bootstrap to start booting uos
[in]mic_ctx_t *mic_ctx - mic context
[in]uint32_t uos_size - size of uos image
notify_uosboot(mic_ctx_t
*mic_ctx
, uint32_t uos_size
)
uint32_t adapter_memsize
= 0;
uint32_t uos_reserved_size
= 0;
uint8_t* mmio_va
= mic_ctx
->mmio
.va
;
// Program the register with uOS image size for bootstrap
set_uos_size(mmio_va
, uos_size
);
get_adapter_memsize(mmio_va
, &adapter_memsize
);
// Program the register to inform the uOS of how much space to reserve
get_uos_reserved_size(mmio_va
, adapter_memsize
, &uos_reserved_size
);
set_uos_reserved_size(mmio_va
, uos_reserved_size
);
mic_send_bootstrap_intr(mic_ctx
);
DESCRIPTION :: boots Linux OS on the card
[in]mic_ctx_t *mic_ctx - mic context
[in]char *imgname - file path for uos image to be loaded on the card
RETURN_VALUE:: 0 if successful, non-zero if failure
boot_linux_uos(mic_ctx_t
*mic_ctx
, char *imgname
, char *initramfsname
)
uint64_t uos_cmd_offset
= 0;
uint32_t initramfs_image
= 0;
uint32_t initramfs_size
= 0;
printk("MIC %d Booting\n", mic_ctx
->bi_id
);
if (mic_ctx
->state
!= MIC_BOOT
) {
printk(KERN_ERR
"MIC %d is not in offline mode\n", mic_ctx
->bi_id
);
//loads uos image at given path into gddr
if ((status
= load_uos_into_gddr(mic_ctx
, imgname
, &uos_size
, &uos_cmd_offset
)) != 0) {
printk("Cannot load uos in gddr\n");
if (initramfsname
&& (status
= load_initramfs(mic_ctx
, initramfsname
, &initramfs_image
, &initramfs_size
)) != 0) {
printk("Cannot load initramfs in gddr\n");
status
= load_command_line(mic_ctx
, uos_cmd_offset
);
//program scratch register with uos image size and notify bootstrap
status
= notify_uosboot(mic_ctx
, uos_size
);
DESCRIPTION :: boots Maintenance mode handler on the card
[in]mic_ctx_t *mic_ctx - mic context
[in]char *imgname - file path for uos image to be loaded on the card
RETURN_VALUE:: 0 if successful, non-zero if failure
int boot_micdev_app(mic_ctx_t
*mic_ctx
, char *imgname
)
uint64_t uos_cmd_offset
= 0;
int32_t temp_scratch2
= 0;
printk("MIC %d Booting\n", mic_ctx
->bi_id
);
mmio_va
= mic_ctx
->mmio
.va
;
status
= load_uos_into_gddr(mic_ctx
, imgname
, &uos_size
, &uos_cmd_offset
);
printk("Cannot load uos in gddr\n");
temp_scratch2
= SBOX_READ(mmio_va
, SBOX_SCRATCH2
);
temp_scratch2
= SCRATCH2_CLEAR_DOWNLOAD_STATUS(temp_scratch2
);
SBOX_WRITE(temp_scratch2
, mmio_va
, SBOX_SCRATCH2
);
//program scratch register with uos image size and notify bootstrap
status
= notify_uosboot(mic_ctx
, uos_size
);
status
= wait_for_bootstrap(mmio_va
);
mic_setstate(mic_ctx
, MIC_BOOTFAIL
);
mic_setstate(mic_ctx
, MIC_ONLINE
);
printk("ELF booted succesfully\n");
/* Perform hardware reset of the device */
reset_timer(struct timer_list
*arg
)
mic_ctx_t
*mic_ctx
= from_timer(mic_ctx
, arg
, boot_timer
);
uint32_t postcode
= mic_getpostcode(mic_ctx
);
printk("mic%d: Resetting (Post Code %c%c)\n", mic_ctx
->bi_id
,
postcode
& 0xff, (postcode
>> 8) & 0xff);
/* Assuming that the bootstrap takes around 90 seconds to reset,
* we fail after 300 seconds, thus allowing 3 attempts to reset
if (mic_ctx
->reset_count
== RESET_FAIL_TIME
||
!postcode
|| 0xffffffff == postcode
|| mic_ctx
->state
== MIC_RESETFAIL
) {
mic_ctx
->reset_count
= 0;
mic_setstate(mic_ctx
, MIC_RESETFAIL
);
wake_up(&mic_ctx
->resetwq
);
printk("MIC %d RESETFAIL postcode %c%c %d\n", mic_ctx
->bi_id
,
postcode
& 0xff, (postcode
>> 8) & 0xff, postcode
);
/* check for F2 or F4 error codes from bootstrap */
if ((postcode
== RESET_FAILED_F2
) || (postcode
== RESET_FAILED_F4
)) {
if (mic_ctx
->resetworkq
) {
queue_work(mic_ctx
->resetworkq
, &mic_ctx
->resetwork
);
mic_ctx
->reset_count
= 0;
mic_setstate(mic_ctx
, MIC_RESETFAIL
);
wake_up(&mic_ctx
->resetwq
);
/* checking if bootstrap is ready or still resetting */
scratch2
= SBOX_READ(mic_ctx
->mmio
.va
, SBOX_SCRATCH2
);
if (SCRATCH2_DOWNLOAD_STATUS(scratch2
)) {
mic_setstate(mic_ctx
, MIC_READY
);
mic_enable_msi_interrupts(mic_ctx
);
mic_enable_interrupts(mic_ctx
);
mic_smpt_restore(mic_ctx
);
wake_up(&mic_ctx
->resetwq
);
mic_ctx
->reset_count
= 0;
mic_ctx
->boot_timer
.expires
= jiffies
+ HZ
;
timer_setup(&mic_ctx
->boot_timer
, reset_timer
, 0);
adapter_wait_reset(mic_ctx_t
*mic_ctx
)
mic_ctx
->boot_timer
.expires
= jiffies
+ HZ
;
mic_ctx
->boot_start
= jiffies
;
timer_setup(&mic_ctx
->boot_timer
, reset_timer
, 0);
adapter_reset(mic_ctx_t
*mic_ctx
, int wait_reset
, int reattempt
)
mutex_lock(&mic_ctx
->state_lock
);
/* TODO: check state for lost node as well once design is done */
if ((mic_ctx
->state
== MIC_RESET
|| mic_ctx
->state
== MIC_READY
) && (reattempt
== 0)) {
mic_setstate(mic_ctx
, MIC_INVALID
);
del_timer_sync(&mic_ctx
->boot_timer
);
mutex_unlock(&mic_ctx
->state_lock
);
mutex_unlock(&mic_ctx
->state_lock
);
mic_setstate(mic_ctx
, MIC_RESET
);
mutex_unlock(&mic_ctx
->state_lock
);
del_timer_sync(&mic_ctx
->boot_timer
);
//Write 0 to uos download status otherwise we might continue booting
//before reset has completed...
SBOX_WRITE(0, mic_ctx
->mmio
.va
, SBOX_SCRATCH2
);
// Virtual network link value should be 0 before reset
SBOX_WRITE(0, mic_ctx
->mmio
.va
, SBOX_SCRATCH14
);
// Data from Doorbell1 about restart/shutdown should be 0 before reset
SBOX_WRITE(0, mic_ctx
->mmio
.va
, SBOX_SDBIC1
);
//This will trigger reset
resetReg
= SBOX_READ(mic_ctx
->mmio
.va
, SBOX_RGCR
);
SBOX_WRITE(resetReg
, mic_ctx
->mmio
.va
, SBOX_RGCR
);
/* At least of KNF it seems we really want to delay at least 1 second */
/* after touching reset to prevent a lot of problems. */
adapter_wait_reset(mic_ctx
);
void ramoops_flip(mic_ctx_t
*mic_ctx
);
adapter_shutdown_device(mic_ctx_t
*mic_ctx
)
if (micpm_get_reference(mic_ctx
, true))
mutex_lock(&mic_ctx
->state_lock
);
if (mic_ctx
->state
== MIC_ONLINE
) {
mic_setstate(mic_ctx
, MIC_SHUTDOWN
);
* Writing to SBOX RDMASR0 will generate an interrupt
* on the uOS which will initiate orderly shutdown.
mic_send_sht_intr(mic_ctx
);
mutex_unlock(&mic_ctx
->state_lock
);
micpm_put_reference(mic_ctx
);
adapter_stop_device(mic_ctx_t
*mic_ctx
, int wait_reset
, int reattempt
)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34) || \
defined(RHEL_RELEASE_CODE)
mic_vhost_blk_stop(mic_ctx
->bd_info
);
close_dma_device(mic_ctx
->bi_id
+ 1, &mic_ctx
->dma_handle
);
/* Calling adapter_reset after issuing Host shutdown/reboot
* leads to randon NMIs. These are not rleated to any Card in
* specific but occurs on the PCI bridge. */
if ((system_state
== SYSTEM_POWER_OFF
) ||
(system_state
== SYSTEM_RESTART
) ||
(system_state
== SYSTEM_HALT
))
adapter_reset(mic_ctx
, wait_reset
, reattempt
);
destroy_reset_workqueue(mic_ctx_t
*mic_ctx
)
struct workqueue_struct
*tempworkq
;
tempworkq
= mic_ctx
->resetworkq
;
mic_ctx
->resetworkq
= NULL
;
destroy_workqueue(tempworkq
);
del_timer_sync(&mic_ctx
->boot_timer
);
adapter_remove(mic_ctx_t
*mic_ctx
)
if (mic_ctx
->bi_vcons
.dc_hdr_virt
) {
mic_ctx_unmap_single(mic_ctx
, mic_ctx
->bi_vcons
.dc_hdr_dma_addr
,
sizeof(struct vcons_buf
));
kfree(mic_ctx
->bi_vcons
.dc_hdr_virt
);
mic_ctx
->bi_vcons
.dc_hdr_virt
= NULL
;
if (mic_ctx
->bi_vcons
.dc_buf_virt
) {
mic_ctx_unmap_single(mic_ctx
, mic_ctx
->bi_vcons
.dc_dma_addr
,
free_pages((uint64_t)mic_ctx
->bi_vcons
.dc_buf_virt
, 0);
mic_ctx
->bi_vcons
.dc_buf_virt
= NULL
;
mic_psmi_uninit(mic_ctx
);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) || defined(RHEL_RELEASE_CODE)
mic_vhost_blk_remove(mic_ctx
->bd_info
);
mic_unreg_irqhandler(mic_ctx
, 0x1, "MIC SHUTDOWN DoorBell 1");
mic_smpt_uninit(mic_ctx
);
/* Make sure that no reset timer is running after the workqueue is destroyed */
destroy_reset_workqueue(mic_ctx
);
iounmap((void *)mic_ctx
->mmio
.va
);
iounmap((void *)mic_ctx
->aper
.va
);
#define MIC_MAX_BOOT_TIME 180 // Maximum number of seconds to wait for boot to complete
online_timer(struct timer_list
*arg
)
mic_ctx_t
*mic_ctx
= from_timer(mic_ctx
, arg
, boot_timer
);
uint64_t delay
= (jiffies
- mic_ctx
->boot_start
) / HZ
;
if (mic_ctx
->state
== MIC_ONLINE
)
if (delay
> MIC_MAX_BOOT_TIME
) {
printk("Fail booting MIC %d. Wait time execeed %d seconds\n", mic_ctx
->bi_id
, MIC_MAX_BOOT_TIME
);
mic_ctx
->state
= MIC_BOOTFAIL
;
mic_ctx
->boot_timer
.expires
= jiffies
+ HZ
;
timer_setup(&mic_ctx
->boot_timer
, online_timer
, 0);
printk("Waiting for MIC %d boot %lld\n", mic_ctx
->bi_id
, delay
);
boot_timer(struct timer_list
*arg
)
mic_ctx_t
*mic_ctx
= from_timer(mic_ctx
, arg
, boot_timer
);
struct micvnet_info
*vnet_info
= (struct micvnet_info
*) mic_ctx
->bi_vethinfo
;
uint64_t delay
= (jiffies
- mic_ctx
->boot_start
) / HZ
;
bool timer_restart
= false;
if ((mic_ctx
->state
!= MIC_BOOT
) && (mic_ctx
->state
!= MIC_ONLINE
)) {
if (delay
> MIC_MAX_BOOT_TIME
) {
printk("Fail booting MIC %d. Wait time execeed %d seconds\n", mic_ctx
->bi_id
, MIC_MAX_BOOT_TIME
);
mic_ctx
->state
= MIC_BOOTFAIL
;
printk("Waiting for MIC %d boot %lld\n", mic_ctx
->bi_id
, delay
);
if (mic_vnet_mode
!= VNET_MODE_DMA
)
timer_restart
= (SBOX_READ(mic_ctx
->mmio
.va
, SBOX_SCRATCH14
) == 0)?
else if (atomic_read(&vnet_info
->vi_state
) != MICVNET_STATE_LINKUP
)
timer_restart
= (mic_ctx
->state
!= MIC_ONLINE
)? true: false;
mic_ctx
->boot_timer
.expires
= jiffies
+ HZ
;
timer_setup(&mic_ctx
->boot_timer
, boot_timer
, 0);
mic_ctx
->boot_timer
.expires
= jiffies
+ HZ
;
timer_setup(&mic_ctx
->boot_timer
, online_timer
, 0);
printk("MIC %d Network link is up\n", mic_ctx
->bi_id
);
schedule_work(&mic_ctx
->boot_ws
);
post_boot_startup(struct work_struct
*work
)
= container_of(work
, mic_ctx_t
, boot_ws
);
if (micpm_get_reference(mic_ctx
, true) != 0)
// We should only enable DMA after uos is booted
BUG_ON(open_dma_device(mic_ctx
->bi_id
+1,
mic_ctx
->mmio
.va
+ HOST_SBOX_BASE_ADDRESS
,
if (micveth_start(mic_ctx
))
printk(KERN_ERR
"%s: micveth_start failed\n", __FUNCTION__
);
micpm_put_reference(mic_ctx
);
attempt_reset(struct work_struct
*work
)
= container_of(work
, mic_ctx_t
, resetwork
);
printk("Reattempting reset after F2/F4 failure\n");
adapter_reset(mic_ctx
, RESET_WAIT
, RESET_REATTEMPT
);
ioremap_work(struct work_struct
*work
)
= container_of(work
, mic_ctx_t
, ioremapwork
);
mic_ctx
->aper
.va
= ioremap_wc(mic_ctx
->aper
.pa
, mic_ctx
->aper
.len
);
if (mic_ctx
->aper
.va
== NULL
) {
printk(KERN_ERR
"mic %d: failed to map aperture space\n", mic_ctx
->bi_id
);
mutex_lock(&mic_ctx
->state_lock
);
mic_setstate(mic_ctx
, MIC_RESETFAIL
);
mutex_unlock(&mic_ctx
->state_lock
);
wake_up(&mic_ctx
->ioremapwq
);
adapter_post_boot_device(mic_ctx_t
*mic_ctx
)
mic_ctx
->boot_timer
.expires
= jiffies
+ HZ
;
mic_ctx
->boot_start
= jiffies
;
timer_setup(&mic_ctx
->boot_timer
, boot_timer
, 0);
mic_shutdown_host_doorbell_intr_handler(mic_ctx_t
*mic_ctx
, int doorbell
)
struct micscif_dev
*dev
= &scif_dev
[mic_get_scifnode_id(mic_ctx
)];
mic_ctx
->sdbic1
= SBOX_READ(mic_ctx
->mmio
.va
, SBOX_SDBIC1
);
SBOX_WRITE(0x0, mic_ctx
->mmio
.va
, SBOX_SDBIC1
);
queue_delayed_work(dev
->sd_ln_wq
,
&dev
->sd_watchdog_work
, 0);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0))
ramoops_proc_show(struct seq_file
*m
, void *data
)
uint64_t id
= ((uint64_t)data
) & 0xffffffff;
uint64_t entry
= ((uint64_t)data
) >> 32;
struct list_head
*pos
, *tmpq
;
mic_ctx_t
*mic_ctx
= NULL
;
list_for_each_safe(pos
, tmpq
, &mic_data
.dd_bdlist
) {
bd
= list_entry(pos
, bd_info_t
, bi_list
);
if (mic_ctx
->bi_id
== id
)
spin_lock_irqsave(&mic_ctx
->ramoops_lock
, flags
);
record
= mic_ctx
->ramoops_va
[entry
];
spin_unlock_irqrestore(&mic_ctx
->ramoops_lock
, flags
);
size
= mic_ctx
->ramoops_size
;
if ((output
= kzalloc(size
, GFP_ATOMIC
)) == NULL
) {
spin_unlock_irqrestore(&mic_ctx
->ramoops_lock
, flags
);
l
+= scnprintf(output
, size
, "%s", record
);
spin_unlock_irqrestore(&mic_ctx
->ramoops_lock
, flags
);
seq_printf(m
, "%s", output
);
ramoops_proc_open(struct inode
*inode
, struct file
*file
)
return single_open(file
, ramoops_proc_show
, NULL
);
struct file_operations ramoops_proc_fops
= {
.open
= ramoops_proc_open
,
.release
= single_release
,
ramoops_read(char *buf
, char **start
, off_t offset
, int len
, int *eof
, void *data
)
uint64_t id
= ((uint64_t)data
) & 0xffffffff;
uint64_t entry
= ((uint64_t)data
) >> 32;
struct list_head
*pos
, *tmpq
;
mic_ctx_t
*mic_ctx
= NULL
;
list_for_each_safe(pos
, tmpq
, &mic_data
.dd_bdlist
) {
bd
= list_entry(pos
, bd_info_t
, bi_list
);
if (mic_ctx
->bi_id
== id
)
spin_lock_irqsave(&mic_ctx
->ramoops_lock
, flags
);
record
= mic_ctx
->ramoops_va
[entry
];
spin_unlock_irqrestore(&mic_ctx
->ramoops_lock
, flags
);
size
= mic_ctx
->ramoops_size
;
if ((output
= kzalloc(size
, GFP_ATOMIC
)) == NULL
) {
spin_unlock_irqrestore(&mic_ctx
->ramoops_lock
, flags
);
l
+= scnprintf(output
, size
, "%s", record
);
spin_unlock_irqrestore(&mic_ctx
->ramoops_lock
, flags
);
left_to_read
= l
- offset
;
left_to_read
= min(len
, left_to_read
);
memcpy(buf
, output
+ offset
, left_to_read
);
set_ramoops_pa(mic_ctx_t
*mic_ctx
)
if (mic_ctx
->ramoops_pa
[0] == 0L) {
kfree(mic_ctx
->ramoops_va
[0]);
mic_ctx
->ramoops_size
= 0;
mic_ctx
->ramoops_va
[0] = NULL
;
ramoops_probe(mic_ctx_t
*mic_ctx
)
mic_ctx
->ramoops_size
= ramoops_count
* PAGE_SIZE
;
if ((mic_ctx
->ramoops_va
[0] = kzalloc(mic_ctx
->ramoops_size
, GFP_KERNEL
)) != NULL
) {
spin_lock_init(&mic_ctx
->ramoops_lock
);
mic_ctx
->ramoops_va
[1] = NULL
;
mic_ctx
->ramoops_pa
[0] = mic_ctx_map_single(mic_ctx
, mic_ctx
->ramoops_va
[0],
if (set_ramoops_pa(mic_ctx
))
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0))
snprintf(name
, 64, "mic%d", mic_ctx
->bi_id
);
proc_create_data(name
, 0444, ramoops_dir
, &ramoops_proc_fops
,
(void *)(long)mic_ctx
->bi_id
);
snprintf(name
, 64, "mic%d_prev", mic_ctx
->bi_id
);
proc_create_data(name
, 0444, ramoops_dir
, &ramoops_proc_fops
,
(void *)((long)mic_ctx
->bi_id
| (1L << 32)));
snprintf(name
, 64, "mic%d", mic_ctx
->bi_id
);
if (create_proc_read_entry(name
, 0444, ramoops_dir
, ramoops_read
,
(void *)(long)mic_ctx
->bi_id
) == NULL
)
printk("Failed to intialize /proc/mic_ramoops/%s\n", name
);
snprintf(name
, 64, "mic%d_prev", mic_ctx
->bi_id
);
if (create_proc_read_entry(name
, 0444, ramoops_dir
, ramoops_read
,
(void *)((long)mic_ctx
->bi_id
| (1L << 32))) == NULL
)
printk("Failed to intialize /proc/mic_ramoops/%s\n", name
);
mic_ctx
->ramoops_size
= 0;
ramoops_flip(mic_ctx_t
*mic_ctx
)
if (mic_ctx
->ramoops_size
== 0)
spin_lock_irqsave(&mic_ctx
->ramoops_lock
, flags
);
if (mic_ctx
->ramoops_va
[1] != NULL
) {
mic_ctx_unmap_single(mic_ctx
, mic_ctx
->ramoops_pa
[1], mic_ctx
->ramoops_size
);
kfree(mic_ctx
->ramoops_va
[1]);
mic_ctx
->ramoops_pa
[1] = mic_ctx
->ramoops_pa
[0];
mic_ctx
->ramoops_va
[1] = mic_ctx
->ramoops_va
[0];
if ((mic_ctx
->ramoops_va
[0] = kzalloc(mic_ctx
->ramoops_size
, GFP_ATOMIC
)) != NULL
) {
mic_ctx
->ramoops_pa
[0] = mic_ctx_map_single(mic_ctx
, mic_ctx
->ramoops_va
[0],
spin_unlock_irqrestore(&mic_ctx
->ramoops_lock
, flags
);
adapter_probe(mic_ctx_t
*mic_ctx
)
// Init the irq information
atomic_set(&mic_ctx
->bi_irq
.mi_received
, 0);
spin_lock_init(&mic_ctx
->bi_irq
.mi_lock
);
tasklet_init(&mic_ctx
->bi_dpc
, adapter_dpc
, (unsigned long)&mic_ctx
->bi_dpc
);
for (db
= 0; db
< MIC_NUM_DB
; db
++) {
INIT_LIST_HEAD(&mic_ctx
->bi_irq
.mi_dblist
[db
]);
mic_enable_msi_interrupts(mic_ctx
);
scratch13
= SBOX_READ(mic_ctx
->mmio
.va
, SBOX_SCRATCH13
);
mic_ctx
->bi_stepping
= SCRATCH13_STEP_ID(scratch13
);
mic_ctx
->bi_substepping
= SCRATCH13_SUB_STEP(scratch13
);
mic_ctx
->bi_platform
= PLATFORM_EMULATOR
;
mic_ctx
->bi_platform
= SCRATCH13_PLATFORM_ID(scratch13
);
mic_enable_interrupts(mic_ctx
);
if (micveth_probe(mic_ctx
))
printk(KERN_ERR
"%s: micveth_probe failed\n", __FUNCTION__
);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) || defined(RHEL_RELEASE_CODE)
if (mic_vhost_blk_probe(mic_ctx
->bd_info
))
printk(KERN_ERR
"%s: mic_vhost_blk_probe failed\n", __FUNCTION__
);
printk(KERN_ERR
"%s: micpm_probe failed\n", __FUNCTION__
);
mic_reg_irqhandler(mic_ctx
, 1, "MIC SHUTDOWN DoorBell 1",
mic_shutdown_host_doorbell_intr_handler
);
printk("boot_linux_uos failed \n");
// We should only enable DMA after uos is booted
//mic_dma_lib_init(mic_ctx->mmio.va+HOST_SBOX_BASE_ADDRESS);
adapter_start_device(mic_ctx_t
*mic_ctx
)
mutex_lock(&mic_ctx
->state_lock
);
if (mic_ctx
->state
== MIC_READY
) {
mic_setstate(mic_ctx
, MIC_BOOT
);
mutex_unlock(&mic_ctx
->state_lock
);
/* TODO: Unknown state handling? */
printk(KERN_ERR
"%s %d state %d??\n",
__func__
, __LINE__
, mic_ctx
->state
);
mutex_unlock(&mic_ctx
->state_lock
);
mic_ctx
->mode
= MODE_LINUX
;
ret
= boot_linux_uos(mic_ctx
, mic_ctx
->image
, mic_ctx
->initramfs
);
printk(KERN_ERR
"boot_linux_uos failed %d\n", ret
);
ret
= adapter_post_boot_device(mic_ctx
);
printk(KERN_ERR
"adapter post boot failed %d\n", ret
);
pr_debug("adapter started successfully\n");
adapter_init_device(mic_ctx_t
*mic_ctx
)
struct vcons_buf
*vcons_buf
;
uint32_t mmio_data_cc
; /* mmio data from class code register */
uint32_t mmio_data_bar
; /* mmio data from bar enable register */
spin_lock_init(&mic_ctx
->sysfs_lock
);
mic_setstate(mic_ctx
, MIC_RESET
);
mic_ctx
->mode
= MODE_NONE
;
mic_ctx
->reset_count
= 0;
mutex_init (&mic_ctx
->state_lock
);
init_waitqueue_head(&mic_ctx
->resetwq
);
init_waitqueue_head(&mic_ctx
->ioremapwq
);
if (!(mic_ctx
->resetworkq
= __mic_create_singlethread_workqueue("RESET WORK")))
if (!(mic_ctx
->ioremapworkq
= __mic_create_singlethread_workqueue("IOREMAP_WORK"))) {
INIT_WORK(&mic_ctx
->ioremapwork
, ioremap_work
);
INIT_WORK(&mic_ctx
->boot_ws
, post_boot_startup
);
INIT_WORK(&mic_ctx
->resetwork
, attempt_reset
);
atomic_set(&mic_ctx
->gate_interrupt
, 0);
device_id
= mic_ctx
->bi_pdev
->device
;
mic_ctx
->bi_family
= get_product_family(device_id
);
if ((mic_ctx
->mmio
.va
= ioremap_nocache(mic_ctx
->mmio
.pa
,
mic_ctx
->mmio
.len
)) == NULL
) {
printk("mic %d: failed to map mmio space\n", mic_ctx
->bi_id
);
if (mic_ctx
->aper
.pa
== 0) {
* Read class code from SBOX_PCIE_PCI_REVISION_ID_AND_C_0X8 register
* If the mode is zombie, then
* 1> Aperture is not available
* 2> Register 0x5CD4 is written to 0x00000002 to disable all BARs except MMIO
* 3> Register 0x5808 is written to 0xFF0000XX to set the class ID to a generic PCI device.
mmio_data_cc
= SBOX_READ(mic_ctx
->mmio
.va
, SBOX_PCIE_PCI_REVISION_ID_AND_C_0X8
);
mmio_data_cc
= PCIE_CLASS_CODE(mmio_data_cc
);
mmio_data_bar
= SBOX_READ(mic_ctx
->mmio
.va
, SBOX_PCIE_BAR_ENABLE
);
if((mmio_data_cc
== ZOMBIE_CLASS_CODE
) && (mmio_data_bar
== DISABLE_BAR
)) {
mic_ctx
->card_usage_mode
= USAGE_MODE_ZOMBIE
;
usagemode_param
= USAGE_MODE_ZOMBIE
;
printk("Error: Not in zombie mode and aperture is 0\n");
goto adap_init_unmapmmio
;
if (mic_ctx
->ioremapworkq
) {
queue_work(mic_ctx
->ioremapworkq
, &mic_ctx
->ioremapwork
);
if ((mic_ctx
->aper
.va
= ioremap_wc(mic_ctx
->aper
.pa
, mic_ctx
->aper
.len
)) == NULL
) {
printk("mic %d: failed to map aperture space\n", mic_ctx
->bi_id
);
goto adap_init_unmapmmio
;
// Allocate memory for PCI serial console
mic_ctx
->bi_vcons
.dc_buf_virt
= (void *)get_zeroed_page(GFP_KERNEL
);
mic_ctx
->bi_vcons
.dc_hdr_virt
= kzalloc(sizeof(struct vcons_buf
), GFP_KERNEL
);
if ((!mic_ctx
->bi_vcons
.dc_buf_virt
) || (!mic_ctx
->bi_vcons
.dc_hdr_virt
)) {
printk(KERN_ERR
"mic %d: failed to allocate memory for vcons buffer\n",
mic_ctx
->bi_vcons
.dc_enabled
= 0;
if (mic_ctx
->bi_vcons
.dc_buf_virt
)
free_pages((uint64_t)mic_ctx
->bi_vcons
.dc_buf_virt
, 0);
if (mic_ctx
->bi_vcons
.dc_hdr_virt
)
kfree(mic_ctx
->bi_vcons
.dc_hdr_virt
);
mic_ctx
->bi_vcons
.dc_hdr_dma_addr
= mic_ctx_map_single(mic_ctx
,
mic_ctx
->bi_vcons
.dc_hdr_virt
,
sizeof(struct vcons_buf
));
mic_ctx
->bi_vcons
.dc_dma_addr
= mic_ctx_map_single(mic_ctx
,
mic_ctx
->bi_vcons
.dc_buf_virt
,
if ((!mic_ctx
->bi_vcons
.dc_dma_addr
) ||
(!mic_ctx
->bi_vcons
.dc_hdr_dma_addr
))
mic_ctx
->bi_vcons
.dc_enabled
= 0;
mic_ctx
->bi_vcons
.dc_enabled
= 1;
mic_ctx
->bi_vcons
.dc_size
= MICVCONS_BUF_SIZE
;
vcons_buf
= (struct vcons_buf
*)(mic_ctx
->bi_vcons
.dc_hdr_virt
);
vcons_buf
->o_buf_dma_addr
= mic_ctx
->bi_vcons
.dc_dma_addr
;
vcons_buf
->o_size
= MICVCONS_BUF_SIZE
;
vcons_buf
->host_magic
= MIC_HOST_VCONS_READY
;
vcons_buf
->host_rb_ver
= micscif_rb_get_version();
mic_ctx
->dma_handle
= NULL
;
// To avoid hazard on Windows, sku_build_table is done on DriverEntry
device_id
= mic_ctx
->bi_pdev
->device
;
sku_find(mic_ctx
, device_id
);
// To avoid hazard on Windows, sku_destroy_table is done on MicUnload
/* Determine the amount of compensation that needs to be applied to MIC's ETC timer */
calculate_etc_compensation(mic_ctx
);
iounmap(mic_ctx
->mmio
.va
);
destroy_workqueue(mic_ctx
->ioremapworkq
);
destroy_workqueue(mic_ctx
->resetworkq
);
mic_enable_interrupts(mic_ctx_t
*mic_ctx
)
ENABLE_MIC_INTERRUPTS(mic_ctx
->mmio
.va
);
mic_disable_interrupts(mic_ctx_t
*mic_ctx
)
sboxSice0reg
= SBOX_READ(mic_ctx
->mmio
.va
, SBOX_SICE0
);
SBOX_WRITE(sboxSice0reg
, mic_ctx
->mmio
.va
, SBOX_SICC0
);
mic_enable_msi_interrupts(mic_ctx_t
*mic_ctx
)
// Only support single MSI interrupt for now
sboxMXARreg
= SBOX_SICE0_DBR_BITS(0xf) | SBOX_SICE0_DMA_BITS(0xff);
if (mic_ctx
->bi_family
== FAMILY_KNC
)
SBOX_WRITE(sboxMXARreg
, mic_ctx
->mmio
.va
, SBOX_MXAR0_K1OM
);
SBOX_WRITE(sboxMXARreg
, mic_ctx
->mmio
.va
, SBOX_MXAR0
);
mic_reg_irqhandler(mic_ctx_t
*mic_ctx
, int doorbell
, char *idstring
,
int (*irqfunc
)(mic_ctx_t
*mic_ctx
, int doorbell
))
mic_irqhandler_t
*irqhandle
;
if (doorbell
> MIC_IRQ_MAX
) {
if (!(irqhandle
= kmalloc(sizeof(mic_irqhandler_t
), GFP_ATOMIC
)))
if (!(irqhandle
->ih_idstring
= kmalloc(strlen(idstring
) + 1, GFP_ATOMIC
)))
irqhandle
->ih_func
= irqfunc
;
strcpy(irqhandle
->ih_idstring
, idstring
);
spin_lock_irqsave(&mic_ctx
->bi_irq
.mi_lock
, flags
);
list_add_tail(&irqhandle
->ih_list
, &mic_ctx
->bi_irq
.mi_dblist
[doorbell
]);
spin_unlock_irqrestore(&mic_ctx
->bi_irq
.mi_lock
, flags
);
mic_unreg_irqhandler(mic_ctx_t
*mic_ctx
, int doorbell
, char *idstring
)
mic_irqhandler_t
*irqhandle
;
struct list_head
*pos
, *tmpq
;
spin_lock_irqsave(&mic_ctx
->bi_irq
.mi_lock
, flags
);
list_for_each_safe(pos
, tmpq
, &mic_ctx
->bi_irq
.mi_dblist
[doorbell
]) {
irqhandle
= list_entry(pos
, mic_irqhandler_t
, ih_list
);
if (strcmp(idstring
, irqhandle
->ih_idstring
) == 0) {
kfree(irqhandle
->ih_idstring
);
spin_unlock_irqrestore(&mic_ctx
->bi_irq
.mi_lock
, flags
);
void adapter_process_one_interrupt(mic_ctx_t
*mic_ctx
, uint32_t events
)
mic_irqhandler_t
*irqhandle
;
atomic_inc(&mic_ctx
->bi_irq
.mi_received
);
if (SBOX_SICR0_DBR(events
)) {
for (doorbell
= 0; doorbell
< 4; doorbell
++) {
if (SBOX_SICR0_DBR(events
) & (0x1 << doorbell
)) {
spin_lock(&mic_ctx
->bi_irq
.mi_lock
);
list_for_each(pos
, &mic_ctx
->bi_irq
.mi_dblist
[doorbell
]) {
irqhandle
= list_entry(pos
, mic_irqhandler_t
, ih_list
);
irqhandle
->ih_func(mic_ctx
, doorbell
);
spin_unlock(&mic_ctx
->bi_irq
.mi_lock
);
if (SBOX_SICR0_DMA(events
))
host_dma_interrupt_handler(mic_ctx
->dma_handle
, events
);
adapter_isr(mic_ctx_t
*mic_ctx
)
volatile uint32_t sboxSicr0reg
;
if (atomic_cmpxchg(&mic_ctx
->gate_interrupt
, 0, 1) == 1)
sboxSicr0reg
= SBOX_READ(mic_ctx
->mmio
.va
, SBOX_SICR0
);
if (unlikely(!sboxSicr0reg
)) {
atomic_set(&mic_ctx
->gate_interrupt
, 0);
// tell mic that we recived interrupt otherwise it will keep sending them
SBOX_WRITE(sboxSicr0reg
, mic_ctx
->mmio
.va
, SBOX_SICR0
);
// This only applies to KNC B0
if (FAMILY_KNC
== mic_ctx
->bi_family
&&
mic_ctx
->bi_stepping
>= KNC_B0_STEP
)
mic_enable_interrupts(mic_ctx
);
atomic_set(&mic_ctx
->gate_interrupt
, 0);
adapter_process_one_interrupt(mic_ctx
, sboxSicr0reg
);
adapter_imsr(mic_ctx_t
*mic_ctx
)
#if 0 /* TODO: disable interrupt when KNC auto-enable isn't used */
mic_disable_interrupts(mic_ctx
);
tasklet_schedule(&mic_ctx
->bi_dpc
);
static void adapter_dpc(unsigned long dpc
)
container_of((struct tasklet_struct
*)dpc
, mic_ctx_t
, bi_dpc
);
volatile uint32_t sboxSicr0reg
;
if (atomic_cmpxchg(&mic_ctx
->gate_interrupt
, 0, 1) == 1)
/* Clear pending bit array */
if (FAMILY_KNC
== mic_ctx
->bi_family
) {
if (KNC_A_STEP
== mic_ctx
->bi_stepping
)
SBOX_WRITE(1, mic_ctx
->mmio
.va
, SBOX_MSIXPBACR_K1OM
);
SBOX_WRITE(1, mic_ctx
->mmio
.va
, SBOX_MSIXPBACR
);
sboxSicr0reg
= SBOX_READ(mic_ctx
->mmio
.va
, SBOX_SICR0
);
if (unlikely(!sboxSicr0reg
)) {
atomic_set(&mic_ctx
->gate_interrupt
, 0);
SBOX_WRITE(sboxSicr0reg
, mic_ctx
->mmio
.va
, SBOX_SICR0
);
// This only applies to KNC B0
if (FAMILY_KNC
== mic_ctx
->bi_family
&&
mic_ctx
->bi_stepping
>= KNC_B0_STEP
)
mic_enable_interrupts(mic_ctx
);
atomic_set(&mic_ctx
->gate_interrupt
, 0);
adapter_process_one_interrupt(mic_ctx
, sboxSicr0reg
);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0))
ramoops_dir
= proc_mkdir("mic_ramoops", NULL
);
ramoops_dir
= create_proc_entry("mic_ramoops", S_IFDIR
| S_IRUGO
, NULL
);
remove_proc_entry("mic_ramoops", NULL
);
void ramoops_remove(mic_ctx_t
*mic_ctx
)
snprintf(name
, 64, "mic%d", mic_ctx
->bi_id
);
remove_proc_entry(name
, ramoops_dir
);
snprintf(name
, 64, "mic%d_prev", mic_ctx
->bi_id
);
remove_proc_entry(name
, ramoops_dir
);
if (mic_ctx
->ramoops_size
== 0)
for (i
= 0; i
< 2; i
++) {
if (mic_ctx
->ramoops_va
[i
] != NULL
) {
mic_ctx_unmap_single(mic_ctx
, mic_ctx
->ramoops_pa
[i
],
kfree(mic_ctx
->ramoops_va
[i
]);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0))
vmcore_dir
= proc_mkdir("mic_vmcore", NULL
);
vmcore_dir
= create_proc_entry("mic_vmcore", S_IFDIR
| S_IRUGO
, NULL
);
remove_proc_entry("mic_vmcore", NULL
);
void vmcore_remove(mic_ctx_t
*mic_ctx
)
snprintf(name
, 64, "mic%d", mic_ctx
->bi_id
);
if (mic_ctx
->vmcore_dir
) {
remove_proc_entry(name
, vmcore_dir
);
mic_ctx
->vmcore_dir
= NULL
;
if (mic_ctx
->elfcorebuf
) {
kfree(mic_ctx
->elfcorebuf
);
mic_ctx
->elfcorebuf
= NULL
;
mic_ctx
->elfcorebuf_sz
= 0;
mic_ctx
->vmcore_size
= 0;
INIT_LIST_HEAD(&mic_data
.dd_bdlist
);
void show_stepping_comm(mic_ctx_t
*mic_ctx
,char *buf
)
char string
[STEPINGSTRSIZE
];
switch (mic_ctx
->bi_family
) {
switch (mic_ctx
->bi_stepping
) {
string
[1] = mic_ctx
->bi_substepping
+ '0';
switch (mic_ctx
->bi_stepping
) {
strncpy(buf
,string
,STEPINGSTRSIZE
);