/* * 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" static void initialize_config(struct rsxx_card_cfg *cfg) { cfg->hdr.version = RSXX_CFG_VERSION; cfg->data.block_size = RSXX_HW_BLK_SIZE; cfg->data.stripe_size = RSXX_HW_BLK_SIZE; cfg->data.vendor_id = RSXX_VENDOR_ID_IBM; 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 ------------------*/ static int rsxx_save_config(struct rsxx_cardinfo *card) { 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"); initialize_config(&card->config); 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; }