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 | unsigned long success = true; |
175 | ||
176 | BUG_ON(isci_phy->isci_port != NULL); | |
6cb4d6b3 | 177 | |
6f231dda DW |
178 | isci_phy->isci_port = isci_port; |
179 | ||
180 | dev_dbg(&isci_host->pdev->dev, | |
181 | "%s: isci_port = %p\n", | |
182 | __func__, isci_port); | |
183 | ||
184 | spin_lock_irqsave(&isci_phy->sas_phy.frame_rcvd_lock, flags); | |
185 | ||
186 | isci_port_change_state(isci_phy->isci_port, isci_starting); | |
187 | ||
188 | scic_port_get_properties(port, &properties); | |
189 | ||
d7b90fc3 | 190 | if (phy->protocol == SCIC_SDS_PHY_PROTOCOL_SATA) { |
150fc6fc | 191 | u64 attached_sas_address; |
6f231dda | 192 | |
6f231dda | 193 | isci_phy->sas_phy.oob_mode = SATA_OOB_MODE; |
f2f30080 | 194 | isci_phy->sas_phy.frame_rcvd_size = sizeof(struct dev_to_host_fis); |
6f231dda DW |
195 | |
196 | /* | |
197 | * For direct-attached SATA devices, the SCI core will | |
198 | * automagically assign a SAS address to the end device | |
199 | * for the purpose of creating a port. This SAS address | |
200 | * will not be the same as assigned to the PHY and needs | |
201 | * to be obtained from struct scic_port_properties properties. | |
202 | */ | |
150fc6fc DW |
203 | attached_sas_address = properties.remote.sas_address.high; |
204 | attached_sas_address <<= 32; | |
205 | attached_sas_address |= properties.remote.sas_address.low; | |
206 | swab64s(&attached_sas_address); | |
6f231dda | 207 | |
150fc6fc DW |
208 | memcpy(&isci_phy->sas_phy.attached_sas_addr, |
209 | &attached_sas_address, sizeof(attached_sas_address)); | |
d7b90fc3 | 210 | } else if (phy->protocol == SCIC_SDS_PHY_PROTOCOL_SAS) { |
6f231dda | 211 | isci_phy->sas_phy.oob_mode = SAS_OOB_MODE; |
4b7ebd05 | 212 | isci_phy->sas_phy.frame_rcvd_size = sizeof(struct sas_identify_frame); |
6f231dda DW |
213 | |
214 | /* Copy the attached SAS address from the IAF */ | |
215 | memcpy(isci_phy->sas_phy.attached_sas_addr, | |
4b7ebd05 | 216 | isci_phy->frame_rcvd.iaf.sas_addr, SAS_ADDR_SIZE); |
6f231dda DW |
217 | } else { |
218 | dev_err(&isci_host->pdev->dev, "%s: unkown target\n", __func__); | |
219 | success = false; | |
220 | } | |
221 | ||
83e51430 DW |
222 | isci_phy->sas_phy.phy->negotiated_linkrate = sci_phy_linkrate(phy); |
223 | ||
6f231dda DW |
224 | spin_unlock_irqrestore(&isci_phy->sas_phy.frame_rcvd_lock, flags); |
225 | ||
226 | /* Notify libsas that we have an address frame, if indeed | |
227 | * we've found an SSP, SMP, or STP target */ | |
228 | if (success) | |
229 | isci_host->sas_ha.notify_port_event(&isci_phy->sas_phy, | |
230 | PORTE_BYTES_DMAED); | |
231 | } | |
232 | ||
233 | ||
234 | /** | |
235 | * isci_port_link_down() - This function is called by the sci core when a link | |
236 | * becomes inactive. | |
237 | * @isci_host: This parameter specifies the isci host object. | |
238 | * @phy: This parameter specifies the isci phy with the active link. | |
239 | * @port: This parameter specifies the isci port with the active link. | |
240 | * | |
241 | */ | |
09d7da13 DJ |
242 | void isci_port_link_down(struct isci_host *isci_host, struct isci_phy *isci_phy, |
243 | struct isci_port *isci_port) | |
6f231dda DW |
244 | { |
245 | struct isci_remote_device *isci_device; | |
246 | ||
247 | dev_dbg(&isci_host->pdev->dev, | |
248 | "%s: isci_port = %p\n", __func__, isci_port); | |
249 | ||
250 | if (isci_port) { | |
251 | ||
252 | /* check to see if this is the last phy on this port. */ | |
253 | if (isci_phy->sas_phy.port | |
254 | && isci_phy->sas_phy.port->num_phys == 1) { | |
255 | ||
256 | /* change the state for all devices on this port. | |
257 | * The next task sent to this device will be returned | |
258 | * as SAS_TASK_UNDELIVERED, and the scsi mid layer | |
259 | * will remove the target | |
260 | */ | |
261 | list_for_each_entry(isci_device, | |
262 | &isci_port->remote_dev_list, | |
263 | node) { | |
264 | dev_dbg(&isci_host->pdev->dev, | |
265 | "%s: isci_device = %p\n", | |
266 | __func__, isci_device); | |
267 | isci_remote_device_change_state(isci_device, | |
268 | isci_stopping); | |
269 | } | |
270 | } | |
271 | isci_port_change_state(isci_port, isci_stopping); | |
272 | } | |
273 | ||
274 | /* Notify libsas of the borken link, this will trigger calls to our | |
275 | * isci_port_deformed and isci_dev_gone functions. | |
276 | */ | |
277 | sas_phy_disconnected(&isci_phy->sas_phy); | |
278 | isci_host->sas_ha.notify_phy_event(&isci_phy->sas_phy, | |
279 | PHYE_LOSS_OF_SIGNAL); | |
280 | ||
281 | isci_phy->isci_port = NULL; | |
282 | ||
283 | dev_dbg(&isci_host->pdev->dev, | |
284 | "%s: isci_port = %p - Done\n", __func__, isci_port); | |
285 | } | |
286 | ||
287 | ||
288 | /** | |
289 | * isci_port_deformed() - This function is called by libsas when a port becomes | |
290 | * inactive. | |
291 | * @phy: This parameter specifies the libsas phy with the inactive port. | |
292 | * | |
293 | */ | |
294 | void isci_port_deformed( | |
295 | struct asd_sas_phy *phy) | |
296 | { | |
297 | pr_debug("%s: sas_phy = %p\n", __func__, phy); | |
298 | } | |
299 | ||
300 | /** | |
301 | * isci_port_formed() - This function is called by libsas when a port becomes | |
302 | * active. | |
303 | * @phy: This parameter specifies the libsas phy with the active port. | |
304 | * | |
305 | */ | |
306 | void isci_port_formed( | |
307 | struct asd_sas_phy *phy) | |
308 | { | |
309 | pr_debug("%s: sas_phy = %p, sas_port = %p\n", __func__, phy, phy->port); | |
310 | } | |
311 | ||
312 | /** | |
313 | * isci_port_ready() - This function is called by the sci core when a link | |
314 | * becomes ready. | |
315 | * @isci_host: This parameter specifies the isci host object. | |
316 | * @port: This parameter specifies the sci port with the active link. | |
317 | * | |
318 | */ | |
09d7da13 | 319 | void isci_port_ready(struct isci_host *isci_host, struct isci_port *isci_port) |
6f231dda DW |
320 | { |
321 | dev_dbg(&isci_host->pdev->dev, | |
322 | "%s: isci_port = %p\n", __func__, isci_port); | |
323 | ||
324 | complete_all(&isci_port->start_complete); | |
325 | isci_port_change_state(isci_port, isci_ready); | |
326 | return; | |
327 | } | |
328 | ||
329 | /** | |
330 | * isci_port_not_ready() - This function is called by the sci core when a link | |
331 | * is not ready. All remote devices on this link will be removed if they are | |
332 | * in the stopping state. | |
333 | * @isci_host: This parameter specifies the isci host object. | |
334 | * @port: This parameter specifies the sci port with the active link. | |
335 | * | |
336 | */ | |
09d7da13 | 337 | void isci_port_not_ready(struct isci_host *isci_host, struct isci_port *isci_port) |
6f231dda DW |
338 | { |
339 | dev_dbg(&isci_host->pdev->dev, | |
340 | "%s: isci_port = %p\n", __func__, isci_port); | |
341 | } | |
342 | ||
343 | /** | |
344 | * isci_port_hard_reset_complete() - This function is called by the sci core | |
345 | * when the hard reset complete notification has been received. | |
346 | * @port: This parameter specifies the sci port with the active link. | |
347 | * @completion_status: This parameter specifies the core status for the reset | |
348 | * process. | |
349 | * | |
350 | */ | |
09d7da13 DJ |
351 | void isci_port_hard_reset_complete(struct isci_port *isci_port, |
352 | enum sci_status completion_status) | |
6f231dda DW |
353 | { |
354 | dev_dbg(&isci_port->isci_host->pdev->dev, | |
355 | "%s: isci_port = %p, completion_status=%x\n", | |
356 | __func__, isci_port, completion_status); | |
357 | ||
358 | /* Save the status of the hard reset from the port. */ | |
359 | isci_port->hard_reset_status = completion_status; | |
360 | ||
361 | complete_all(&isci_port->hard_reset_complete); | |
362 | } | |
4393aa4e DW |
363 | |
364 | int isci_port_perform_hard_reset(struct isci_host *ihost, struct isci_port *iport, | |
365 | struct isci_phy *iphy) | |
6f231dda | 366 | { |
4393aa4e | 367 | unsigned long flags; |
6f231dda DW |
368 | enum sci_status status; |
369 | int ret = TMF_RESP_FUNC_COMPLETE; | |
6f231dda | 370 | |
4393aa4e DW |
371 | dev_dbg(&ihost->pdev->dev, "%s: iport = %p\n", |
372 | __func__, iport); | |
6f231dda | 373 | |
4393aa4e | 374 | init_completion(&iport->hard_reset_complete); |
6f231dda | 375 | |
4393aa4e | 376 | spin_lock_irqsave(&ihost->scic_lock, flags); |
6f231dda DW |
377 | |
378 | #define ISCI_PORT_RESET_TIMEOUT SCIC_SDS_SIGNATURE_FIS_TIMEOUT | |
4393aa4e | 379 | status = scic_port_hard_reset(iport->sci_port_handle, |
6f231dda DW |
380 | ISCI_PORT_RESET_TIMEOUT); |
381 | ||
4393aa4e | 382 | spin_unlock_irqrestore(&ihost->scic_lock, flags); |
6f231dda DW |
383 | |
384 | if (status == SCI_SUCCESS) { | |
4393aa4e | 385 | wait_for_completion(&iport->hard_reset_complete); |
6f231dda | 386 | |
4393aa4e DW |
387 | dev_dbg(&ihost->pdev->dev, |
388 | "%s: iport = %p; hard reset completion\n", | |
389 | __func__, iport); | |
6f231dda | 390 | |
4393aa4e | 391 | if (iport->hard_reset_status != SCI_SUCCESS) |
6f231dda DW |
392 | ret = TMF_RESP_FUNC_FAILED; |
393 | } else { | |
394 | ret = TMF_RESP_FUNC_FAILED; | |
395 | ||
4393aa4e DW |
396 | dev_err(&ihost->pdev->dev, |
397 | "%s: iport = %p; scic_port_hard_reset call" | |
6f231dda | 398 | " failed 0x%x\n", |
4393aa4e | 399 | __func__, iport, status); |
6f231dda DW |
400 | |
401 | } | |
402 | ||
403 | /* If the hard reset for the port has failed, consider this | |
404 | * the same as link failures on all phys in the port. | |
405 | */ | |
406 | if (ret != TMF_RESP_FUNC_COMPLETE) { | |
4393aa4e DW |
407 | dev_err(&ihost->pdev->dev, |
408 | "%s: iport = %p; hard reset failed " | |
6f231dda | 409 | "(0x%x) - sending link down to libsas for phy %p\n", |
4393aa4e DW |
410 | __func__, iport, iport->hard_reset_status, iphy); |
411 | ||
412 | isci_port_link_down(ihost, iphy, iport); | |
6f231dda DW |
413 | } |
414 | ||
415 | return ret; | |
416 | } | |
09d7da13 DJ |
417 | |
418 | /** | |
419 | * isci_port_invalid_link_up() - This function informs the SCI Core user that | |
420 | * a phy/link became ready, but the phy is not allowed in the port. In some | |
421 | * situations the underlying hardware only allows for certain phy to port | |
422 | * mappings. If these mappings are violated, then this API is invoked. | |
423 | * @controller: This parameter represents the controller which contains the | |
424 | * port. | |
425 | * @port: This parameter specifies the SCI port object for which the callback | |
426 | * is being invoked. | |
427 | * @phy: This parameter specifies the phy that came ready, but the phy can't be | |
428 | * a valid member of the port. | |
429 | * | |
430 | */ | |
431 | void isci_port_invalid_link_up(struct scic_sds_controller *scic, | |
432 | struct scic_sds_port *sci_port, | |
433 | struct scic_sds_phy *phy) | |
434 | { | |
d3757c3a | 435 | struct isci_host *ihost = scic->ihost; |
09d7da13 DJ |
436 | |
437 | dev_warn(&ihost->pdev->dev, "Invalid link up!\n"); | |
438 | } | |
439 | ||
440 | void isci_port_stop_complete(struct scic_sds_controller *scic, | |
441 | struct scic_sds_port *sci_port, | |
442 | enum sci_status completion_status) | |
443 | { | |
d3757c3a | 444 | struct isci_host *ihost = scic->ihost; |
09d7da13 DJ |
445 | |
446 | dev_dbg(&ihost->pdev->dev, "Port stop complete\n"); | |
447 | } |