18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci* Filename: config.c 48c2ecf20Sopenharmony_ci* 58c2ecf20Sopenharmony_ci* Authors: Joshua Morris <josh.h.morris@us.ibm.com> 68c2ecf20Sopenharmony_ci* Philip Kelleher <pjk1939@linux.vnet.ibm.com> 78c2ecf20Sopenharmony_ci* 88c2ecf20Sopenharmony_ci* (C) Copyright 2013 IBM Corporation 98c2ecf20Sopenharmony_ci*/ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/types.h> 128c2ecf20Sopenharmony_ci#include <linux/crc32.h> 138c2ecf20Sopenharmony_ci#include <linux/swab.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include "rsxx_priv.h" 168c2ecf20Sopenharmony_ci#include "rsxx_cfg.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic void initialize_config(struct rsxx_card_cfg *cfg) 198c2ecf20Sopenharmony_ci{ 208c2ecf20Sopenharmony_ci cfg->hdr.version = RSXX_CFG_VERSION; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci cfg->data.block_size = RSXX_HW_BLK_SIZE; 238c2ecf20Sopenharmony_ci cfg->data.stripe_size = RSXX_HW_BLK_SIZE; 248c2ecf20Sopenharmony_ci cfg->data.vendor_id = RSXX_VENDOR_ID_IBM; 258c2ecf20Sopenharmony_ci cfg->data.cache_order = (-1); 268c2ecf20Sopenharmony_ci cfg->data.intr_coal.mode = RSXX_INTR_COAL_DISABLED; 278c2ecf20Sopenharmony_ci cfg->data.intr_coal.count = 0; 288c2ecf20Sopenharmony_ci cfg->data.intr_coal.latency = 0; 298c2ecf20Sopenharmony_ci} 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic u32 config_data_crc32(struct rsxx_card_cfg *cfg) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci /* 348c2ecf20Sopenharmony_ci * Return the compliment of the CRC to ensure compatibility 358c2ecf20Sopenharmony_ci * (i.e. this is how early rsxx drivers did it.) 368c2ecf20Sopenharmony_ci */ 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci return ~crc32(~0, &cfg->data, sizeof(cfg->data)); 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/*----------------- Config Byte Swap Functions -------------------*/ 438c2ecf20Sopenharmony_cistatic void config_hdr_be_to_cpu(struct card_cfg_hdr *hdr) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci hdr->version = be32_to_cpu((__force __be32) hdr->version); 468c2ecf20Sopenharmony_ci hdr->crc = be32_to_cpu((__force __be32) hdr->crc); 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic void config_hdr_cpu_to_be(struct card_cfg_hdr *hdr) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci hdr->version = (__force u32) cpu_to_be32(hdr->version); 528c2ecf20Sopenharmony_ci hdr->crc = (__force u32) cpu_to_be32(hdr->crc); 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic void config_data_swab(struct rsxx_card_cfg *cfg) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci u32 *data = (u32 *) &cfg->data; 588c2ecf20Sopenharmony_ci int i; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci for (i = 0; i < (sizeof(cfg->data) / 4); i++) 618c2ecf20Sopenharmony_ci data[i] = swab32(data[i]); 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic void config_data_le_to_cpu(struct rsxx_card_cfg *cfg) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci u32 *data = (u32 *) &cfg->data; 678c2ecf20Sopenharmony_ci int i; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci for (i = 0; i < (sizeof(cfg->data) / 4); i++) 708c2ecf20Sopenharmony_ci data[i] = le32_to_cpu((__force __le32) data[i]); 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic void config_data_cpu_to_le(struct rsxx_card_cfg *cfg) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci u32 *data = (u32 *) &cfg->data; 768c2ecf20Sopenharmony_ci int i; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci for (i = 0; i < (sizeof(cfg->data) / 4); i++) 798c2ecf20Sopenharmony_ci data[i] = (__force u32) cpu_to_le32(data[i]); 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/*----------------- Config Operations ------------------*/ 848c2ecf20Sopenharmony_cistatic int rsxx_save_config(struct rsxx_cardinfo *card) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci struct rsxx_card_cfg cfg; 878c2ecf20Sopenharmony_ci int st; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci memcpy(&cfg, &card->config, sizeof(cfg)); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci if (unlikely(cfg.hdr.version != RSXX_CFG_VERSION)) { 928c2ecf20Sopenharmony_ci dev_err(CARD_TO_DEV(card), 938c2ecf20Sopenharmony_ci "Cannot save config with invalid version %d\n", 948c2ecf20Sopenharmony_ci cfg.hdr.version); 958c2ecf20Sopenharmony_ci return -EINVAL; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci /* Convert data to little endian for the CRC calculation. */ 998c2ecf20Sopenharmony_ci config_data_cpu_to_le(&cfg); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci cfg.hdr.crc = config_data_crc32(&cfg); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci /* 1048c2ecf20Sopenharmony_ci * Swap the data from little endian to big endian so it can be 1058c2ecf20Sopenharmony_ci * stored. 1068c2ecf20Sopenharmony_ci */ 1078c2ecf20Sopenharmony_ci config_data_swab(&cfg); 1088c2ecf20Sopenharmony_ci config_hdr_cpu_to_be(&cfg.hdr); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci st = rsxx_creg_write(card, CREG_ADD_CONFIG, sizeof(cfg), &cfg, 1); 1118c2ecf20Sopenharmony_ci if (st) 1128c2ecf20Sopenharmony_ci return st; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci return 0; 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ciint rsxx_load_config(struct rsxx_cardinfo *card) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci int st; 1208c2ecf20Sopenharmony_ci u32 crc; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci st = rsxx_creg_read(card, CREG_ADD_CONFIG, sizeof(card->config), 1238c2ecf20Sopenharmony_ci &card->config, 1); 1248c2ecf20Sopenharmony_ci if (st) { 1258c2ecf20Sopenharmony_ci dev_err(CARD_TO_DEV(card), 1268c2ecf20Sopenharmony_ci "Failed reading card config.\n"); 1278c2ecf20Sopenharmony_ci return st; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci config_hdr_be_to_cpu(&card->config.hdr); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (card->config.hdr.version == RSXX_CFG_VERSION) { 1338c2ecf20Sopenharmony_ci /* 1348c2ecf20Sopenharmony_ci * We calculate the CRC with the data in little endian, because 1358c2ecf20Sopenharmony_ci * early drivers did not take big endian CPUs into account. 1368c2ecf20Sopenharmony_ci * The data is always stored in big endian, so we need to byte 1378c2ecf20Sopenharmony_ci * swap it before calculating the CRC. 1388c2ecf20Sopenharmony_ci */ 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci config_data_swab(&card->config); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* Check the CRC */ 1438c2ecf20Sopenharmony_ci crc = config_data_crc32(&card->config); 1448c2ecf20Sopenharmony_ci if (crc != card->config.hdr.crc) { 1458c2ecf20Sopenharmony_ci dev_err(CARD_TO_DEV(card), 1468c2ecf20Sopenharmony_ci "Config corruption detected!\n"); 1478c2ecf20Sopenharmony_ci dev_info(CARD_TO_DEV(card), 1488c2ecf20Sopenharmony_ci "CRC (sb x%08x is x%08x)\n", 1498c2ecf20Sopenharmony_ci card->config.hdr.crc, crc); 1508c2ecf20Sopenharmony_ci return -EIO; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci /* Convert the data to CPU byteorder */ 1548c2ecf20Sopenharmony_ci config_data_le_to_cpu(&card->config); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci } else if (card->config.hdr.version != 0) { 1578c2ecf20Sopenharmony_ci dev_err(CARD_TO_DEV(card), 1588c2ecf20Sopenharmony_ci "Invalid config version %d.\n", 1598c2ecf20Sopenharmony_ci card->config.hdr.version); 1608c2ecf20Sopenharmony_ci /* 1618c2ecf20Sopenharmony_ci * Config version changes require special handling from the 1628c2ecf20Sopenharmony_ci * user 1638c2ecf20Sopenharmony_ci */ 1648c2ecf20Sopenharmony_ci return -EINVAL; 1658c2ecf20Sopenharmony_ci } else { 1668c2ecf20Sopenharmony_ci dev_info(CARD_TO_DEV(card), 1678c2ecf20Sopenharmony_ci "Initializing card configuration.\n"); 1688c2ecf20Sopenharmony_ci initialize_config(&card->config); 1698c2ecf20Sopenharmony_ci st = rsxx_save_config(card); 1708c2ecf20Sopenharmony_ci if (st) 1718c2ecf20Sopenharmony_ci return st; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci card->config_valid = 1; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci dev_dbg(CARD_TO_DEV(card), "version: x%08x\n", 1778c2ecf20Sopenharmony_ci card->config.hdr.version); 1788c2ecf20Sopenharmony_ci dev_dbg(CARD_TO_DEV(card), "crc: x%08x\n", 1798c2ecf20Sopenharmony_ci card->config.hdr.crc); 1808c2ecf20Sopenharmony_ci dev_dbg(CARD_TO_DEV(card), "block_size: x%08x\n", 1818c2ecf20Sopenharmony_ci card->config.data.block_size); 1828c2ecf20Sopenharmony_ci dev_dbg(CARD_TO_DEV(card), "stripe_size: x%08x\n", 1838c2ecf20Sopenharmony_ci card->config.data.stripe_size); 1848c2ecf20Sopenharmony_ci dev_dbg(CARD_TO_DEV(card), "vendor_id: x%08x\n", 1858c2ecf20Sopenharmony_ci card->config.data.vendor_id); 1868c2ecf20Sopenharmony_ci dev_dbg(CARD_TO_DEV(card), "cache_order: x%08x\n", 1878c2ecf20Sopenharmony_ci card->config.data.cache_order); 1888c2ecf20Sopenharmony_ci dev_dbg(CARD_TO_DEV(card), "mode: x%08x\n", 1898c2ecf20Sopenharmony_ci card->config.data.intr_coal.mode); 1908c2ecf20Sopenharmony_ci dev_dbg(CARD_TO_DEV(card), "count: x%08x\n", 1918c2ecf20Sopenharmony_ci card->config.data.intr_coal.count); 1928c2ecf20Sopenharmony_ci dev_dbg(CARD_TO_DEV(card), "latency: x%08x\n", 1938c2ecf20Sopenharmony_ci card->config.data.intr_coal.latency); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci return 0; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 198