config.c 5.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
/*
* Filename: config.c
*
*
* Authors: Joshua Morris <josh.h.morris@us.ibm.com>
*	Philip Kelleher <pjk1939@linux.vnet.ibm.com>
*
* (C) Copyright 2013 IBM Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/

#include <linux/types.h>
#include <linux/crc32.h>
#include <linux/swab.h>

#include "rsxx_priv.h"
#include "rsxx_cfg.h"

32
static void initialize_config(struct rsxx_card_cfg *cfg)
33 34 35 36 37
{
	cfg->hdr.version = RSXX_CFG_VERSION;

	cfg->data.block_size        = RSXX_HW_BLK_SIZE;
	cfg->data.stripe_size       = RSXX_HW_BLK_SIZE;
38
	cfg->data.vendor_id         = RSXX_VENDOR_ID_IBM;
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
	cfg->data.cache_order       = (-1);
	cfg->data.intr_coal.mode    = RSXX_INTR_COAL_DISABLED;
	cfg->data.intr_coal.count   = 0;
	cfg->data.intr_coal.latency = 0;
}

static u32 config_data_crc32(struct rsxx_card_cfg *cfg)
{
	/*
	 * Return the compliment of the CRC to ensure compatibility
	 * (i.e. this is how early rsxx drivers did it.)
	 */

	return ~crc32(~0, &cfg->data, sizeof(cfg->data));
}


/*----------------- Config Byte Swap Functions -------------------*/
static void config_hdr_be_to_cpu(struct card_cfg_hdr *hdr)
{
	hdr->version = be32_to_cpu((__force __be32) hdr->version);
	hdr->crc     = be32_to_cpu((__force __be32) hdr->crc);
}

static void config_hdr_cpu_to_be(struct card_cfg_hdr *hdr)
{
	hdr->version = (__force u32) cpu_to_be32(hdr->version);
	hdr->crc     = (__force u32) cpu_to_be32(hdr->crc);
}

static void config_data_swab(struct rsxx_card_cfg *cfg)
{
	u32 *data = (u32 *) &cfg->data;
	int i;

	for (i = 0; i < (sizeof(cfg->data) / 4); i++)
		data[i] = swab32(data[i]);
}

static void config_data_le_to_cpu(struct rsxx_card_cfg *cfg)
{
	u32 *data = (u32 *) &cfg->data;
	int i;

	for (i = 0; i < (sizeof(cfg->data) / 4); i++)
		data[i] = le32_to_cpu((__force __le32) data[i]);
}

static void config_data_cpu_to_le(struct rsxx_card_cfg *cfg)
{
	u32 *data = (u32 *) &cfg->data;
	int i;

	for (i = 0; i < (sizeof(cfg->data) / 4); i++)
		data[i] = (__force u32) cpu_to_le32(data[i]);
}


/*----------------- Config Operations ------------------*/
98
static int rsxx_save_config(struct rsxx_cardinfo *card)
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
{
	struct rsxx_card_cfg cfg;
	int st;

	memcpy(&cfg, &card->config, sizeof(cfg));

	if (unlikely(cfg.hdr.version != RSXX_CFG_VERSION)) {
		dev_err(CARD_TO_DEV(card),
			"Cannot save config with invalid version %d\n",
			cfg.hdr.version);
		return -EINVAL;
	}

	/* Convert data to little endian for the CRC calculation. */
	config_data_cpu_to_le(&cfg);

	cfg.hdr.crc = config_data_crc32(&cfg);

	/*
	 * Swap the data from little endian to big endian so it can be
	 * stored.
	 */
	config_data_swab(&cfg);
	config_hdr_cpu_to_be(&cfg.hdr);

	st = rsxx_creg_write(card, CREG_ADD_CONFIG, sizeof(cfg), &cfg, 1);
	if (st)
		return st;

	return 0;
}

int rsxx_load_config(struct rsxx_cardinfo *card)
{
	int st;
	u32 crc;

	st = rsxx_creg_read(card, CREG_ADD_CONFIG, sizeof(card->config),
				&card->config, 1);
	if (st) {
		dev_err(CARD_TO_DEV(card),
			"Failed reading card config.\n");
		return st;
	}

	config_hdr_be_to_cpu(&card->config.hdr);

	if (card->config.hdr.version == RSXX_CFG_VERSION) {
		/*
		 * We calculate the CRC with the data in little endian, because
		 * early drivers did not take big endian CPUs into account.
		 * The data is always stored in big endian, so we need to byte
		 * swap it before calculating the CRC.
		 */

		config_data_swab(&card->config);

		/* Check the CRC */
		crc = config_data_crc32(&card->config);
		if (crc != card->config.hdr.crc) {
			dev_err(CARD_TO_DEV(card),
				"Config corruption detected!\n");
			dev_info(CARD_TO_DEV(card),
				"CRC (sb x%08x is x%08x)\n",
				card->config.hdr.crc, crc);
			return -EIO;
		}

		/* Convert the data to CPU byteorder */
		config_data_le_to_cpu(&card->config);

	} else if (card->config.hdr.version != 0) {
		dev_err(CARD_TO_DEV(card),
			"Invalid config version %d.\n",
			card->config.hdr.version);
		/*
		 * Config version changes require special handling from the
		 * user
		 */
		return -EINVAL;
	} else {
		dev_info(CARD_TO_DEV(card),
			"Initializing card configuration.\n");
182
		initialize_config(&card->config);
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
		st = rsxx_save_config(card);
		if (st)
			return st;
	}

	card->config_valid = 1;

	dev_dbg(CARD_TO_DEV(card), "version:     x%08x\n",
		card->config.hdr.version);
	dev_dbg(CARD_TO_DEV(card), "crc:         x%08x\n",
		card->config.hdr.crc);
	dev_dbg(CARD_TO_DEV(card), "block_size:  x%08x\n",
		card->config.data.block_size);
	dev_dbg(CARD_TO_DEV(card), "stripe_size: x%08x\n",
		card->config.data.stripe_size);
	dev_dbg(CARD_TO_DEV(card), "vendor_id:   x%08x\n",
		card->config.data.vendor_id);
	dev_dbg(CARD_TO_DEV(card), "cache_order: x%08x\n",
		card->config.data.cache_order);
	dev_dbg(CARD_TO_DEV(card), "mode:        x%08x\n",
		card->config.data.intr_coal.mode);
	dev_dbg(CARD_TO_DEV(card), "count:       x%08x\n",
		card->config.data.intr_coal.count);
	dev_dbg(CARD_TO_DEV(card), "latency:     x%08x\n",
		 card->config.data.intr_coal.latency);

	return 0;
}