1 | /* |
2 | * Copyright (c) 2000-2001 Adaptec Inc. |
3 | * All rights reserved. |
4 | * |
5 | * Redistribution and use in source and binary forms, with or without |
6 | * modification, are permitted provided that the following conditions |
7 | * are met: |
8 | * 1. Redistributions of source code must retain the above copyright |
9 | * notice, this list of conditions, and the following disclaimer, |
10 | * without modification. |
11 | * 2. Redistributions in binary form must reproduce at minimum a disclaimer |
12 | * substantially similar to the "NO WARRANTY" disclaimer below |
13 | * ("Disclaimer") and any redistribution must be conditioned upon |
14 | * including a substantially similar Disclaimer requirement for further |
15 | * binary redistribution. |
16 | * 3. Neither the names of the above-listed copyright holders nor the names |
17 | * of any contributors may be used to endorse or promote products derived |
18 | * from this software without specific prior written permission. |
19 | * |
20 | * Alternatively, this software may be distributed under the terms of the |
21 | * GNU General Public License ("GPL") version 2 as published by the Free |
22 | * Software Foundation. |
23 | * |
24 | * NO WARRANTY |
25 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
26 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
27 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR |
28 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
29 | * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
30 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
31 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
32 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
33 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
34 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
35 | * POSSIBILITY OF SUCH DAMAGES. |
36 | * |
37 | * String handling code courtesy of Gerard Roudier's <groudier@club-internet.fr> |
38 | * sym driver. |
39 | * |
40 | * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic7xxx_proc.c#29 $ |
41 | */ |
42 | #include "aic7xxx_osm.h" |
43 | #include "aic7xxx_inline.h" |
44 | #include "aic7xxx_93cx6.h" |
45 | |
46 | static void ahc_dump_target_state(struct ahc_softc *ahc, |
47 | struct seq_file *m, |
48 | u_int our_id, char channel, |
49 | u_int target_id, u_int target_offset); |
50 | static void ahc_dump_device_state(struct seq_file *m, |
51 | struct scsi_device *dev); |
52 | |
53 | /* |
54 | * Table of syncrates that don't follow the "divisible by 4" |
55 | * rule. This table will be expanded in future SCSI specs. |
56 | */ |
57 | static const struct { |
58 | u_int period_factor; |
59 | u_int period; /* in 100ths of ns */ |
60 | } scsi_syncrates[] = { |
61 | { 0x08, 625 }, /* FAST-160 */ |
62 | { 0x09, 1250 }, /* FAST-80 */ |
63 | { 0x0a, 2500 }, /* FAST-40 40MHz */ |
64 | { 0x0b, 3030 }, /* FAST-40 33MHz */ |
65 | { 0x0c, 5000 } /* FAST-20 */ |
66 | }; |
67 | |
68 | /* |
69 | * Return the frequency in kHz corresponding to the given |
70 | * sync period factor. |
71 | */ |
72 | static u_int |
73 | ahc_calc_syncsrate(u_int period_factor) |
74 | { |
75 | int i; |
76 | |
77 | /* See if the period is in the "exception" table */ |
78 | for (i = 0; i < ARRAY_SIZE(scsi_syncrates); i++) { |
79 | |
80 | if (period_factor == scsi_syncrates[i].period_factor) { |
81 | /* Period in kHz */ |
82 | return (100000000 / scsi_syncrates[i].period); |
83 | } |
84 | } |
85 | |
86 | /* |
87 | * Wasn't in the table, so use the standard |
88 | * 4 times conversion. |
89 | */ |
90 | return (10000000 / (period_factor * 4 * 10)); |
91 | } |
92 | |
93 | static void |
94 | ahc_format_transinfo(struct seq_file *m, struct ahc_transinfo *tinfo) |
95 | { |
96 | u_int speed; |
97 | u_int freq; |
98 | u_int mb; |
99 | |
100 | speed = 3300; |
101 | freq = 0; |
102 | if (tinfo->offset != 0) { |
103 | freq = ahc_calc_syncsrate(period_factor: tinfo->period); |
104 | speed = freq; |
105 | } |
106 | speed *= (0x01 << tinfo->width); |
107 | mb = speed / 1000; |
108 | if (mb > 0) |
109 | seq_printf(m, fmt: "%d.%03dMB/s transfers" , mb, speed % 1000); |
110 | else |
111 | seq_printf(m, fmt: "%dKB/s transfers" , speed); |
112 | |
113 | if (freq != 0) { |
114 | seq_printf(m, fmt: " (%d.%03dMHz%s, offset %d" , |
115 | freq / 1000, freq % 1000, |
116 | (tinfo->ppr_options & MSG_EXT_PPR_DT_REQ) != 0 |
117 | ? " DT" : "" , tinfo->offset); |
118 | } |
119 | |
120 | if (tinfo->width > 0) { |
121 | if (freq != 0) { |
122 | seq_puts(m, s: ", " ); |
123 | } else { |
124 | seq_puts(m, s: " (" ); |
125 | } |
126 | seq_printf(m, fmt: "%dbit)" , 8 * (0x01 << tinfo->width)); |
127 | } else if (freq != 0) { |
128 | seq_putc(m, c: ')'); |
129 | } |
130 | seq_putc(m, c: '\n'); |
131 | } |
132 | |
133 | static void |
134 | ahc_dump_target_state(struct ahc_softc *ahc, struct seq_file *m, |
135 | u_int our_id, char channel, u_int target_id, |
136 | u_int target_offset) |
137 | { |
138 | struct scsi_target *starget; |
139 | struct ahc_initiator_tinfo *tinfo; |
140 | struct ahc_tmode_tstate *tstate; |
141 | int lun; |
142 | |
143 | tinfo = ahc_fetch_transinfo(ahc, channel, our_id, |
144 | remote_id: target_id, tstate: &tstate); |
145 | if ((ahc->features & AHC_TWIN) != 0) |
146 | seq_printf(m, fmt: "Channel %c " , channel); |
147 | seq_printf(m, fmt: "Target %d Negotiation Settings\n" , target_id); |
148 | seq_puts(m, s: "\tUser: " ); |
149 | ahc_format_transinfo(m, tinfo: &tinfo->user); |
150 | starget = ahc->platform_data->starget[target_offset]; |
151 | if (!starget) |
152 | return; |
153 | |
154 | seq_puts(m, s: "\tGoal: " ); |
155 | ahc_format_transinfo(m, tinfo: &tinfo->goal); |
156 | seq_puts(m, s: "\tCurr: " ); |
157 | ahc_format_transinfo(m, tinfo: &tinfo->curr); |
158 | |
159 | for (lun = 0; lun < AHC_NUM_LUNS; lun++) { |
160 | struct scsi_device *sdev; |
161 | |
162 | sdev = scsi_device_lookup_by_target(starget, lun); |
163 | |
164 | if (sdev == NULL) |
165 | continue; |
166 | |
167 | ahc_dump_device_state(m, dev: sdev); |
168 | } |
169 | } |
170 | |
171 | static void |
172 | ahc_dump_device_state(struct seq_file *m, struct scsi_device *sdev) |
173 | { |
174 | struct ahc_linux_device *dev = scsi_transport_device_data(sdev); |
175 | |
176 | seq_printf(m, fmt: "\tChannel %c Target %d Lun %d Settings\n" , |
177 | sdev->sdev_target->channel + 'A', |
178 | sdev->sdev_target->id, (u8)sdev->lun); |
179 | |
180 | seq_printf(m, fmt: "\t\tCommands Queued %ld\n" , dev->commands_issued); |
181 | seq_printf(m, fmt: "\t\tCommands Active %d\n" , dev->active); |
182 | seq_printf(m, fmt: "\t\tCommand Openings %d\n" , dev->openings); |
183 | seq_printf(m, fmt: "\t\tMax Tagged Openings %d\n" , dev->maxtags); |
184 | seq_printf(m, fmt: "\t\tDevice Queue Frozen Count %d\n" , dev->qfrozen); |
185 | } |
186 | |
187 | int |
188 | ahc_proc_write_seeprom(struct Scsi_Host *shost, char *buffer, int length) |
189 | { |
190 | struct ahc_softc *ahc = *(struct ahc_softc **)shost->hostdata; |
191 | struct seeprom_descriptor sd; |
192 | int have_seeprom; |
193 | u_long s; |
194 | int paused; |
195 | int written; |
196 | |
197 | /* Default to failure. */ |
198 | written = -EINVAL; |
199 | ahc_lock(ahc, flags: &s); |
200 | paused = ahc_is_paused(ahc); |
201 | if (!paused) |
202 | ahc_pause(ahc); |
203 | |
204 | if (length != sizeof(struct seeprom_config)) { |
205 | printk("ahc_proc_write_seeprom: incorrect buffer size\n" ); |
206 | goto done; |
207 | } |
208 | |
209 | have_seeprom = ahc_verify_cksum(sc: (struct seeprom_config*)buffer); |
210 | if (have_seeprom == 0) { |
211 | printk("ahc_proc_write_seeprom: cksum verification failed\n" ); |
212 | goto done; |
213 | } |
214 | |
215 | sd.sd_ahc = ahc; |
216 | #if AHC_PCI_CONFIG > 0 |
217 | if ((ahc->chip & AHC_PCI) != 0) { |
218 | sd.sd_control_offset = SEECTL; |
219 | sd.sd_status_offset = SEECTL; |
220 | sd.sd_dataout_offset = SEECTL; |
221 | if (ahc->flags & AHC_LARGE_SEEPROM) |
222 | sd.sd_chip = C56_66; |
223 | else |
224 | sd.sd_chip = C46; |
225 | sd.sd_MS = SEEMS; |
226 | sd.sd_RDY = SEERDY; |
227 | sd.sd_CS = SEECS; |
228 | sd.sd_CK = SEECK; |
229 | sd.sd_DO = SEEDO; |
230 | sd.sd_DI = SEEDI; |
231 | have_seeprom = ahc_acquire_seeprom(ahc, sd: &sd); |
232 | } else |
233 | #endif |
234 | if ((ahc->chip & AHC_VL) != 0) { |
235 | sd.sd_control_offset = SEECTL_2840; |
236 | sd.sd_status_offset = STATUS_2840; |
237 | sd.sd_dataout_offset = STATUS_2840; |
238 | sd.sd_chip = C46; |
239 | sd.sd_MS = 0; |
240 | sd.sd_RDY = EEPROM_TF; |
241 | sd.sd_CS = CS_2840; |
242 | sd.sd_CK = CK_2840; |
243 | sd.sd_DO = DO_2840; |
244 | sd.sd_DI = DI_2840; |
245 | have_seeprom = TRUE; |
246 | } else { |
247 | printk("ahc_proc_write_seeprom: unsupported adapter type\n" ); |
248 | goto done; |
249 | } |
250 | |
251 | if (!have_seeprom) { |
252 | printk("ahc_proc_write_seeprom: No Serial EEPROM\n" ); |
253 | goto done; |
254 | } else { |
255 | u_int start_addr; |
256 | |
257 | if (ahc->seep_config == NULL) { |
258 | ahc->seep_config = kmalloc(size: sizeof(*ahc->seep_config), |
259 | GFP_ATOMIC); |
260 | if (ahc->seep_config == NULL) { |
261 | printk("aic7xxx: Unable to allocate serial " |
262 | "eeprom buffer. Write failing\n" ); |
263 | goto done; |
264 | } |
265 | } |
266 | printk("aic7xxx: Writing Serial EEPROM\n" ); |
267 | start_addr = 32 * (ahc->channel - 'A'); |
268 | ahc_write_seeprom(sd: &sd, buf: (u_int16_t *)buffer, start_addr, |
269 | count: sizeof(struct seeprom_config)/2); |
270 | ahc_read_seeprom(sd: &sd, buf: (uint16_t *)ahc->seep_config, |
271 | start_addr, count: sizeof(struct seeprom_config)/2); |
272 | #if AHC_PCI_CONFIG > 0 |
273 | if ((ahc->chip & AHC_VL) == 0) |
274 | ahc_release_seeprom(sd: &sd); |
275 | #endif |
276 | written = length; |
277 | } |
278 | |
279 | done: |
280 | if (!paused) |
281 | ahc_unpause(ahc); |
282 | ahc_unlock(ahc, flags: &s); |
283 | return (written); |
284 | } |
285 | |
286 | /* |
287 | * Return information to handle /proc support for the driver. |
288 | */ |
289 | int |
290 | ahc_linux_show_info(struct seq_file *m, struct Scsi_Host *shost) |
291 | { |
292 | struct ahc_softc *ahc = *(struct ahc_softc **)shost->hostdata; |
293 | char ahc_info[256]; |
294 | u_int max_targ; |
295 | u_int i; |
296 | |
297 | seq_printf(m, fmt: "Adaptec AIC7xxx driver version: %s\n" , |
298 | AIC7XXX_DRIVER_VERSION); |
299 | seq_printf(m, fmt: "%s\n" , ahc->description); |
300 | ahc_controller_info(ahc, buf: ahc_info); |
301 | seq_printf(m, fmt: "%s\n" , ahc_info); |
302 | seq_printf(m, fmt: "Allocated SCBs: %d, SG List Length: %d\n\n" , |
303 | ahc->scb_data->numscbs, AHC_NSEG); |
304 | |
305 | |
306 | if (ahc->seep_config == NULL) |
307 | seq_puts(m, s: "No Serial EEPROM\n" ); |
308 | else { |
309 | seq_puts(m, s: "Serial EEPROM:\n" ); |
310 | for (i = 0; i < sizeof(*ahc->seep_config)/2; i++) { |
311 | if (((i % 8) == 0) && (i != 0)) { |
312 | seq_putc(m, c: '\n'); |
313 | } |
314 | seq_printf(m, fmt: "0x%.4x " , |
315 | ((uint16_t*)ahc->seep_config)[i]); |
316 | } |
317 | seq_putc(m, c: '\n'); |
318 | } |
319 | seq_putc(m, c: '\n'); |
320 | |
321 | max_targ = 16; |
322 | if ((ahc->features & (AHC_WIDE|AHC_TWIN)) == 0) |
323 | max_targ = 8; |
324 | |
325 | for (i = 0; i < max_targ; i++) { |
326 | u_int our_id; |
327 | u_int target_id; |
328 | char channel; |
329 | |
330 | channel = 'A'; |
331 | our_id = ahc->our_id; |
332 | target_id = i; |
333 | if (i > 7 && (ahc->features & AHC_TWIN) != 0) { |
334 | channel = 'B'; |
335 | our_id = ahc->our_id_b; |
336 | target_id = i % 8; |
337 | } |
338 | |
339 | ahc_dump_target_state(ahc, m, our_id, |
340 | channel, target_id, target_offset: i); |
341 | } |
342 | return 0; |
343 | } |
344 | |