]>
Commit | Line | Data |
---|---|---|
8722ff8c | 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 | ||
f3791203 | 32 | static void initialize_config(struct rsxx_card_cfg *cfg) |
8722ff8c | 33 | { |
8722ff8c | 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; | |
9bb3c446 | 38 | cfg->data.vendor_id = RSXX_VENDOR_ID_IBM; |
8722ff8c | 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 | ||
45 | static 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 -------------------*/ | |
57 | static 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 | ||
63 | static 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 | ||
69 | static 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 | ||
78 | static 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 | ||
87 | static 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 ------------------*/ | |
c206c709 | 98 | static int rsxx_save_config(struct rsxx_cardinfo *card) |
8722ff8c | 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 | ||
131 | int 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"); | |
f3791203 | 182 | initialize_config(&card->config); |
8722ff8c | 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 |