| /* |
| * 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; |
| } |
| |