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