1/*
2* Filename: config.c
3*
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* This program is free software; you can redistribute it and/or
11* modify it under the terms of the GNU General Public License as
12* published by the Free Software Foundation; either version 2 of the
13* License, or (at your option) any later version.
14*
15* This program is distributed in the hope that it will be useful, but
16* WITHOUT ANY WARRANTY; without even the implied warranty of
17* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18* General Public License for more details.
19*
20* You should have received a copy of the GNU General Public License
21* along with this program; if not, write to the Free Software Foundation,
22* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23*/
24
25#include <linux/types.h>
26#include <linux/crc32.h>
27#include <linux/swab.h>
28
29#include "rsxx_priv.h"
30#include "rsxx_cfg.h"
31
32static void initialize_config(struct rsxx_card_cfg *cfg)
33{
34	cfg->hdr.version = RSXX_CFG_VERSION;
35
36	cfg->data.block_size        = RSXX_HW_BLK_SIZE;
37	cfg->data.stripe_size       = RSXX_HW_BLK_SIZE;
38	cfg->data.vendor_id         = RSXX_VENDOR_ID_IBM;
39	cfg->data.cache_order       = (-1);
40	cfg->data.intr_coal.mode    = RSXX_INTR_COAL_DISABLED;
41	cfg->data.intr_coal.count   = 0;
42	cfg->data.intr_coal.latency = 0;
43}
44
45static u32 config_data_crc32(struct rsxx_card_cfg *cfg)
46{
47	/*
48	 * Return the compliment of the CRC to ensure compatibility
49	 * (i.e. this is how early rsxx drivers did it.)
50	 */
51
52	return ~crc32(~0, &cfg->data, sizeof(cfg->data));
53}
54
55
56/*----------------- Config Byte Swap Functions -------------------*/
57static void config_hdr_be_to_cpu(struct card_cfg_hdr *hdr)
58{
59	hdr->version = be32_to_cpu((__force __be32) hdr->version);
60	hdr->crc     = be32_to_cpu((__force __be32) hdr->crc);
61}
62
63static void config_hdr_cpu_to_be(struct card_cfg_hdr *hdr)
64{
65	hdr->version = (__force u32) cpu_to_be32(hdr->version);
66	hdr->crc     = (__force u32) cpu_to_be32(hdr->crc);
67}
68
69static void config_data_swab(struct rsxx_card_cfg *cfg)
70{
71	u32 *data = (u32 *) &cfg->data;
72	int i;
73
74	for (i = 0; i < (sizeof(cfg->data) / 4); i++)
75		data[i] = swab32(data[i]);
76}
77
78static void config_data_le_to_cpu(struct rsxx_card_cfg *cfg)
79{
80	u32 *data = (u32 *) &cfg->data;
81	int i;
82
83	for (i = 0; i < (sizeof(cfg->data) / 4); i++)
84		data[i] = le32_to_cpu((__force __le32) data[i]);
85}
86
87static void config_data_cpu_to_le(struct rsxx_card_cfg *cfg)
88{
89	u32 *data = (u32 *) &cfg->data;
90	int i;
91
92	for (i = 0; i < (sizeof(cfg->data) / 4); i++)
93		data[i] = (__force u32) cpu_to_le32(data[i]);
94}
95
96
97/*----------------- Config Operations ------------------*/
98static int rsxx_save_config(struct rsxx_cardinfo *card)
99{
100	struct rsxx_card_cfg cfg;
101	int st;
102
103	memcpy(&cfg, &card->config, sizeof(cfg));
104
105	if (unlikely(cfg.hdr.version != RSXX_CFG_VERSION)) {
106		dev_err(CARD_TO_DEV(card),
107			"Cannot save config with invalid version %d\n",
108			cfg.hdr.version);
109		return -EINVAL;
110	}
111
112	/* Convert data to little endian for the CRC calculation. */
113	config_data_cpu_to_le(&cfg);
114
115	cfg.hdr.crc = config_data_crc32(&cfg);
116
117	/*
118	 * Swap the data from little endian to big endian so it can be
119	 * stored.
120	 */
121	config_data_swab(&cfg);
122	config_hdr_cpu_to_be(&cfg.hdr);
123
124	st = rsxx_creg_write(card, CREG_ADD_CONFIG, sizeof(cfg), &cfg, 1);
125	if (st)
126		return st;
127
128	return 0;
129}
130
131int rsxx_load_config(struct rsxx_cardinfo *card)
132{
133	int st;
134	u32 crc;
135
136	st = rsxx_creg_read(card, CREG_ADD_CONFIG, sizeof(card->config),
137				&card->config, 1);
138	if (st) {
139		dev_err(CARD_TO_DEV(card),
140			"Failed reading card config.\n");
141		return st;
142	}
143
144	config_hdr_be_to_cpu(&card->config.hdr);
145
146	if (card->config.hdr.version == RSXX_CFG_VERSION) {
147		/*
148		 * We calculate the CRC with the data in little endian, because
149		 * early drivers did not take big endian CPUs into account.
150		 * The data is always stored in big endian, so we need to byte
151		 * swap it before calculating the CRC.
152		 */
153
154		config_data_swab(&card->config);
155
156		/* Check the CRC */
157		crc = config_data_crc32(&card->config);
158		if (crc != card->config.hdr.crc) {
159			dev_err(CARD_TO_DEV(card),
160				"Config corruption detected!\n");
161			dev_info(CARD_TO_DEV(card),
162				"CRC (sb x%08x is x%08x)\n",
163				card->config.hdr.crc, crc);
164			return -EIO;
165		}
166
167		/* Convert the data to CPU byteorder */
168		config_data_le_to_cpu(&card->config);
169
170	} else if (card->config.hdr.version != 0) {
171		dev_err(CARD_TO_DEV(card),
172			"Invalid config version %d.\n",
173			card->config.hdr.version);
174		/*
175		 * Config version changes require special handling from the
176		 * user
177		 */
178		return -EINVAL;
179	} else {
180		dev_info(CARD_TO_DEV(card),
181			"Initializing card configuration.\n");
182		initialize_config(&card->config);
183		st = rsxx_save_config(card);
184		if (st)
185			return st;
186	}
187
188	card->config_valid = 1;
189
190	dev_dbg(CARD_TO_DEV(card), "version:     x%08x\n",
191		card->config.hdr.version);
192	dev_dbg(CARD_TO_DEV(card), "crc:         x%08x\n",
193		card->config.hdr.crc);
194	dev_dbg(CARD_TO_DEV(card), "block_size:  x%08x\n",
195		card->config.data.block_size);
196	dev_dbg(CARD_TO_DEV(card), "stripe_size: x%08x\n",
197		card->config.data.stripe_size);
198	dev_dbg(CARD_TO_DEV(card), "vendor_id:   x%08x\n",
199		card->config.data.vendor_id);
200	dev_dbg(CARD_TO_DEV(card), "cache_order: x%08x\n",
201		card->config.data.cache_order);
202	dev_dbg(CARD_TO_DEV(card), "mode:        x%08x\n",
203		card->config.data.intr_coal.mode);
204	dev_dbg(CARD_TO_DEV(card), "count:       x%08x\n",
205		card->config.data.intr_coal.count);
206	dev_dbg(CARD_TO_DEV(card), "latency:     x%08x\n",
207		 card->config.data.intr_coal.latency);
208
209	return 0;
210}
211
212