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 | |
32 | static void initialize_config(void *config) |
33 | { |
34 | struct rsxx_card_cfg *cfg = (struct rsxx_card_cfg *) config; |
35 | |
36 | cfg->hdr.version = RSXX_CFG_VERSION; |
37 | |
38 | cfg->data.block_size = RSXX_HW_BLK_SIZE; |
39 | cfg->data.stripe_size = RSXX_HW_BLK_SIZE; |
40 | cfg->data.vendor_id = RSXX_VENDOR_ID_TMS_IBM; |
41 | cfg->data.cache_order = (-1); |
42 | cfg->data.intr_coal.mode = RSXX_INTR_COAL_DISABLED; |
43 | cfg->data.intr_coal.count = 0; |
44 | cfg->data.intr_coal.latency = 0; |
45 | } |
46 | |
47 | static u32 config_data_crc32(struct rsxx_card_cfg *cfg) |
48 | { |
49 | /* |
50 | * Return the compliment of the CRC to ensure compatibility |
51 | * (i.e. this is how early rsxx drivers did it.) |
52 | */ |
53 | |
54 | return ~crc32(~0, &cfg->data, sizeof(cfg->data)); |
55 | } |
56 | |
57 | |
58 | /*----------------- Config Byte Swap Functions -------------------*/ |
59 | static void config_hdr_be_to_cpu(struct card_cfg_hdr *hdr) |
60 | { |
61 | hdr->version = be32_to_cpu((__force __be32) hdr->version); |
62 | hdr->crc = be32_to_cpu((__force __be32) hdr->crc); |
63 | } |
64 | |
65 | static void config_hdr_cpu_to_be(struct card_cfg_hdr *hdr) |
66 | { |
67 | hdr->version = (__force u32) cpu_to_be32(hdr->version); |
68 | hdr->crc = (__force u32) cpu_to_be32(hdr->crc); |
69 | } |
70 | |
71 | static void config_data_swab(struct rsxx_card_cfg *cfg) |
72 | { |
73 | u32 *data = (u32 *) &cfg->data; |
74 | int i; |
75 | |
76 | for (i = 0; i < (sizeof(cfg->data) / 4); i++) |
77 | data[i] = swab32(data[i]); |
78 | } |
79 | |
80 | static void config_data_le_to_cpu(struct rsxx_card_cfg *cfg) |
81 | { |
82 | u32 *data = (u32 *) &cfg->data; |
83 | int i; |
84 | |
85 | for (i = 0; i < (sizeof(cfg->data) / 4); i++) |
86 | data[i] = le32_to_cpu((__force __le32) data[i]); |
87 | } |
88 | |
89 | static void config_data_cpu_to_le(struct rsxx_card_cfg *cfg) |
90 | { |
91 | u32 *data = (u32 *) &cfg->data; |
92 | int i; |
93 | |
94 | for (i = 0; i < (sizeof(cfg->data) / 4); i++) |
95 | data[i] = (__force u32) cpu_to_le32(data[i]); |
96 | } |
97 | |
98 | |
99 | /*----------------- Config Operations ------------------*/ |
100 | int rsxx_save_config(struct rsxx_cardinfo *card) |
101 | { |
102 | struct rsxx_card_cfg cfg; |
103 | int st; |
104 | |
105 | memcpy(&cfg, &card->config, sizeof(cfg)); |
106 | |
107 | if (unlikely(cfg.hdr.version != RSXX_CFG_VERSION)) { |
108 | dev_err(CARD_TO_DEV(card), |
109 | "Cannot save config with invalid version %d\n", |
110 | cfg.hdr.version); |
111 | return -EINVAL; |
112 | } |
113 | |
114 | /* Convert data to little endian for the CRC calculation. */ |
115 | config_data_cpu_to_le(&cfg); |
116 | |
117 | cfg.hdr.crc = config_data_crc32(&cfg); |
118 | |
119 | /* |
120 | * Swap the data from little endian to big endian so it can be |
121 | * stored. |
122 | */ |
123 | config_data_swab(&cfg); |
124 | config_hdr_cpu_to_be(&cfg.hdr); |
125 | |
126 | st = rsxx_creg_write(card, CREG_ADD_CONFIG, sizeof(cfg), &cfg, 1); |
127 | if (st) |
128 | return st; |
129 | |
130 | return 0; |
131 | } |
132 | |
133 | int rsxx_load_config(struct rsxx_cardinfo *card) |
134 | { |
135 | int st; |
136 | u32 crc; |
137 | |
138 | st = rsxx_creg_read(card, CREG_ADD_CONFIG, sizeof(card->config), |
139 | &card->config, 1); |
140 | if (st) { |
141 | dev_err(CARD_TO_DEV(card), |
142 | "Failed reading card config.\n"); |
143 | return st; |
144 | } |
145 | |
146 | config_hdr_be_to_cpu(&card->config.hdr); |
147 | |
148 | if (card->config.hdr.version == RSXX_CFG_VERSION) { |
149 | /* |
150 | * We calculate the CRC with the data in little endian, because |
151 | * early drivers did not take big endian CPUs into account. |
152 | * The data is always stored in big endian, so we need to byte |
153 | * swap it before calculating the CRC. |
154 | */ |
155 | |
156 | config_data_swab(&card->config); |
157 | |
158 | /* Check the CRC */ |
159 | crc = config_data_crc32(&card->config); |
160 | if (crc != card->config.hdr.crc) { |
161 | dev_err(CARD_TO_DEV(card), |
162 | "Config corruption detected!\n"); |
163 | dev_info(CARD_TO_DEV(card), |
164 | "CRC (sb x%08x is x%08x)\n", |
165 | card->config.hdr.crc, crc); |
166 | return -EIO; |
167 | } |
168 | |
169 | /* Convert the data to CPU byteorder */ |
170 | config_data_le_to_cpu(&card->config); |
171 | |
172 | } else if (card->config.hdr.version != 0) { |
173 | dev_err(CARD_TO_DEV(card), |
174 | "Invalid config version %d.\n", |
175 | card->config.hdr.version); |
176 | /* |
177 | * Config version changes require special handling from the |
178 | * user |
179 | */ |
180 | return -EINVAL; |
181 | } else { |
182 | dev_info(CARD_TO_DEV(card), |
183 | "Initializing card configuration.\n"); |
184 | initialize_config(card); |
185 | st = rsxx_save_config(card); |
186 | if (st) |
187 | return st; |
188 | } |
189 | |
190 | card->config_valid = 1; |
191 | |
192 | dev_dbg(CARD_TO_DEV(card), "version: x%08x\n", |
193 | card->config.hdr.version); |
194 | dev_dbg(CARD_TO_DEV(card), "crc: x%08x\n", |
195 | card->config.hdr.crc); |
196 | dev_dbg(CARD_TO_DEV(card), "block_size: x%08x\n", |
197 | card->config.data.block_size); |
198 | dev_dbg(CARD_TO_DEV(card), "stripe_size: x%08x\n", |
199 | card->config.data.stripe_size); |
200 | dev_dbg(CARD_TO_DEV(card), "vendor_id: x%08x\n", |
201 | card->config.data.vendor_id); |
202 | dev_dbg(CARD_TO_DEV(card), "cache_order: x%08x\n", |
203 | card->config.data.cache_order); |
204 | dev_dbg(CARD_TO_DEV(card), "mode: x%08x\n", |
205 | card->config.data.intr_coal.mode); |
206 | dev_dbg(CARD_TO_DEV(card), "count: x%08x\n", |
207 | card->config.data.intr_coal.count); |
208 | dev_dbg(CARD_TO_DEV(card), "latency: x%08x\n", |
209 | card->config.data.intr_coal.latency); |
210 | |
211 | return 0; |
212 | } |
213 | |