Commit | Line | Data |
---|---|---|
6f231dda DW |
1 | /* |
2 | * This file is provided under a dual BSD/GPLv2 license. When using or | |
3 | * redistributing this file, you may do so under either license. | |
4 | * | |
5 | * GPL LICENSE SUMMARY | |
6 | * | |
7 | * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of version 2 of the GNU General Public License as | |
11 | * published by the Free Software Foundation. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, but | |
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | * General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program; if not, write to the Free Software | |
20 | * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | |
21 | * The full GNU General Public License is included in this distribution | |
22 | * in the file called LICENSE.GPL. | |
23 | * | |
24 | * BSD LICENSE | |
25 | * | |
26 | * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. | |
27 | * All rights reserved. | |
28 | * | |
29 | * Redistribution and use in source and binary forms, with or without | |
30 | * modification, are permitted provided that the following conditions | |
31 | * are met: | |
32 | * | |
33 | * * Redistributions of source code must retain the above copyright | |
34 | * notice, this list of conditions and the following disclaimer. | |
35 | * * Redistributions in binary form must reproduce the above copyright | |
36 | * notice, this list of conditions and the following disclaimer in | |
37 | * the documentation and/or other materials provided with the | |
38 | * distribution. | |
39 | * * Neither the name of Intel Corporation nor the names of its | |
40 | * contributors may be used to endorse or promote products derived | |
41 | * from this software without specific prior written permission. | |
42 | * | |
43 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
44 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
45 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
46 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
47 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
48 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
49 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
50 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
51 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
52 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
53 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
54 | */ | |
55 | ||
56 | /** | |
57 | * This file contains the isci port implementation. | |
58 | * | |
59 | * | |
60 | */ | |
61 | ||
62 | ||
63 | #include <linux/workqueue.h> | |
64 | #include "isci.h" | |
65 | #include "scic_io_request.h" | |
6f231dda DW |
66 | #include "scic_phy.h" |
67 | #include "scic_sds_phy.h" | |
68 | #include "scic_port.h" | |
69 | #include "port.h" | |
70 | #include "request.h" | |
d3757c3a | 71 | #include "core/scic_sds_controller.h" |
6f231dda DW |
72 | |
73 | static void isci_port_change_state( | |
74 | struct isci_port *isci_port, | |
75 | enum isci_status status); | |
76 | ||
77 | ||
78 | ||
79 | /** | |
80 | * isci_port_init() - This function initializes the given isci_port object. | |
81 | * @isci_port: This parameter specifies the port object to be initialized. | |
82 | * @isci_host: This parameter specifies parent controller object for the port. | |
83 | * @index: This parameter specifies which SCU port the isci_port associates | |
84 | * with. Generally, SCU port 0 relates to isci_port 0, etc. | |
85 | * | |
86 | */ | |
87 | void isci_port_init( | |
88 | struct isci_port *isci_port, | |
89 | struct isci_host *isci_host, | |
90 | int index) | |
91 | { | |
92 | struct scic_sds_port *scic_port; | |
93 | struct scic_sds_controller *controller = isci_host->core_controller; | |
94 | ||
95 | INIT_LIST_HEAD(&isci_port->remote_dev_list); | |
96 | INIT_LIST_HEAD(&isci_port->domain_dev_list); | |
6f231dda DW |
97 | spin_lock_init(&isci_port->state_lock); |
98 | init_completion(&isci_port->start_complete); | |
99 | isci_port->isci_host = isci_host; | |
100 | isci_port_change_state(isci_port, isci_freed); | |
101 | ||
102 | (void)scic_controller_get_port_handle(controller, index, &scic_port); | |
6f231dda | 103 | isci_port->sci_port_handle = scic_port; |
115bd1f9 | 104 | scic_port->iport = isci_port; |
6f231dda DW |
105 | } |
106 | ||
107 | ||
108 | /** | |
109 | * isci_port_get_state() - This function gets the status of the port object. | |
110 | * @isci_port: This parameter points to the isci_port object | |
111 | * | |
112 | * status of the object as a isci_status enum. | |
113 | */ | |
114 | enum isci_status isci_port_get_state( | |
115 | struct isci_port *isci_port) | |
116 | { | |
117 | return isci_port->status; | |
118 | } | |
119 | ||
120 | static void isci_port_change_state( | |
121 | struct isci_port *isci_port, | |
122 | enum isci_status status) | |
123 | { | |
124 | unsigned long flags; | |
125 | ||
126 | dev_dbg(&isci_port->isci_host->pdev->dev, | |
127 | "%s: isci_port = %p, state = 0x%x\n", | |
128 | __func__, isci_port, status); | |
129 | ||
130 | spin_lock_irqsave(&isci_port->state_lock, flags); | |
131 | isci_port->status = status; | |
132 | spin_unlock_irqrestore(&isci_port->state_lock, flags); | |
133 | } | |
134 | ||
135 | void isci_port_bc_change_received( | |
136 | struct isci_host *isci_host, | |
137 | struct scic_sds_port *port, | |
138 | struct scic_sds_phy *phy) | |
139 | { | |
e1e72a00 | 140 | struct isci_phy *isci_phy = phy->iphy; |
6f231dda DW |
141 | |
142 | dev_dbg(&isci_host->pdev->dev, | |
143 | "%s: isci_phy = %p, sas_phy = %p\n", | |
144 | __func__, | |
145 | isci_phy, | |
146 | &isci_phy->sas_phy); | |
147 | ||
148 | isci_host->sas_ha.notify_port_event( | |
149 | &isci_phy->sas_phy, | |
150 | PORTE_BROADCAST_RCVD | |
151 | ); | |
152 | ||
153 | scic_port_enable_broadcast_change_notification(port); | |
154 | } | |
155 | ||
156 | /** | |
157 | * isci_port_link_up() - This function is called by the sci core when a link | |
158 | * becomes active. the identify address frame is retrieved from the core and | |
159 | * a notify port event is sent to libsas. | |
160 | * @isci_host: This parameter specifies the isci host object. | |
161 | * @port: This parameter specifies the sci port with the active link. | |
162 | * @phy: This parameter specifies the sci phy with the active link. | |
163 | * | |
164 | */ | |
165 | void isci_port_link_up( | |
166 | struct isci_host *isci_host, | |
167 | struct scic_sds_port *port, | |
168 | struct scic_sds_phy *phy) | |
169 | { | |
170 | unsigned long flags; | |
171 | struct scic_port_properties properties; | |
e1e72a00 | 172 | struct isci_phy *isci_phy = phy->iphy; |
115bd1f9 | 173 | struct isci_port *isci_port = port->iport; |
6f231dda DW |
174 | enum sci_status call_status; |
175 | unsigned long success = true; | |
176 | ||
177 | BUG_ON(isci_phy->isci_port != NULL); | |
6cb4d6b3 | 178 | |
6f231dda DW |
179 | isci_phy->isci_port = isci_port; |
180 | ||
181 | dev_dbg(&isci_host->pdev->dev, | |
182 | "%s: isci_port = %p\n", | |
183 | __func__, isci_port); | |
184 | ||
185 | spin_lock_irqsave(&isci_phy->sas_phy.frame_rcvd_lock, flags); | |
186 | ||
187 | isci_port_change_state(isci_phy->isci_port, isci_starting); | |
188 | ||
189 | scic_port_get_properties(port, &properties); | |
190 | ||
191 | if (properties.remote.protocols.u.bits.stp_target) { | |
150fc6fc | 192 | u64 attached_sas_address; |
6f231dda DW |
193 | |
194 | struct scic_sata_phy_properties sata_phy_properties; | |
195 | ||
196 | isci_phy->sas_phy.oob_mode = SATA_OOB_MODE; | |
197 | ||
198 | /* Get a copy of the signature fis for libsas */ | |
199 | call_status = scic_sata_phy_get_properties(phy, | |
200 | &sata_phy_properties); | |
201 | ||
35173d57 | 202 | /* |
6f231dda DW |
203 | * XXX I am concerned about this "assert". shouldn't we |
204 | * handle the return appropriately? | |
205 | */ | |
206 | BUG_ON(call_status != SCI_SUCCESS); | |
207 | ||
f2f30080 DJ |
208 | isci_phy->frame_rcvd.fis = sata_phy_properties.signature_fis; |
209 | isci_phy->sas_phy.frame_rcvd_size = sizeof(struct dev_to_host_fis); | |
6f231dda DW |
210 | |
211 | /* | |
212 | * For direct-attached SATA devices, the SCI core will | |
213 | * automagically assign a SAS address to the end device | |
214 | * for the purpose of creating a port. This SAS address | |
215 | * will not be the same as assigned to the PHY and needs | |
216 | * to be obtained from struct scic_port_properties properties. | |
217 | */ | |
150fc6fc DW |
218 | attached_sas_address = properties.remote.sas_address.high; |
219 | attached_sas_address <<= 32; | |
220 | attached_sas_address |= properties.remote.sas_address.low; | |
221 | swab64s(&attached_sas_address); | |
6f231dda | 222 | |
150fc6fc DW |
223 | memcpy(&isci_phy->sas_phy.attached_sas_addr, |
224 | &attached_sas_address, sizeof(attached_sas_address)); | |
6f231dda DW |
225 | |
226 | } else if (properties.remote.protocols.u.bits.ssp_target || | |
227 | properties.remote.protocols.u.bits.smp_target) { | |
228 | ||
229 | struct scic_sas_phy_properties sas_phy_properties; | |
230 | ||
231 | isci_phy->sas_phy.oob_mode = SAS_OOB_MODE; | |
232 | ||
233 | /* Get a copy of the identify address frame for libsas */ | |
234 | call_status = scic_sas_phy_get_properties(phy, | |
235 | &sas_phy_properties); | |
236 | ||
237 | BUG_ON(call_status != SCI_SUCCESS); | |
238 | ||
4b7ebd05 DJ |
239 | isci_phy->frame_rcvd.iaf = sas_phy_properties.rcvd_iaf; |
240 | isci_phy->sas_phy.frame_rcvd_size = sizeof(struct sas_identify_frame); | |
6f231dda DW |
241 | |
242 | /* Copy the attached SAS address from the IAF */ | |
243 | memcpy(isci_phy->sas_phy.attached_sas_addr, | |
4b7ebd05 | 244 | isci_phy->frame_rcvd.iaf.sas_addr, SAS_ADDR_SIZE); |
6f231dda DW |
245 | |
246 | } else { | |
247 | dev_err(&isci_host->pdev->dev, "%s: unkown target\n", __func__); | |
248 | success = false; | |
249 | } | |
250 | ||
83e51430 DW |
251 | isci_phy->sas_phy.phy->negotiated_linkrate = sci_phy_linkrate(phy); |
252 | ||
6f231dda DW |
253 | spin_unlock_irqrestore(&isci_phy->sas_phy.frame_rcvd_lock, flags); |
254 | ||
255 | /* Notify libsas that we have an address frame, if indeed | |
256 | * we've found an SSP, SMP, or STP target */ | |
257 | if (success) | |
258 | isci_host->sas_ha.notify_port_event(&isci_phy->sas_phy, | |
259 | PORTE_BYTES_DMAED); | |
260 | } | |
261 | ||
262 | ||
263 | /** | |
264 | * isci_port_link_down() - This function is called by the sci core when a link | |
265 | * becomes inactive. | |
266 | * @isci_host: This parameter specifies the isci host object. | |
267 | * @phy: This parameter specifies the isci phy with the active link. | |
268 | * @port: This parameter specifies the isci port with the active link. | |
269 | * | |
270 | */ | |
09d7da13 DJ |
271 | void isci_port_link_down(struct isci_host *isci_host, struct isci_phy *isci_phy, |
272 | struct isci_port *isci_port) | |
6f231dda DW |
273 | { |
274 | struct isci_remote_device *isci_device; | |
275 | ||
276 | dev_dbg(&isci_host->pdev->dev, | |
277 | "%s: isci_port = %p\n", __func__, isci_port); | |
278 | ||
279 | if (isci_port) { | |
280 | ||
281 | /* check to see if this is the last phy on this port. */ | |
282 | if (isci_phy->sas_phy.port | |
283 | && isci_phy->sas_phy.port->num_phys == 1) { | |
284 | ||
285 | /* change the state for all devices on this port. | |
286 | * The next task sent to this device will be returned | |
287 | * as SAS_TASK_UNDELIVERED, and the scsi mid layer | |
288 | * will remove the target | |
289 | */ | |
290 | list_for_each_entry(isci_device, | |
291 | &isci_port->remote_dev_list, | |
292 | node) { | |
293 | dev_dbg(&isci_host->pdev->dev, | |
294 | "%s: isci_device = %p\n", | |
295 | __func__, isci_device); | |
296 | isci_remote_device_change_state(isci_device, | |
297 | isci_stopping); | |
298 | } | |
299 | } | |
300 | isci_port_change_state(isci_port, isci_stopping); | |
301 | } | |
302 | ||
303 | /* Notify libsas of the borken link, this will trigger calls to our | |
304 | * isci_port_deformed and isci_dev_gone functions. | |
305 | */ | |
306 | sas_phy_disconnected(&isci_phy->sas_phy); | |
307 | isci_host->sas_ha.notify_phy_event(&isci_phy->sas_phy, | |
308 | PHYE_LOSS_OF_SIGNAL); | |
309 | ||
310 | isci_phy->isci_port = NULL; | |
311 | ||
312 | dev_dbg(&isci_host->pdev->dev, | |
313 | "%s: isci_port = %p - Done\n", __func__, isci_port); | |
314 | } | |
315 | ||
316 | ||
317 | /** | |
318 | * isci_port_deformed() - This function is called by libsas when a port becomes | |
319 | * inactive. | |
320 | * @phy: This parameter specifies the libsas phy with the inactive port. | |
321 | * | |
322 | */ | |
323 | void isci_port_deformed( | |
324 | struct asd_sas_phy *phy) | |
325 | { | |
326 | pr_debug("%s: sas_phy = %p\n", __func__, phy); | |
327 | } | |
328 | ||
329 | /** | |
330 | * isci_port_formed() - This function is called by libsas when a port becomes | |
331 | * active. | |
332 | * @phy: This parameter specifies the libsas phy with the active port. | |
333 | * | |
334 | */ | |
335 | void isci_port_formed( | |
336 | struct asd_sas_phy *phy) | |
337 | { | |
338 | pr_debug("%s: sas_phy = %p, sas_port = %p\n", __func__, phy, phy->port); | |
339 | } | |
340 | ||
341 | /** | |
342 | * isci_port_ready() - This function is called by the sci core when a link | |
343 | * becomes ready. | |
344 | * @isci_host: This parameter specifies the isci host object. | |
345 | * @port: This parameter specifies the sci port with the active link. | |
346 | * | |
347 | */ | |
09d7da13 | 348 | void isci_port_ready(struct isci_host *isci_host, struct isci_port *isci_port) |
6f231dda DW |
349 | { |
350 | dev_dbg(&isci_host->pdev->dev, | |
351 | "%s: isci_port = %p\n", __func__, isci_port); | |
352 | ||
353 | complete_all(&isci_port->start_complete); | |
354 | isci_port_change_state(isci_port, isci_ready); | |
355 | return; | |
356 | } | |
357 | ||
358 | /** | |
359 | * isci_port_not_ready() - This function is called by the sci core when a link | |
360 | * is not ready. All remote devices on this link will be removed if they are | |
361 | * in the stopping state. | |
362 | * @isci_host: This parameter specifies the isci host object. | |
363 | * @port: This parameter specifies the sci port with the active link. | |
364 | * | |
365 | */ | |
09d7da13 | 366 | void isci_port_not_ready(struct isci_host *isci_host, struct isci_port *isci_port) |
6f231dda DW |
367 | { |
368 | dev_dbg(&isci_host->pdev->dev, | |
369 | "%s: isci_port = %p\n", __func__, isci_port); | |
370 | } | |
371 | ||
372 | /** | |
373 | * isci_port_hard_reset_complete() - This function is called by the sci core | |
374 | * when the hard reset complete notification has been received. | |
375 | * @port: This parameter specifies the sci port with the active link. | |
376 | * @completion_status: This parameter specifies the core status for the reset | |
377 | * process. | |
378 | * | |
379 | */ | |
09d7da13 DJ |
380 | void isci_port_hard_reset_complete(struct isci_port *isci_port, |
381 | enum sci_status completion_status) | |
6f231dda DW |
382 | { |
383 | dev_dbg(&isci_port->isci_host->pdev->dev, | |
384 | "%s: isci_port = %p, completion_status=%x\n", | |
385 | __func__, isci_port, completion_status); | |
386 | ||
387 | /* Save the status of the hard reset from the port. */ | |
388 | isci_port->hard_reset_status = completion_status; | |
389 | ||
390 | complete_all(&isci_port->hard_reset_complete); | |
391 | } | |
4393aa4e DW |
392 | |
393 | int isci_port_perform_hard_reset(struct isci_host *ihost, struct isci_port *iport, | |
394 | struct isci_phy *iphy) | |
6f231dda | 395 | { |
4393aa4e | 396 | unsigned long flags; |
6f231dda DW |
397 | enum sci_status status; |
398 | int ret = TMF_RESP_FUNC_COMPLETE; | |
6f231dda | 399 | |
4393aa4e DW |
400 | dev_dbg(&ihost->pdev->dev, "%s: iport = %p\n", |
401 | __func__, iport); | |
6f231dda | 402 | |
4393aa4e | 403 | init_completion(&iport->hard_reset_complete); |
6f231dda | 404 | |
4393aa4e | 405 | spin_lock_irqsave(&ihost->scic_lock, flags); |
6f231dda DW |
406 | |
407 | #define ISCI_PORT_RESET_TIMEOUT SCIC_SDS_SIGNATURE_FIS_TIMEOUT | |
4393aa4e | 408 | status = scic_port_hard_reset(iport->sci_port_handle, |
6f231dda DW |
409 | ISCI_PORT_RESET_TIMEOUT); |
410 | ||
4393aa4e | 411 | spin_unlock_irqrestore(&ihost->scic_lock, flags); |
6f231dda DW |
412 | |
413 | if (status == SCI_SUCCESS) { | |
4393aa4e | 414 | wait_for_completion(&iport->hard_reset_complete); |
6f231dda | 415 | |
4393aa4e DW |
416 | dev_dbg(&ihost->pdev->dev, |
417 | "%s: iport = %p; hard reset completion\n", | |
418 | __func__, iport); | |
6f231dda | 419 | |
4393aa4e | 420 | if (iport->hard_reset_status != SCI_SUCCESS) |
6f231dda DW |
421 | ret = TMF_RESP_FUNC_FAILED; |
422 | } else { | |
423 | ret = TMF_RESP_FUNC_FAILED; | |
424 | ||
4393aa4e DW |
425 | dev_err(&ihost->pdev->dev, |
426 | "%s: iport = %p; scic_port_hard_reset call" | |
6f231dda | 427 | " failed 0x%x\n", |
4393aa4e | 428 | __func__, iport, status); |
6f231dda DW |
429 | |
430 | } | |
431 | ||
432 | /* If the hard reset for the port has failed, consider this | |
433 | * the same as link failures on all phys in the port. | |
434 | */ | |
435 | if (ret != TMF_RESP_FUNC_COMPLETE) { | |
4393aa4e DW |
436 | dev_err(&ihost->pdev->dev, |
437 | "%s: iport = %p; hard reset failed " | |
6f231dda | 438 | "(0x%x) - sending link down to libsas for phy %p\n", |
4393aa4e DW |
439 | __func__, iport, iport->hard_reset_status, iphy); |
440 | ||
441 | isci_port_link_down(ihost, iphy, iport); | |
6f231dda DW |
442 | } |
443 | ||
444 | return ret; | |
445 | } | |
09d7da13 DJ |
446 | |
447 | /** | |
448 | * isci_port_invalid_link_up() - This function informs the SCI Core user that | |
449 | * a phy/link became ready, but the phy is not allowed in the port. In some | |
450 | * situations the underlying hardware only allows for certain phy to port | |
451 | * mappings. If these mappings are violated, then this API is invoked. | |
452 | * @controller: This parameter represents the controller which contains the | |
453 | * port. | |
454 | * @port: This parameter specifies the SCI port object for which the callback | |
455 | * is being invoked. | |
456 | * @phy: This parameter specifies the phy that came ready, but the phy can't be | |
457 | * a valid member of the port. | |
458 | * | |
459 | */ | |
460 | void isci_port_invalid_link_up(struct scic_sds_controller *scic, | |
461 | struct scic_sds_port *sci_port, | |
462 | struct scic_sds_phy *phy) | |
463 | { | |
d3757c3a | 464 | struct isci_host *ihost = scic->ihost; |
09d7da13 DJ |
465 | |
466 | dev_warn(&ihost->pdev->dev, "Invalid link up!\n"); | |
467 | } | |
468 | ||
469 | void isci_port_stop_complete(struct scic_sds_controller *scic, | |
470 | struct scic_sds_port *sci_port, | |
471 | enum sci_status completion_status) | |
472 | { | |
d3757c3a | 473 | struct isci_host *ihost = scic->ihost; |
09d7da13 DJ |
474 | |
475 | dev_dbg(&ihost->pdev->dev, "Port stop complete\n"); | |
476 | } |