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 | |