1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3* Filename: config.c
4*
5* Authors: Joshua Morris <josh.h.morris@us.ibm.com>
6*	Philip Kelleher <pjk1939@linux.vnet.ibm.com>
7*
8* (C) Copyright 2013 IBM Corporation
9*/
10
11#include <linux/types.h>
12#include <linux/crc32.h>
13#include <linux/swab.h>
14
15#include "rsxx_priv.h"
16#include "rsxx_cfg.h"
17
18static void initialize_config(struct rsxx_card_cfg *cfg)
19{
20	cfg->hdr.version = RSXX_CFG_VERSION;
21
22	cfg->data.block_size        = RSXX_HW_BLK_SIZE;
23	cfg->data.stripe_size       = RSXX_HW_BLK_SIZE;
24	cfg->data.vendor_id         = RSXX_VENDOR_ID_IBM;
25	cfg->data.cache_order       = (-1);
26	cfg->data.intr_coal.mode    = RSXX_INTR_COAL_DISABLED;
27	cfg->data.intr_coal.count   = 0;
28	cfg->data.intr_coal.latency = 0;
29}
30
31static u32 config_data_crc32(struct rsxx_card_cfg *cfg)
32{
33	/*
34	 * Return the compliment of the CRC to ensure compatibility
35	 * (i.e. this is how early rsxx drivers did it.)
36	 */
37
38	return ~crc32(~0, &cfg->data, sizeof(cfg->data));
39}
40
41
42/*----------------- Config Byte Swap Functions -------------------*/
43static void config_hdr_be_to_cpu(struct card_cfg_hdr *hdr)
44{
45	hdr->version = be32_to_cpu((__force __be32) hdr->version);
46	hdr->crc     = be32_to_cpu((__force __be32) hdr->crc);
47}
48
49static void config_hdr_cpu_to_be(struct card_cfg_hdr *hdr)
50{
51	hdr->version = (__force u32) cpu_to_be32(hdr->version);
52	hdr->crc     = (__force u32) cpu_to_be32(hdr->crc);
53}
54
55static void config_data_swab(struct rsxx_card_cfg *cfg)
56{
57	u32 *data = (u32 *) &cfg->data;
58	int i;
59
60	for (i = 0; i < (sizeof(cfg->data) / 4); i++)
61		data[i] = swab32(data[i]);
62}
63
64static void config_data_le_to_cpu(struct rsxx_card_cfg *cfg)
65{
66	u32 *data = (u32 *) &cfg->data;
67	int i;
68
69	for (i = 0; i < (sizeof(cfg->data) / 4); i++)
70		data[i] = le32_to_cpu((__force __le32) data[i]);
71}
72
73static void config_data_cpu_to_le(struct rsxx_card_cfg *cfg)
74{
75	u32 *data = (u32 *) &cfg->data;
76	int i;
77
78	for (i = 0; i < (sizeof(cfg->data) / 4); i++)
79		data[i] = (__force u32) cpu_to_le32(data[i]);
80}
81
82
83/*----------------- Config Operations ------------------*/
84static int rsxx_save_config(struct rsxx_cardinfo *card)
85{
86	struct rsxx_card_cfg cfg;
87	int st;
88
89	memcpy(&cfg, &card->config, sizeof(cfg));
90
91	if (unlikely(cfg.hdr.version != RSXX_CFG_VERSION)) {
92		dev_err(CARD_TO_DEV(card),
93			"Cannot save config with invalid version %d\n",
94			cfg.hdr.version);
95		return -EINVAL;
96	}
97
98	/* Convert data to little endian for the CRC calculation. */
99	config_data_cpu_to_le(&cfg);
100
101	cfg.hdr.crc = config_data_crc32(&cfg);
102
103	/*
104	 * Swap the data from little endian to big endian so it can be
105	 * stored.
106	 */
107	config_data_swab(&cfg);
108	config_hdr_cpu_to_be(&cfg.hdr);
109
110	st = rsxx_creg_write(card, CREG_ADD_CONFIG, sizeof(cfg), &cfg, 1);
111	if (st)
112		return st;
113
114	return 0;
115}
116
117int rsxx_load_config(struct rsxx_cardinfo *card)
118{
119	int st;
120	u32 crc;
121
122	st = rsxx_creg_read(card, CREG_ADD_CONFIG, sizeof(card->config),
123				&card->config, 1);
124	if (st) {
125		dev_err(CARD_TO_DEV(card),
126			"Failed reading card config.\n");
127		return st;
128	}
129
130	config_hdr_be_to_cpu(&card->config.hdr);
131
132	if (card->config.hdr.version == RSXX_CFG_VERSION) {
133		/*
134		 * We calculate the CRC with the data in little endian, because
135		 * early drivers did not take big endian CPUs into account.
136		 * The data is always stored in big endian, so we need to byte
137		 * swap it before calculating the CRC.
138		 */
139
140		config_data_swab(&card->config);
141
142		/* Check the CRC */
143		crc = config_data_crc32(&card->config);
144		if (crc != card->config.hdr.crc) {
145			dev_err(CARD_TO_DEV(card),
146				"Config corruption detected!\n");
147			dev_info(CARD_TO_DEV(card),
148				"CRC (sb x%08x is x%08x)\n",
149				card->config.hdr.crc, crc);
150			return -EIO;
151		}
152
153		/* Convert the data to CPU byteorder */
154		config_data_le_to_cpu(&card->config);
155
156	} else if (card->config.hdr.version != 0) {
157		dev_err(CARD_TO_DEV(card),
158			"Invalid config version %d.\n",
159			card->config.hdr.version);
160		/*
161		 * Config version changes require special handling from the
162		 * user
163		 */
164		return -EINVAL;
165	} else {
166		dev_info(CARD_TO_DEV(card),
167			"Initializing card configuration.\n");
168		initialize_config(&card->config);
169		st = rsxx_save_config(card);
170		if (st)
171			return st;
172	}
173
174	card->config_valid = 1;
175
176	dev_dbg(CARD_TO_DEV(card), "version:     x%08x\n",
177		card->config.hdr.version);
178	dev_dbg(CARD_TO_DEV(card), "crc:         x%08x\n",
179		card->config.hdr.crc);
180	dev_dbg(CARD_TO_DEV(card), "block_size:  x%08x\n",
181		card->config.data.block_size);
182	dev_dbg(CARD_TO_DEV(card), "stripe_size: x%08x\n",
183		card->config.data.stripe_size);
184	dev_dbg(CARD_TO_DEV(card), "vendor_id:   x%08x\n",
185		card->config.data.vendor_id);
186	dev_dbg(CARD_TO_DEV(card), "cache_order: x%08x\n",
187		card->config.data.cache_order);
188	dev_dbg(CARD_TO_DEV(card), "mode:        x%08x\n",
189		card->config.data.intr_coal.mode);
190	dev_dbg(CARD_TO_DEV(card), "count:       x%08x\n",
191		card->config.data.intr_coal.count);
192	dev_dbg(CARD_TO_DEV(card), "latency:     x%08x\n",
193		 card->config.data.intr_coal.latency);
194
195	return 0;
196}
197
198