162306a36Sopenharmony_ci/********************************************************************** 262306a36Sopenharmony_ci * Author: Cavium, Inc. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Contact: support@cavium.com 562306a36Sopenharmony_ci * Please include "LiquidIO" in the subject. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (c) 2003-2016 Cavium, Inc. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * This file is free software; you can redistribute it and/or modify 1062306a36Sopenharmony_ci * it under the terms of the GNU General Public License, Version 2, as 1162306a36Sopenharmony_ci * published by the Free Software Foundation. 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * This file is distributed in the hope that it will be useful, but 1462306a36Sopenharmony_ci * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty 1562306a36Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or 1662306a36Sopenharmony_ci * NONINFRINGEMENT. See the GNU General Public License for more details. 1762306a36Sopenharmony_ci ***********************************************************************/ 1862306a36Sopenharmony_ci/* 1962306a36Sopenharmony_ci * @file octeon_console.c 2062306a36Sopenharmony_ci */ 2162306a36Sopenharmony_ci#include <linux/moduleparam.h> 2262306a36Sopenharmony_ci#include <linux/pci.h> 2362306a36Sopenharmony_ci#include <linux/netdevice.h> 2462306a36Sopenharmony_ci#include <linux/crc32.h> 2562306a36Sopenharmony_ci#include "liquidio_common.h" 2662306a36Sopenharmony_ci#include "octeon_droq.h" 2762306a36Sopenharmony_ci#include "octeon_iq.h" 2862306a36Sopenharmony_ci#include "response_manager.h" 2962306a36Sopenharmony_ci#include "octeon_device.h" 3062306a36Sopenharmony_ci#include "liquidio_image.h" 3162306a36Sopenharmony_ci#include "octeon_mem_ops.h" 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic void octeon_remote_lock(void); 3462306a36Sopenharmony_cistatic void octeon_remote_unlock(void); 3562306a36Sopenharmony_cistatic u64 cvmx_bootmem_phy_named_block_find(struct octeon_device *oct, 3662306a36Sopenharmony_ci const char *name, 3762306a36Sopenharmony_ci u32 flags); 3862306a36Sopenharmony_cistatic int octeon_console_read(struct octeon_device *oct, u32 console_num, 3962306a36Sopenharmony_ci char *buffer, u32 buf_size); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define BOOTLOADER_PCI_READ_BUFFER_DATA_ADDR 0x0006c008 4262306a36Sopenharmony_ci#define BOOTLOADER_PCI_READ_BUFFER_LEN_ADDR 0x0006c004 4362306a36Sopenharmony_ci#define BOOTLOADER_PCI_READ_BUFFER_OWNER_ADDR 0x0006c000 4462306a36Sopenharmony_ci#define BOOTLOADER_PCI_READ_DESC_ADDR 0x0006c100 4562306a36Sopenharmony_ci#define BOOTLOADER_PCI_WRITE_BUFFER_STR_LEN 248 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define OCTEON_PCI_IO_BUF_OWNER_OCTEON 0x00000001 4862306a36Sopenharmony_ci#define OCTEON_PCI_IO_BUF_OWNER_HOST 0x00000002 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/** Can change without breaking ABI */ 5162306a36Sopenharmony_ci#define CVMX_BOOTMEM_NUM_NAMED_BLOCKS 64 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/** minimum alignment of bootmem alloced blocks */ 5462306a36Sopenharmony_ci#define CVMX_BOOTMEM_ALIGNMENT_SIZE (16ull) 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/** CVMX bootmem descriptor major version */ 5762306a36Sopenharmony_ci#define CVMX_BOOTMEM_DESC_MAJ_VER 3 5862306a36Sopenharmony_ci/* CVMX bootmem descriptor minor version */ 5962306a36Sopenharmony_ci#define CVMX_BOOTMEM_DESC_MIN_VER 0 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* Current versions */ 6262306a36Sopenharmony_ci#define OCTEON_PCI_CONSOLE_MAJOR_VERSION 1 6362306a36Sopenharmony_ci#define OCTEON_PCI_CONSOLE_MINOR_VERSION 0 6462306a36Sopenharmony_ci#define OCTEON_PCI_CONSOLE_BLOCK_NAME "__pci_console" 6562306a36Sopenharmony_ci#define OCTEON_CONSOLE_POLL_INTERVAL_MS 100 /* 10 times per second */ 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/* First three members of cvmx_bootmem_desc are left in original 6862306a36Sopenharmony_ci * positions for backwards compatibility. 6962306a36Sopenharmony_ci * Assumes big endian target 7062306a36Sopenharmony_ci */ 7162306a36Sopenharmony_cistruct cvmx_bootmem_desc { 7262306a36Sopenharmony_ci /** spinlock to control access to list */ 7362306a36Sopenharmony_ci u32 lock; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci /** flags for indicating various conditions */ 7662306a36Sopenharmony_ci u32 flags; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci u64 head_addr; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci /** incremented changed when incompatible changes made */ 8162306a36Sopenharmony_ci u32 major_version; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /** incremented changed when compatible changes made, 8462306a36Sopenharmony_ci * reset to zero when major incremented 8562306a36Sopenharmony_ci */ 8662306a36Sopenharmony_ci u32 minor_version; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci u64 app_data_addr; 8962306a36Sopenharmony_ci u64 app_data_size; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /** number of elements in named blocks array */ 9262306a36Sopenharmony_ci u32 nb_num_blocks; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci /** length of name array in bootmem blocks */ 9562306a36Sopenharmony_ci u32 named_block_name_len; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci /** address of named memory block descriptors */ 9862306a36Sopenharmony_ci u64 named_block_array_addr; 9962306a36Sopenharmony_ci}; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci/* Structure that defines a single console. 10262306a36Sopenharmony_ci * 10362306a36Sopenharmony_ci * Note: when read_index == write_index, the buffer is empty. 10462306a36Sopenharmony_ci * The actual usable size of each console is console_buf_size -1; 10562306a36Sopenharmony_ci */ 10662306a36Sopenharmony_cistruct octeon_pci_console { 10762306a36Sopenharmony_ci u64 input_base_addr; 10862306a36Sopenharmony_ci u32 input_read_index; 10962306a36Sopenharmony_ci u32 input_write_index; 11062306a36Sopenharmony_ci u64 output_base_addr; 11162306a36Sopenharmony_ci u32 output_read_index; 11262306a36Sopenharmony_ci u32 output_write_index; 11362306a36Sopenharmony_ci u32 lock; 11462306a36Sopenharmony_ci u32 buf_size; 11562306a36Sopenharmony_ci}; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci/* This is the main container structure that contains all the information 11862306a36Sopenharmony_ci * about all PCI consoles. The address of this structure is passed to various 11962306a36Sopenharmony_ci * routines that operation on PCI consoles. 12062306a36Sopenharmony_ci */ 12162306a36Sopenharmony_cistruct octeon_pci_console_desc { 12262306a36Sopenharmony_ci u32 major_version; 12362306a36Sopenharmony_ci u32 minor_version; 12462306a36Sopenharmony_ci u32 lock; 12562306a36Sopenharmony_ci u32 flags; 12662306a36Sopenharmony_ci u32 num_consoles; 12762306a36Sopenharmony_ci u32 pad; 12862306a36Sopenharmony_ci /* must be 64 bit aligned here... */ 12962306a36Sopenharmony_ci /* Array of addresses of octeon_pci_console structures */ 13062306a36Sopenharmony_ci u64 console_addr_array[]; 13162306a36Sopenharmony_ci /* Implicit storage for console_addr_array */ 13262306a36Sopenharmony_ci}; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci/* 13562306a36Sopenharmony_ci * This function is the implementation of the get macros defined 13662306a36Sopenharmony_ci * for individual structure members. The argument are generated 13762306a36Sopenharmony_ci * by the macros inorder to read only the needed memory. 13862306a36Sopenharmony_ci * 13962306a36Sopenharmony_ci * @param oct Pointer to current octeon device 14062306a36Sopenharmony_ci * @param base 64bit physical address of the complete structure 14162306a36Sopenharmony_ci * @param offset Offset from the beginning of the structure to the member being 14262306a36Sopenharmony_ci * accessed. 14362306a36Sopenharmony_ci * @param size Size of the structure member. 14462306a36Sopenharmony_ci * 14562306a36Sopenharmony_ci * @return Value of the structure member promoted into a u64. 14662306a36Sopenharmony_ci */ 14762306a36Sopenharmony_cistatic inline u64 __cvmx_bootmem_desc_get(struct octeon_device *oct, 14862306a36Sopenharmony_ci u64 base, 14962306a36Sopenharmony_ci u32 offset, 15062306a36Sopenharmony_ci u32 size) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci base = (1ull << 63) | (base + offset); 15362306a36Sopenharmony_ci switch (size) { 15462306a36Sopenharmony_ci case 4: 15562306a36Sopenharmony_ci return octeon_read_device_mem32(oct, base); 15662306a36Sopenharmony_ci case 8: 15762306a36Sopenharmony_ci return octeon_read_device_mem64(oct, base); 15862306a36Sopenharmony_ci default: 15962306a36Sopenharmony_ci return 0; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci/* 16462306a36Sopenharmony_ci * This function retrieves the string name of a named block. It is 16562306a36Sopenharmony_ci * more complicated than a simple memcpy() since the named block 16662306a36Sopenharmony_ci * descriptor may not be directly accessible. 16762306a36Sopenharmony_ci * 16862306a36Sopenharmony_ci * @param addr Physical address of the named block descriptor 16962306a36Sopenharmony_ci * @param str String to receive the named block string name 17062306a36Sopenharmony_ci * @param len Length of the string buffer, which must match the length 17162306a36Sopenharmony_ci * stored in the bootmem descriptor. 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_cistatic void CVMX_BOOTMEM_NAMED_GET_NAME(struct octeon_device *oct, 17462306a36Sopenharmony_ci u64 addr, 17562306a36Sopenharmony_ci char *str, 17662306a36Sopenharmony_ci u32 len) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci addr += offsetof(struct cvmx_bootmem_named_block_desc, name); 17962306a36Sopenharmony_ci octeon_pci_read_core_mem(oct, addr, (u8 *)str, len); 18062306a36Sopenharmony_ci str[len] = 0; 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci/* See header file for descriptions of functions */ 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci/* 18662306a36Sopenharmony_ci * Check the version information on the bootmem descriptor 18762306a36Sopenharmony_ci * 18862306a36Sopenharmony_ci * @param exact_match 18962306a36Sopenharmony_ci * Exact major version to check against. A zero means 19062306a36Sopenharmony_ci * check that the version supports named blocks. 19162306a36Sopenharmony_ci * 19262306a36Sopenharmony_ci * @return Zero if the version is correct. Negative if the version is 19362306a36Sopenharmony_ci * incorrect. Failures also cause a message to be displayed. 19462306a36Sopenharmony_ci */ 19562306a36Sopenharmony_cistatic int __cvmx_bootmem_check_version(struct octeon_device *oct, 19662306a36Sopenharmony_ci u32 exact_match) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci u32 major_version; 19962306a36Sopenharmony_ci u32 minor_version; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (!oct->bootmem_desc_addr) 20262306a36Sopenharmony_ci oct->bootmem_desc_addr = 20362306a36Sopenharmony_ci octeon_read_device_mem64(oct, 20462306a36Sopenharmony_ci BOOTLOADER_PCI_READ_DESC_ADDR); 20562306a36Sopenharmony_ci major_version = (u32)__cvmx_bootmem_desc_get( 20662306a36Sopenharmony_ci oct, oct->bootmem_desc_addr, 20762306a36Sopenharmony_ci offsetof(struct cvmx_bootmem_desc, major_version), 20862306a36Sopenharmony_ci sizeof_field(struct cvmx_bootmem_desc, major_version)); 20962306a36Sopenharmony_ci minor_version = (u32)__cvmx_bootmem_desc_get( 21062306a36Sopenharmony_ci oct, oct->bootmem_desc_addr, 21162306a36Sopenharmony_ci offsetof(struct cvmx_bootmem_desc, minor_version), 21262306a36Sopenharmony_ci sizeof_field(struct cvmx_bootmem_desc, minor_version)); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci dev_dbg(&oct->pci_dev->dev, "%s: major_version=%d\n", __func__, 21562306a36Sopenharmony_ci major_version); 21662306a36Sopenharmony_ci if ((major_version > 3) || 21762306a36Sopenharmony_ci (exact_match && major_version != exact_match)) { 21862306a36Sopenharmony_ci dev_err(&oct->pci_dev->dev, "bootmem ver mismatch %d.%d addr:0x%llx\n", 21962306a36Sopenharmony_ci major_version, minor_version, 22062306a36Sopenharmony_ci (long long)oct->bootmem_desc_addr); 22162306a36Sopenharmony_ci return -1; 22262306a36Sopenharmony_ci } else { 22362306a36Sopenharmony_ci return 0; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic const struct cvmx_bootmem_named_block_desc 22862306a36Sopenharmony_ci*__cvmx_bootmem_find_named_block_flags(struct octeon_device *oct, 22962306a36Sopenharmony_ci const char *name, u32 flags) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci struct cvmx_bootmem_named_block_desc *desc = 23262306a36Sopenharmony_ci &oct->bootmem_named_block_desc; 23362306a36Sopenharmony_ci u64 named_addr = cvmx_bootmem_phy_named_block_find(oct, name, flags); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (named_addr) { 23662306a36Sopenharmony_ci desc->base_addr = __cvmx_bootmem_desc_get( 23762306a36Sopenharmony_ci oct, named_addr, 23862306a36Sopenharmony_ci offsetof(struct cvmx_bootmem_named_block_desc, 23962306a36Sopenharmony_ci base_addr), 24062306a36Sopenharmony_ci sizeof_field( 24162306a36Sopenharmony_ci struct cvmx_bootmem_named_block_desc, 24262306a36Sopenharmony_ci base_addr)); 24362306a36Sopenharmony_ci desc->size = __cvmx_bootmem_desc_get(oct, named_addr, 24462306a36Sopenharmony_ci offsetof(struct cvmx_bootmem_named_block_desc, 24562306a36Sopenharmony_ci size), 24662306a36Sopenharmony_ci sizeof_field( 24762306a36Sopenharmony_ci struct cvmx_bootmem_named_block_desc, 24862306a36Sopenharmony_ci size)); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci strscpy(desc->name, name, sizeof(desc->name)); 25162306a36Sopenharmony_ci return &oct->bootmem_named_block_desc; 25262306a36Sopenharmony_ci } else { 25362306a36Sopenharmony_ci return NULL; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic u64 cvmx_bootmem_phy_named_block_find(struct octeon_device *oct, 25862306a36Sopenharmony_ci const char *name, 25962306a36Sopenharmony_ci u32 flags) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci u64 result = 0; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (!__cvmx_bootmem_check_version(oct, 3)) { 26462306a36Sopenharmony_ci u32 i; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci u64 named_block_array_addr = __cvmx_bootmem_desc_get( 26762306a36Sopenharmony_ci oct, oct->bootmem_desc_addr, 26862306a36Sopenharmony_ci offsetof(struct cvmx_bootmem_desc, 26962306a36Sopenharmony_ci named_block_array_addr), 27062306a36Sopenharmony_ci sizeof_field(struct cvmx_bootmem_desc, 27162306a36Sopenharmony_ci named_block_array_addr)); 27262306a36Sopenharmony_ci u32 num_blocks = (u32)__cvmx_bootmem_desc_get( 27362306a36Sopenharmony_ci oct, oct->bootmem_desc_addr, 27462306a36Sopenharmony_ci offsetof(struct cvmx_bootmem_desc, 27562306a36Sopenharmony_ci nb_num_blocks), 27662306a36Sopenharmony_ci sizeof_field(struct cvmx_bootmem_desc, 27762306a36Sopenharmony_ci nb_num_blocks)); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci u32 name_length = (u32)__cvmx_bootmem_desc_get( 28062306a36Sopenharmony_ci oct, oct->bootmem_desc_addr, 28162306a36Sopenharmony_ci offsetof(struct cvmx_bootmem_desc, 28262306a36Sopenharmony_ci named_block_name_len), 28362306a36Sopenharmony_ci sizeof_field(struct cvmx_bootmem_desc, 28462306a36Sopenharmony_ci named_block_name_len)); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci u64 named_addr = named_block_array_addr; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci for (i = 0; i < num_blocks; i++) { 28962306a36Sopenharmony_ci u64 named_size = __cvmx_bootmem_desc_get( 29062306a36Sopenharmony_ci oct, named_addr, 29162306a36Sopenharmony_ci offsetof( 29262306a36Sopenharmony_ci struct cvmx_bootmem_named_block_desc, 29362306a36Sopenharmony_ci size), 29462306a36Sopenharmony_ci sizeof_field( 29562306a36Sopenharmony_ci struct cvmx_bootmem_named_block_desc, 29662306a36Sopenharmony_ci size)); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (name && named_size) { 29962306a36Sopenharmony_ci char *name_tmp = 30062306a36Sopenharmony_ci kmalloc(name_length + 1, GFP_KERNEL); 30162306a36Sopenharmony_ci if (!name_tmp) 30262306a36Sopenharmony_ci break; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci CVMX_BOOTMEM_NAMED_GET_NAME(oct, named_addr, 30562306a36Sopenharmony_ci name_tmp, 30662306a36Sopenharmony_ci name_length); 30762306a36Sopenharmony_ci if (!strncmp(name, name_tmp, name_length)) { 30862306a36Sopenharmony_ci result = named_addr; 30962306a36Sopenharmony_ci kfree(name_tmp); 31062306a36Sopenharmony_ci break; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci kfree(name_tmp); 31362306a36Sopenharmony_ci } else if (!name && !named_size) { 31462306a36Sopenharmony_ci result = named_addr; 31562306a36Sopenharmony_ci break; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci named_addr += 31962306a36Sopenharmony_ci sizeof(struct cvmx_bootmem_named_block_desc); 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci return result; 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci/* 32662306a36Sopenharmony_ci * Find a named block on the remote Octeon 32762306a36Sopenharmony_ci * 32862306a36Sopenharmony_ci * @param name Name of block to find 32962306a36Sopenharmony_ci * @param base_addr Address the block is at (OUTPUT) 33062306a36Sopenharmony_ci * @param size The size of the block (OUTPUT) 33162306a36Sopenharmony_ci * 33262306a36Sopenharmony_ci * @return Zero on success, One on failure. 33362306a36Sopenharmony_ci */ 33462306a36Sopenharmony_cistatic int octeon_named_block_find(struct octeon_device *oct, const char *name, 33562306a36Sopenharmony_ci u64 *base_addr, u64 *size) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci const struct cvmx_bootmem_named_block_desc *named_block; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci octeon_remote_lock(); 34062306a36Sopenharmony_ci named_block = __cvmx_bootmem_find_named_block_flags(oct, name, 0); 34162306a36Sopenharmony_ci octeon_remote_unlock(); 34262306a36Sopenharmony_ci if (named_block) { 34362306a36Sopenharmony_ci *base_addr = named_block->base_addr; 34462306a36Sopenharmony_ci *size = named_block->size; 34562306a36Sopenharmony_ci return 0; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci return 1; 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic void octeon_remote_lock(void) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci /* fill this in if any sharing is needed */ 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic void octeon_remote_unlock(void) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci /* fill this in if any sharing is needed */ 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ciint octeon_console_send_cmd(struct octeon_device *oct, char *cmd_str, 36162306a36Sopenharmony_ci u32 wait_hundredths) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci u32 len = (u32)strlen(cmd_str); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci dev_dbg(&oct->pci_dev->dev, "sending \"%s\" to bootloader\n", cmd_str); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci if (len > BOOTLOADER_PCI_WRITE_BUFFER_STR_LEN - 1) { 36862306a36Sopenharmony_ci dev_err(&oct->pci_dev->dev, "Command string too long, max length is: %d\n", 36962306a36Sopenharmony_ci BOOTLOADER_PCI_WRITE_BUFFER_STR_LEN - 1); 37062306a36Sopenharmony_ci return -1; 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci if (octeon_wait_for_bootloader(oct, wait_hundredths) != 0) { 37462306a36Sopenharmony_ci dev_err(&oct->pci_dev->dev, "Bootloader not ready for command.\n"); 37562306a36Sopenharmony_ci return -1; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci /* Write command to bootloader */ 37962306a36Sopenharmony_ci octeon_remote_lock(); 38062306a36Sopenharmony_ci octeon_pci_write_core_mem(oct, BOOTLOADER_PCI_READ_BUFFER_DATA_ADDR, 38162306a36Sopenharmony_ci (u8 *)cmd_str, len); 38262306a36Sopenharmony_ci octeon_write_device_mem32(oct, BOOTLOADER_PCI_READ_BUFFER_LEN_ADDR, 38362306a36Sopenharmony_ci len); 38462306a36Sopenharmony_ci octeon_write_device_mem32(oct, BOOTLOADER_PCI_READ_BUFFER_OWNER_ADDR, 38562306a36Sopenharmony_ci OCTEON_PCI_IO_BUF_OWNER_OCTEON); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci /* Bootloader should accept command very quickly 38862306a36Sopenharmony_ci * if it really was ready 38962306a36Sopenharmony_ci */ 39062306a36Sopenharmony_ci if (octeon_wait_for_bootloader(oct, 200) != 0) { 39162306a36Sopenharmony_ci octeon_remote_unlock(); 39262306a36Sopenharmony_ci dev_err(&oct->pci_dev->dev, "Bootloader did not accept command.\n"); 39362306a36Sopenharmony_ci return -1; 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci octeon_remote_unlock(); 39662306a36Sopenharmony_ci return 0; 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ciint octeon_wait_for_bootloader(struct octeon_device *oct, 40062306a36Sopenharmony_ci u32 wait_time_hundredths) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci dev_dbg(&oct->pci_dev->dev, "waiting %d0 ms for bootloader\n", 40362306a36Sopenharmony_ci wait_time_hundredths); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (octeon_mem_access_ok(oct)) 40662306a36Sopenharmony_ci return -1; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci while (wait_time_hundredths > 0 && 40962306a36Sopenharmony_ci octeon_read_device_mem32(oct, 41062306a36Sopenharmony_ci BOOTLOADER_PCI_READ_BUFFER_OWNER_ADDR) 41162306a36Sopenharmony_ci != OCTEON_PCI_IO_BUF_OWNER_HOST) { 41262306a36Sopenharmony_ci if (--wait_time_hundredths <= 0) 41362306a36Sopenharmony_ci return -1; 41462306a36Sopenharmony_ci schedule_timeout_uninterruptible(HZ / 100); 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci return 0; 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic void octeon_console_handle_result(struct octeon_device *oct, 42062306a36Sopenharmony_ci size_t console_num) 42162306a36Sopenharmony_ci{ 42262306a36Sopenharmony_ci struct octeon_console *console; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci console = &oct->console[console_num]; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci console->waiting = 0; 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cistatic char console_buffer[OCTEON_CONSOLE_MAX_READ_BYTES]; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cistatic void output_console_line(struct octeon_device *oct, 43262306a36Sopenharmony_ci struct octeon_console *console, 43362306a36Sopenharmony_ci size_t console_num, 43462306a36Sopenharmony_ci char *console_buffer, 43562306a36Sopenharmony_ci s32 bytes_read) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci char *line; 43862306a36Sopenharmony_ci s32 i; 43962306a36Sopenharmony_ci size_t len; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci line = console_buffer; 44262306a36Sopenharmony_ci for (i = 0; i < bytes_read; i++) { 44362306a36Sopenharmony_ci /* Output a line at a time, prefixed */ 44462306a36Sopenharmony_ci if (console_buffer[i] == '\n') { 44562306a36Sopenharmony_ci console_buffer[i] = '\0'; 44662306a36Sopenharmony_ci /* We need to output 'line', prefaced by 'leftover'. 44762306a36Sopenharmony_ci * However, it is possible we're being called to 44862306a36Sopenharmony_ci * output 'leftover' by itself (in the case of nothing 44962306a36Sopenharmony_ci * having been read from the console). 45062306a36Sopenharmony_ci * 45162306a36Sopenharmony_ci * To avoid duplication, check for this condition. 45262306a36Sopenharmony_ci */ 45362306a36Sopenharmony_ci if (console->leftover[0] && 45462306a36Sopenharmony_ci (line != console->leftover)) { 45562306a36Sopenharmony_ci if (console->print) 45662306a36Sopenharmony_ci (*console->print)(oct, (u32)console_num, 45762306a36Sopenharmony_ci console->leftover, 45862306a36Sopenharmony_ci line); 45962306a36Sopenharmony_ci console->leftover[0] = '\0'; 46062306a36Sopenharmony_ci } else { 46162306a36Sopenharmony_ci if (console->print) 46262306a36Sopenharmony_ci (*console->print)(oct, (u32)console_num, 46362306a36Sopenharmony_ci line, NULL); 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci line = &console_buffer[i + 1]; 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci /* Save off any leftovers */ 47062306a36Sopenharmony_ci if (line != &console_buffer[bytes_read]) { 47162306a36Sopenharmony_ci console_buffer[bytes_read] = '\0'; 47262306a36Sopenharmony_ci len = strlen(console->leftover); 47362306a36Sopenharmony_ci strscpy(&console->leftover[len], line, 47462306a36Sopenharmony_ci sizeof(console->leftover) - len + 1); 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cistatic void check_console(struct work_struct *work) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci s32 bytes_read, tries, total_read; 48162306a36Sopenharmony_ci size_t len; 48262306a36Sopenharmony_ci struct octeon_console *console; 48362306a36Sopenharmony_ci struct cavium_wk *wk = (struct cavium_wk *)work; 48462306a36Sopenharmony_ci struct octeon_device *oct = (struct octeon_device *)wk->ctxptr; 48562306a36Sopenharmony_ci u32 console_num = (u32)wk->ctxul; 48662306a36Sopenharmony_ci u32 delay; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci console = &oct->console[console_num]; 48962306a36Sopenharmony_ci tries = 0; 49062306a36Sopenharmony_ci total_read = 0; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci do { 49362306a36Sopenharmony_ci /* Take console output regardless of whether it will 49462306a36Sopenharmony_ci * be logged 49562306a36Sopenharmony_ci */ 49662306a36Sopenharmony_ci bytes_read = 49762306a36Sopenharmony_ci octeon_console_read(oct, console_num, console_buffer, 49862306a36Sopenharmony_ci sizeof(console_buffer) - 1); 49962306a36Sopenharmony_ci if (bytes_read > 0) { 50062306a36Sopenharmony_ci total_read += bytes_read; 50162306a36Sopenharmony_ci if (console->waiting) 50262306a36Sopenharmony_ci octeon_console_handle_result(oct, console_num); 50362306a36Sopenharmony_ci if (console->print) { 50462306a36Sopenharmony_ci output_console_line(oct, console, console_num, 50562306a36Sopenharmony_ci console_buffer, bytes_read); 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci } else if (bytes_read < 0) { 50862306a36Sopenharmony_ci dev_err(&oct->pci_dev->dev, "Error reading console %u, ret=%d\n", 50962306a36Sopenharmony_ci console_num, bytes_read); 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci tries++; 51362306a36Sopenharmony_ci } while ((bytes_read > 0) && (tries < 16)); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci /* If nothing is read after polling the console, 51662306a36Sopenharmony_ci * output any leftovers if any 51762306a36Sopenharmony_ci */ 51862306a36Sopenharmony_ci if (console->print && (total_read == 0) && 51962306a36Sopenharmony_ci (console->leftover[0])) { 52062306a36Sopenharmony_ci /* append '\n' as terminator for 'output_console_line' */ 52162306a36Sopenharmony_ci len = strlen(console->leftover); 52262306a36Sopenharmony_ci console->leftover[len] = '\n'; 52362306a36Sopenharmony_ci output_console_line(oct, console, console_num, 52462306a36Sopenharmony_ci console->leftover, (s32)(len + 1)); 52562306a36Sopenharmony_ci console->leftover[0] = '\0'; 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci delay = OCTEON_CONSOLE_POLL_INTERVAL_MS; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci schedule_delayed_work(&wk->work, msecs_to_jiffies(delay)); 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ciint octeon_init_consoles(struct octeon_device *oct) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci int ret = 0; 53662306a36Sopenharmony_ci u64 addr, size; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci ret = octeon_mem_access_ok(oct); 53962306a36Sopenharmony_ci if (ret) { 54062306a36Sopenharmony_ci dev_err(&oct->pci_dev->dev, "Memory access not okay'\n"); 54162306a36Sopenharmony_ci return ret; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci ret = octeon_named_block_find(oct, OCTEON_PCI_CONSOLE_BLOCK_NAME, &addr, 54562306a36Sopenharmony_ci &size); 54662306a36Sopenharmony_ci if (ret) { 54762306a36Sopenharmony_ci dev_err(&oct->pci_dev->dev, "Could not find console '%s'\n", 54862306a36Sopenharmony_ci OCTEON_PCI_CONSOLE_BLOCK_NAME); 54962306a36Sopenharmony_ci return ret; 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci /* Dedicate one of Octeon's BAR1 index registers to create a static 55362306a36Sopenharmony_ci * mapping to a region of Octeon DRAM that contains the PCI console 55462306a36Sopenharmony_ci * named block. 55562306a36Sopenharmony_ci */ 55662306a36Sopenharmony_ci oct->console_nb_info.bar1_index = BAR1_INDEX_STATIC_MAP; 55762306a36Sopenharmony_ci oct->fn_list.bar1_idx_setup(oct, addr, oct->console_nb_info.bar1_index, 55862306a36Sopenharmony_ci true); 55962306a36Sopenharmony_ci oct->console_nb_info.dram_region_base = addr 56062306a36Sopenharmony_ci & ~(OCTEON_BAR1_ENTRY_SIZE - 1ULL); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci /* num_consoles > 0, is an indication that the consoles 56362306a36Sopenharmony_ci * are accessible 56462306a36Sopenharmony_ci */ 56562306a36Sopenharmony_ci oct->num_consoles = octeon_read_device_mem32(oct, 56662306a36Sopenharmony_ci addr + offsetof(struct octeon_pci_console_desc, 56762306a36Sopenharmony_ci num_consoles)); 56862306a36Sopenharmony_ci oct->console_desc_addr = addr; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci dev_dbg(&oct->pci_dev->dev, "Initialized consoles. %d available\n", 57162306a36Sopenharmony_ci oct->num_consoles); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci return ret; 57462306a36Sopenharmony_ci} 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_cistatic void octeon_get_uboot_version(struct octeon_device *oct) 57762306a36Sopenharmony_ci{ 57862306a36Sopenharmony_ci s32 bytes_read, tries, total_read; 57962306a36Sopenharmony_ci struct octeon_console *console; 58062306a36Sopenharmony_ci u32 console_num = 0; 58162306a36Sopenharmony_ci char *uboot_ver; 58262306a36Sopenharmony_ci char *buf; 58362306a36Sopenharmony_ci char *p; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci#define OCTEON_UBOOT_VER_BUF_SIZE 512 58662306a36Sopenharmony_ci buf = kmalloc(OCTEON_UBOOT_VER_BUF_SIZE, GFP_KERNEL); 58762306a36Sopenharmony_ci if (!buf) 58862306a36Sopenharmony_ci return; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (octeon_console_send_cmd(oct, "setenv stdout pci\n", 50)) { 59162306a36Sopenharmony_ci kfree(buf); 59262306a36Sopenharmony_ci return; 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci if (octeon_console_send_cmd(oct, "version\n", 1)) { 59662306a36Sopenharmony_ci kfree(buf); 59762306a36Sopenharmony_ci return; 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci console = &oct->console[console_num]; 60162306a36Sopenharmony_ci tries = 0; 60262306a36Sopenharmony_ci total_read = 0; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci do { 60562306a36Sopenharmony_ci /* Take console output regardless of whether it will 60662306a36Sopenharmony_ci * be logged 60762306a36Sopenharmony_ci */ 60862306a36Sopenharmony_ci bytes_read = 60962306a36Sopenharmony_ci octeon_console_read(oct, 61062306a36Sopenharmony_ci console_num, buf + total_read, 61162306a36Sopenharmony_ci OCTEON_UBOOT_VER_BUF_SIZE - 1 - 61262306a36Sopenharmony_ci total_read); 61362306a36Sopenharmony_ci if (bytes_read > 0) { 61462306a36Sopenharmony_ci buf[bytes_read] = '\0'; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci total_read += bytes_read; 61762306a36Sopenharmony_ci if (console->waiting) 61862306a36Sopenharmony_ci octeon_console_handle_result(oct, console_num); 61962306a36Sopenharmony_ci } else if (bytes_read < 0) { 62062306a36Sopenharmony_ci dev_err(&oct->pci_dev->dev, "Error reading console %u, ret=%d\n", 62162306a36Sopenharmony_ci console_num, bytes_read); 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci tries++; 62562306a36Sopenharmony_ci } while ((bytes_read > 0) && (tries < 16)); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci /* If nothing is read after polling the console, 62862306a36Sopenharmony_ci * output any leftovers if any 62962306a36Sopenharmony_ci */ 63062306a36Sopenharmony_ci if ((total_read == 0) && (console->leftover[0])) { 63162306a36Sopenharmony_ci dev_dbg(&oct->pci_dev->dev, "%u: %s\n", 63262306a36Sopenharmony_ci console_num, console->leftover); 63362306a36Sopenharmony_ci console->leftover[0] = '\0'; 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci buf[OCTEON_UBOOT_VER_BUF_SIZE - 1] = '\0'; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci uboot_ver = strstr(buf, "U-Boot"); 63962306a36Sopenharmony_ci if (uboot_ver) { 64062306a36Sopenharmony_ci p = strstr(uboot_ver, "mips"); 64162306a36Sopenharmony_ci if (p) { 64262306a36Sopenharmony_ci p--; 64362306a36Sopenharmony_ci *p = '\0'; 64462306a36Sopenharmony_ci dev_info(&oct->pci_dev->dev, "%s\n", uboot_ver); 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci } 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci kfree(buf); 64962306a36Sopenharmony_ci octeon_console_send_cmd(oct, "setenv stdout serial\n", 50); 65062306a36Sopenharmony_ci} 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ciint octeon_add_console(struct octeon_device *oct, u32 console_num, 65362306a36Sopenharmony_ci char *dbg_enb) 65462306a36Sopenharmony_ci{ 65562306a36Sopenharmony_ci int ret = 0; 65662306a36Sopenharmony_ci u32 delay; 65762306a36Sopenharmony_ci u64 coreaddr; 65862306a36Sopenharmony_ci struct delayed_work *work; 65962306a36Sopenharmony_ci struct octeon_console *console; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci if (console_num >= oct->num_consoles) { 66262306a36Sopenharmony_ci dev_err(&oct->pci_dev->dev, 66362306a36Sopenharmony_ci "trying to read from console number %d when only 0 to %d exist\n", 66462306a36Sopenharmony_ci console_num, oct->num_consoles); 66562306a36Sopenharmony_ci } else { 66662306a36Sopenharmony_ci console = &oct->console[console_num]; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci console->waiting = 0; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci coreaddr = oct->console_desc_addr + console_num * 8 + 67162306a36Sopenharmony_ci offsetof(struct octeon_pci_console_desc, 67262306a36Sopenharmony_ci console_addr_array); 67362306a36Sopenharmony_ci console->addr = octeon_read_device_mem64(oct, coreaddr); 67462306a36Sopenharmony_ci coreaddr = console->addr + offsetof(struct octeon_pci_console, 67562306a36Sopenharmony_ci buf_size); 67662306a36Sopenharmony_ci console->buffer_size = octeon_read_device_mem32(oct, coreaddr); 67762306a36Sopenharmony_ci coreaddr = console->addr + offsetof(struct octeon_pci_console, 67862306a36Sopenharmony_ci input_base_addr); 67962306a36Sopenharmony_ci console->input_base_addr = 68062306a36Sopenharmony_ci octeon_read_device_mem64(oct, coreaddr); 68162306a36Sopenharmony_ci coreaddr = console->addr + offsetof(struct octeon_pci_console, 68262306a36Sopenharmony_ci output_base_addr); 68362306a36Sopenharmony_ci console->output_base_addr = 68462306a36Sopenharmony_ci octeon_read_device_mem64(oct, coreaddr); 68562306a36Sopenharmony_ci console->leftover[0] = '\0'; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci work = &oct->console_poll_work[console_num].work; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci octeon_get_uboot_version(oct); 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci INIT_DELAYED_WORK(work, check_console); 69262306a36Sopenharmony_ci oct->console_poll_work[console_num].ctxptr = (void *)oct; 69362306a36Sopenharmony_ci oct->console_poll_work[console_num].ctxul = console_num; 69462306a36Sopenharmony_ci delay = OCTEON_CONSOLE_POLL_INTERVAL_MS; 69562306a36Sopenharmony_ci schedule_delayed_work(work, msecs_to_jiffies(delay)); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci /* an empty string means use default debug console enablement */ 69862306a36Sopenharmony_ci if (dbg_enb && !dbg_enb[0]) 69962306a36Sopenharmony_ci dbg_enb = "setenv pci_console_active 1"; 70062306a36Sopenharmony_ci if (dbg_enb) 70162306a36Sopenharmony_ci ret = octeon_console_send_cmd(oct, dbg_enb, 2000); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci console->active = 1; 70462306a36Sopenharmony_ci } 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci return ret; 70762306a36Sopenharmony_ci} 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci/* 71062306a36Sopenharmony_ci * Removes all consoles 71162306a36Sopenharmony_ci * 71262306a36Sopenharmony_ci * @param oct octeon device 71362306a36Sopenharmony_ci */ 71462306a36Sopenharmony_civoid octeon_remove_consoles(struct octeon_device *oct) 71562306a36Sopenharmony_ci{ 71662306a36Sopenharmony_ci u32 i; 71762306a36Sopenharmony_ci struct octeon_console *console; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci for (i = 0; i < oct->num_consoles; i++) { 72062306a36Sopenharmony_ci console = &oct->console[i]; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci if (!console->active) 72362306a36Sopenharmony_ci continue; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci cancel_delayed_work_sync(&oct->console_poll_work[i]. 72662306a36Sopenharmony_ci work); 72762306a36Sopenharmony_ci console->addr = 0; 72862306a36Sopenharmony_ci console->buffer_size = 0; 72962306a36Sopenharmony_ci console->input_base_addr = 0; 73062306a36Sopenharmony_ci console->output_base_addr = 0; 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci oct->num_consoles = 0; 73462306a36Sopenharmony_ci} 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_cistatic inline int octeon_console_free_bytes(u32 buffer_size, 73762306a36Sopenharmony_ci u32 wr_idx, 73862306a36Sopenharmony_ci u32 rd_idx) 73962306a36Sopenharmony_ci{ 74062306a36Sopenharmony_ci if (rd_idx >= buffer_size || wr_idx >= buffer_size) 74162306a36Sopenharmony_ci return -1; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci return ((buffer_size - 1) - (wr_idx - rd_idx)) % buffer_size; 74462306a36Sopenharmony_ci} 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_cistatic inline int octeon_console_avail_bytes(u32 buffer_size, 74762306a36Sopenharmony_ci u32 wr_idx, 74862306a36Sopenharmony_ci u32 rd_idx) 74962306a36Sopenharmony_ci{ 75062306a36Sopenharmony_ci if (rd_idx >= buffer_size || wr_idx >= buffer_size) 75162306a36Sopenharmony_ci return -1; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci return buffer_size - 1 - 75462306a36Sopenharmony_ci octeon_console_free_bytes(buffer_size, wr_idx, rd_idx); 75562306a36Sopenharmony_ci} 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_cistatic int octeon_console_read(struct octeon_device *oct, u32 console_num, 75862306a36Sopenharmony_ci char *buffer, u32 buf_size) 75962306a36Sopenharmony_ci{ 76062306a36Sopenharmony_ci int bytes_to_read; 76162306a36Sopenharmony_ci u32 rd_idx, wr_idx; 76262306a36Sopenharmony_ci struct octeon_console *console; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci if (console_num >= oct->num_consoles) { 76562306a36Sopenharmony_ci dev_err(&oct->pci_dev->dev, "Attempted to read from disabled console %d\n", 76662306a36Sopenharmony_ci console_num); 76762306a36Sopenharmony_ci return 0; 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci console = &oct->console[console_num]; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci /* Check to see if any data is available. 77362306a36Sopenharmony_ci * Maybe optimize this with 64-bit read. 77462306a36Sopenharmony_ci */ 77562306a36Sopenharmony_ci rd_idx = octeon_read_device_mem32(oct, console->addr + 77662306a36Sopenharmony_ci offsetof(struct octeon_pci_console, output_read_index)); 77762306a36Sopenharmony_ci wr_idx = octeon_read_device_mem32(oct, console->addr + 77862306a36Sopenharmony_ci offsetof(struct octeon_pci_console, output_write_index)); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci bytes_to_read = octeon_console_avail_bytes(console->buffer_size, 78162306a36Sopenharmony_ci wr_idx, rd_idx); 78262306a36Sopenharmony_ci if (bytes_to_read <= 0) 78362306a36Sopenharmony_ci return bytes_to_read; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci bytes_to_read = min_t(s32, bytes_to_read, buf_size); 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci /* Check to see if what we want to read is not contiguous, and limit 78862306a36Sopenharmony_ci * ourselves to the contiguous block 78962306a36Sopenharmony_ci */ 79062306a36Sopenharmony_ci if (rd_idx + bytes_to_read >= console->buffer_size) 79162306a36Sopenharmony_ci bytes_to_read = console->buffer_size - rd_idx; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci octeon_pci_read_core_mem(oct, console->output_base_addr + rd_idx, 79462306a36Sopenharmony_ci (u8 *)buffer, bytes_to_read); 79562306a36Sopenharmony_ci octeon_write_device_mem32(oct, console->addr + 79662306a36Sopenharmony_ci offsetof(struct octeon_pci_console, 79762306a36Sopenharmony_ci output_read_index), 79862306a36Sopenharmony_ci (rd_idx + bytes_to_read) % 79962306a36Sopenharmony_ci console->buffer_size); 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci return bytes_to_read; 80262306a36Sopenharmony_ci} 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci#define FBUF_SIZE (4 * 1024 * 1024) 80562306a36Sopenharmony_ci#define MAX_BOOTTIME_SIZE 80 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ciint octeon_download_firmware(struct octeon_device *oct, const u8 *data, 80862306a36Sopenharmony_ci size_t size) 80962306a36Sopenharmony_ci{ 81062306a36Sopenharmony_ci struct octeon_firmware_file_header *h; 81162306a36Sopenharmony_ci char boottime[MAX_BOOTTIME_SIZE]; 81262306a36Sopenharmony_ci struct timespec64 ts; 81362306a36Sopenharmony_ci u32 crc32_result; 81462306a36Sopenharmony_ci u64 load_addr; 81562306a36Sopenharmony_ci u32 image_len; 81662306a36Sopenharmony_ci int ret = 0; 81762306a36Sopenharmony_ci u32 i, rem; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci if (size < sizeof(struct octeon_firmware_file_header)) { 82062306a36Sopenharmony_ci dev_err(&oct->pci_dev->dev, "Firmware file too small (%d < %d).\n", 82162306a36Sopenharmony_ci (u32)size, 82262306a36Sopenharmony_ci (u32)sizeof(struct octeon_firmware_file_header)); 82362306a36Sopenharmony_ci return -EINVAL; 82462306a36Sopenharmony_ci } 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci h = (struct octeon_firmware_file_header *)data; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci if (be32_to_cpu(h->magic) != LIO_NIC_MAGIC) { 82962306a36Sopenharmony_ci dev_err(&oct->pci_dev->dev, "Unrecognized firmware file.\n"); 83062306a36Sopenharmony_ci return -EINVAL; 83162306a36Sopenharmony_ci } 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci crc32_result = crc32((unsigned int)~0, data, 83462306a36Sopenharmony_ci sizeof(struct octeon_firmware_file_header) - 83562306a36Sopenharmony_ci sizeof(u32)) ^ ~0U; 83662306a36Sopenharmony_ci if (crc32_result != be32_to_cpu(h->crc32)) { 83762306a36Sopenharmony_ci dev_err(&oct->pci_dev->dev, "Firmware CRC mismatch (0x%08x != 0x%08x).\n", 83862306a36Sopenharmony_ci crc32_result, be32_to_cpu(h->crc32)); 83962306a36Sopenharmony_ci return -EINVAL; 84062306a36Sopenharmony_ci } 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci if (memcmp(LIQUIDIO_BASE_VERSION, h->version, 84362306a36Sopenharmony_ci strlen(LIQUIDIO_BASE_VERSION))) { 84462306a36Sopenharmony_ci dev_err(&oct->pci_dev->dev, "Unmatched firmware version. Expected %s.x, got %s.\n", 84562306a36Sopenharmony_ci LIQUIDIO_BASE_VERSION, 84662306a36Sopenharmony_ci h->version); 84762306a36Sopenharmony_ci return -EINVAL; 84862306a36Sopenharmony_ci } 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci if (be32_to_cpu(h->num_images) > LIO_MAX_IMAGES) { 85162306a36Sopenharmony_ci dev_err(&oct->pci_dev->dev, "Too many images in firmware file (%d).\n", 85262306a36Sopenharmony_ci be32_to_cpu(h->num_images)); 85362306a36Sopenharmony_ci return -EINVAL; 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci dev_info(&oct->pci_dev->dev, "Firmware version: %s\n", h->version); 85762306a36Sopenharmony_ci snprintf(oct->fw_info.liquidio_firmware_version, 32, "LIQUIDIO: %s", 85862306a36Sopenharmony_ci h->version); 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci data += sizeof(struct octeon_firmware_file_header); 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci dev_info(&oct->pci_dev->dev, "%s: Loading %d images\n", __func__, 86362306a36Sopenharmony_ci be32_to_cpu(h->num_images)); 86462306a36Sopenharmony_ci /* load all images */ 86562306a36Sopenharmony_ci for (i = 0; i < be32_to_cpu(h->num_images); i++) { 86662306a36Sopenharmony_ci load_addr = be64_to_cpu(h->desc[i].addr); 86762306a36Sopenharmony_ci image_len = be32_to_cpu(h->desc[i].len); 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci dev_info(&oct->pci_dev->dev, "Loading firmware %d at %llx\n", 87062306a36Sopenharmony_ci image_len, load_addr); 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci /* Write in 4MB chunks*/ 87362306a36Sopenharmony_ci rem = image_len; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci while (rem) { 87662306a36Sopenharmony_ci if (rem < FBUF_SIZE) 87762306a36Sopenharmony_ci size = rem; 87862306a36Sopenharmony_ci else 87962306a36Sopenharmony_ci size = FBUF_SIZE; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci /* download the image */ 88262306a36Sopenharmony_ci octeon_pci_write_core_mem(oct, load_addr, data, (u32)size); 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci data += size; 88562306a36Sopenharmony_ci rem -= (u32)size; 88662306a36Sopenharmony_ci load_addr += size; 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci } 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci /* Pass date and time information to NIC at the time of loading 89162306a36Sopenharmony_ci * firmware and periodically update the host time to NIC firmware. 89262306a36Sopenharmony_ci * This is to make NIC firmware use the same time reference as Host, 89362306a36Sopenharmony_ci * so that it is easy to correlate logs from firmware and host for 89462306a36Sopenharmony_ci * debugging. 89562306a36Sopenharmony_ci * 89662306a36Sopenharmony_ci * Octeon always uses UTC time. so timezone information is not sent. 89762306a36Sopenharmony_ci */ 89862306a36Sopenharmony_ci ktime_get_real_ts64(&ts); 89962306a36Sopenharmony_ci ret = snprintf(boottime, MAX_BOOTTIME_SIZE, 90062306a36Sopenharmony_ci " time_sec=%lld time_nsec=%ld", 90162306a36Sopenharmony_ci (s64)ts.tv_sec, ts.tv_nsec); 90262306a36Sopenharmony_ci if ((sizeof(h->bootcmd) - strnlen(h->bootcmd, sizeof(h->bootcmd))) < 90362306a36Sopenharmony_ci ret) { 90462306a36Sopenharmony_ci dev_err(&oct->pci_dev->dev, "Boot command buffer too small\n"); 90562306a36Sopenharmony_ci return -EINVAL; 90662306a36Sopenharmony_ci } 90762306a36Sopenharmony_ci strncat(h->bootcmd, boottime, 90862306a36Sopenharmony_ci sizeof(h->bootcmd) - strnlen(h->bootcmd, sizeof(h->bootcmd))); 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci dev_info(&oct->pci_dev->dev, "Writing boot command: %s\n", 91162306a36Sopenharmony_ci h->bootcmd); 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci /* Invoke the bootcmd */ 91462306a36Sopenharmony_ci ret = octeon_console_send_cmd(oct, h->bootcmd, 50); 91562306a36Sopenharmony_ci if (ret) 91662306a36Sopenharmony_ci dev_info(&oct->pci_dev->dev, "Boot command send failed\n"); 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci return ret; 91962306a36Sopenharmony_ci} 920