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/aic79xx_proc.c#19 $ |
41 | */ |
42 | #include "aic79xx_osm.h" |
43 | #include "aic79xx_inline.h" |
44 | |
45 | static void ahd_dump_target_state(struct ahd_softc *ahd, |
46 | struct seq_file *m, |
47 | u_int our_id, char channel, |
48 | u_int target_id); |
49 | static void ahd_dump_device_state(struct seq_file *m, |
50 | struct scsi_device *sdev); |
51 | |
52 | /* |
53 | * Table of syncrates that don't follow the "divisible by 4" |
54 | * rule. This table will be expanded in future SCSI specs. |
55 | */ |
56 | static const struct { |
57 | u_int period_factor; |
58 | u_int period; /* in 100ths of ns */ |
59 | } scsi_syncrates[] = { |
60 | { 0x08, 625 }, /* FAST-160 */ |
61 | { 0x09, 1250 }, /* FAST-80 */ |
62 | { 0x0a, 2500 }, /* FAST-40 40MHz */ |
63 | { 0x0b, 3030 }, /* FAST-40 33MHz */ |
64 | { 0x0c, 5000 } /* FAST-20 */ |
65 | }; |
66 | |
67 | /* |
68 | * Return the frequency in kHz corresponding to the given |
69 | * sync period factor. |
70 | */ |
71 | static u_int |
72 | ahd_calc_syncsrate(u_int period_factor) |
73 | { |
74 | int i; |
75 | |
76 | /* See if the period is in the "exception" table */ |
77 | for (i = 0; i < ARRAY_SIZE(scsi_syncrates); i++) { |
78 | |
79 | if (period_factor == scsi_syncrates[i].period_factor) { |
80 | /* Period in kHz */ |
81 | return (100000000 / scsi_syncrates[i].period); |
82 | } |
83 | } |
84 | |
85 | /* |
86 | * Wasn't in the table, so use the standard |
87 | * 4 times conversion. |
88 | */ |
89 | return (10000000 / (period_factor * 4 * 10)); |
90 | } |
91 | |
92 | static void |
93 | ahd_format_transinfo(struct seq_file *m, struct ahd_transinfo *tinfo) |
94 | { |
95 | u_int speed; |
96 | u_int freq; |
97 | u_int mb; |
98 | |
99 | if (tinfo->period == AHD_PERIOD_UNKNOWN) { |
100 | seq_puts(m, s: "Renegotiation Pending\n" ); |
101 | return; |
102 | } |
103 | speed = 3300; |
104 | freq = 0; |
105 | if (tinfo->offset != 0) { |
106 | freq = ahd_calc_syncsrate(period_factor: tinfo->period); |
107 | speed = freq; |
108 | } |
109 | speed *= (0x01 << tinfo->width); |
110 | mb = speed / 1000; |
111 | if (mb > 0) |
112 | seq_printf(m, fmt: "%d.%03dMB/s transfers" , mb, speed % 1000); |
113 | else |
114 | seq_printf(m, fmt: "%dKB/s transfers" , speed); |
115 | |
116 | if (freq != 0) { |
117 | int printed_options; |
118 | |
119 | printed_options = 0; |
120 | seq_printf(m, fmt: " (%d.%03dMHz" , freq / 1000, freq % 1000); |
121 | if ((tinfo->ppr_options & MSG_EXT_PPR_RD_STRM) != 0) { |
122 | seq_puts(m, s: " RDSTRM" ); |
123 | printed_options++; |
124 | } |
125 | if ((tinfo->ppr_options & MSG_EXT_PPR_DT_REQ) != 0) { |
126 | seq_puts(m, s: printed_options ? "|DT" : " DT" ); |
127 | printed_options++; |
128 | } |
129 | if ((tinfo->ppr_options & MSG_EXT_PPR_IU_REQ) != 0) { |
130 | seq_puts(m, s: printed_options ? "|IU" : " IU" ); |
131 | printed_options++; |
132 | } |
133 | if ((tinfo->ppr_options & MSG_EXT_PPR_RTI) != 0) { |
134 | seq_puts(m, s: printed_options ? "|RTI" : " RTI" ); |
135 | printed_options++; |
136 | } |
137 | if ((tinfo->ppr_options & MSG_EXT_PPR_QAS_REQ) != 0) { |
138 | seq_puts(m, s: printed_options ? "|QAS" : " QAS" ); |
139 | printed_options++; |
140 | } |
141 | } |
142 | |
143 | if (tinfo->width > 0) { |
144 | if (freq != 0) { |
145 | seq_puts(m, s: ", " ); |
146 | } else { |
147 | seq_puts(m, s: " (" ); |
148 | } |
149 | seq_printf(m, fmt: "%dbit)" , 8 * (0x01 << tinfo->width)); |
150 | } else if (freq != 0) { |
151 | seq_putc(m, c: ')'); |
152 | } |
153 | seq_putc(m, c: '\n'); |
154 | } |
155 | |
156 | static void |
157 | ahd_dump_target_state(struct ahd_softc *ahd, struct seq_file *m, |
158 | u_int our_id, char channel, u_int target_id) |
159 | { |
160 | struct scsi_target *starget; |
161 | struct ahd_initiator_tinfo *tinfo; |
162 | struct ahd_tmode_tstate *tstate; |
163 | int lun; |
164 | |
165 | tinfo = ahd_fetch_transinfo(ahd, channel, our_id, |
166 | remote_id: target_id, tstate: &tstate); |
167 | seq_printf(m, fmt: "Target %d Negotiation Settings\n" , target_id); |
168 | seq_puts(m, s: "\tUser: " ); |
169 | ahd_format_transinfo(m, tinfo: &tinfo->user); |
170 | starget = ahd->platform_data->starget[target_id]; |
171 | if (starget == NULL) |
172 | return; |
173 | |
174 | seq_puts(m, s: "\tGoal: " ); |
175 | ahd_format_transinfo(m, tinfo: &tinfo->goal); |
176 | seq_puts(m, s: "\tCurr: " ); |
177 | ahd_format_transinfo(m, tinfo: &tinfo->curr); |
178 | |
179 | for (lun = 0; lun < AHD_NUM_LUNS; lun++) { |
180 | struct scsi_device *dev; |
181 | |
182 | dev = scsi_device_lookup_by_target(starget, lun); |
183 | |
184 | if (dev == NULL) |
185 | continue; |
186 | |
187 | ahd_dump_device_state(m, sdev: dev); |
188 | } |
189 | } |
190 | |
191 | static void |
192 | ahd_dump_device_state(struct seq_file *m, struct scsi_device *sdev) |
193 | { |
194 | struct ahd_linux_device *dev = scsi_transport_device_data(sdev); |
195 | |
196 | seq_printf(m, fmt: "\tChannel %c Target %d Lun %d Settings\n" , |
197 | sdev->sdev_target->channel + 'A', |
198 | sdev->sdev_target->id, (u8)sdev->lun); |
199 | |
200 | seq_printf(m, fmt: "\t\tCommands Queued %ld\n" , dev->commands_issued); |
201 | seq_printf(m, fmt: "\t\tCommands Active %d\n" , dev->active); |
202 | seq_printf(m, fmt: "\t\tCommand Openings %d\n" , dev->openings); |
203 | seq_printf(m, fmt: "\t\tMax Tagged Openings %d\n" , dev->maxtags); |
204 | seq_printf(m, fmt: "\t\tDevice Queue Frozen Count %d\n" , dev->qfrozen); |
205 | } |
206 | |
207 | int |
208 | ahd_proc_write_seeprom(struct Scsi_Host *shost, char *buffer, int length) |
209 | { |
210 | struct ahd_softc *ahd = *(struct ahd_softc **)shost->hostdata; |
211 | ahd_mode_state saved_modes; |
212 | int have_seeprom; |
213 | u_long s; |
214 | int paused; |
215 | int written; |
216 | |
217 | /* Default to failure. */ |
218 | written = -EINVAL; |
219 | ahd_lock(ahd, flags: &s); |
220 | paused = ahd_is_paused(ahd); |
221 | if (!paused) |
222 | ahd_pause(ahd); |
223 | |
224 | saved_modes = ahd_save_modes(ahd); |
225 | ahd_set_modes(ahd, src: AHD_MODE_SCSI, dst: AHD_MODE_SCSI); |
226 | if (length != sizeof(struct seeprom_config)) { |
227 | printk("ahd_proc_write_seeprom: incorrect buffer size\n" ); |
228 | goto done; |
229 | } |
230 | |
231 | have_seeprom = ahd_verify_cksum(sc: (struct seeprom_config*)buffer); |
232 | if (have_seeprom == 0) { |
233 | printk("ahd_proc_write_seeprom: cksum verification failed\n" ); |
234 | goto done; |
235 | } |
236 | |
237 | have_seeprom = ahd_acquire_seeprom(ahd); |
238 | if (!have_seeprom) { |
239 | printk("ahd_proc_write_seeprom: No Serial EEPROM\n" ); |
240 | goto done; |
241 | } else { |
242 | u_int start_addr; |
243 | |
244 | if (ahd->seep_config == NULL) { |
245 | ahd->seep_config = kmalloc(size: sizeof(*ahd->seep_config), |
246 | GFP_ATOMIC); |
247 | if (ahd->seep_config == NULL) { |
248 | printk("aic79xx: Unable to allocate serial " |
249 | "eeprom buffer. Write failing\n" ); |
250 | goto done; |
251 | } |
252 | } |
253 | printk("aic79xx: Writing Serial EEPROM\n" ); |
254 | start_addr = 32 * (ahd->channel - 'A'); |
255 | ahd_write_seeprom(ahd, buf: (u_int16_t *)buffer, start_addr, |
256 | count: sizeof(struct seeprom_config)/2); |
257 | ahd_read_seeprom(ahd, buf: (uint16_t *)ahd->seep_config, |
258 | start_addr, count: sizeof(struct seeprom_config)/2, |
259 | /*ByteStream*/FALSE); |
260 | ahd_release_seeprom(ahd); |
261 | written = length; |
262 | } |
263 | |
264 | done: |
265 | ahd_restore_modes(ahd, state: saved_modes); |
266 | if (!paused) |
267 | ahd_unpause(ahd); |
268 | ahd_unlock(ahd, flags: &s); |
269 | return (written); |
270 | } |
271 | /* |
272 | * Return information to handle /proc support for the driver. |
273 | */ |
274 | int |
275 | ahd_linux_show_info(struct seq_file *m, struct Scsi_Host *shost) |
276 | { |
277 | struct ahd_softc *ahd = *(struct ahd_softc **)shost->hostdata; |
278 | char ahd_info[256]; |
279 | u_int max_targ; |
280 | u_int i; |
281 | |
282 | seq_printf(m, fmt: "Adaptec AIC79xx driver version: %s\n" , |
283 | AIC79XX_DRIVER_VERSION); |
284 | seq_printf(m, fmt: "%s\n" , ahd->description); |
285 | ahd_controller_info(ahd, buf: ahd_info); |
286 | seq_printf(m, fmt: "%s\n" , ahd_info); |
287 | seq_printf(m, fmt: "Allocated SCBs: %d, SG List Length: %d\n\n" , |
288 | ahd->scb_data.numscbs, AHD_NSEG); |
289 | |
290 | max_targ = 16; |
291 | |
292 | if (ahd->seep_config == NULL) |
293 | seq_puts(m, s: "No Serial EEPROM\n" ); |
294 | else { |
295 | seq_puts(m, s: "Serial EEPROM:\n" ); |
296 | for (i = 0; i < sizeof(*ahd->seep_config)/2; i++) { |
297 | if (((i % 8) == 0) && (i != 0)) { |
298 | seq_putc(m, c: '\n'); |
299 | } |
300 | seq_printf(m, fmt: "0x%.4x " , |
301 | ((uint16_t*)ahd->seep_config)[i]); |
302 | } |
303 | seq_putc(m, c: '\n'); |
304 | } |
305 | seq_putc(m, c: '\n'); |
306 | |
307 | if ((ahd->features & AHD_WIDE) == 0) |
308 | max_targ = 8; |
309 | |
310 | for (i = 0; i < max_targ; i++) { |
311 | |
312 | ahd_dump_target_state(ahd, m, our_id: ahd->our_id, channel: 'A', |
313 | /*target_id*/i); |
314 | } |
315 | return 0; |
316 | } |
317 | |