Commit | Line | Data |
---|---|---|
86b89cb0 | 1 | // SPDX-License-Identifier: GPL-2.0 |
2908d778 JB |
2 | /* |
3 | * Serial Attached SCSI (SAS) Expander discovery and configuration | |
4 | * | |
5 | * Copyright (C) 2005 Adaptec, Inc. All rights reserved. | |
6 | * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com> | |
7 | * | |
8 | * This file is licensed under GPLv2. | |
2908d778 JB |
9 | */ |
10 | ||
2908d778 | 11 | #include <linux/scatterlist.h> |
ba1fc175 | 12 | #include <linux/blkdev.h> |
5a0e3ad6 | 13 | #include <linux/slab.h> |
59abc8cc | 14 | #include <asm/unaligned.h> |
2908d778 JB |
15 | |
16 | #include "sas_internal.h" | |
17 | ||
b52df417 | 18 | #include <scsi/sas_ata.h> |
2908d778 JB |
19 | #include <scsi/scsi_transport.h> |
20 | #include <scsi/scsi_transport_sas.h> | |
e15f669c | 21 | #include "scsi_sas_internal.h" |
2908d778 JB |
22 | |
23 | static int sas_discover_expander(struct domain_device *dev); | |
24 | static int sas_configure_routing(struct domain_device *dev, u8 *sas_addr); | |
25 | static int sas_configure_phy(struct domain_device *dev, int phy_id, | |
26 | u8 *sas_addr, int include); | |
27 | static int sas_disable_routing(struct domain_device *dev, u8 *sas_addr); | |
28 | ||
888ea1b1 XY |
29 | static void sas_port_add_ex_phy(struct sas_port *port, struct ex_phy *ex_phy) |
30 | { | |
31 | sas_port_add_phy(port, ex_phy->phy); | |
32 | ex_phy->port = port; | |
33 | ex_phy->phy_state = PHY_DEVICE_DISCOVERED; | |
34 | } | |
35 | ||
48032c0b XY |
36 | static void sas_ex_add_parent_port(struct domain_device *dev, int phy_id) |
37 | { | |
38 | struct expander_device *ex = &dev->ex_dev; | |
39 | struct ex_phy *ex_phy = &ex->ex_phy[phy_id]; | |
40 | ||
41 | if (!ex->parent_port) { | |
42 | ex->parent_port = sas_port_alloc(&dev->rphy->dev, phy_id); | |
43 | /* FIXME: error handling */ | |
44 | BUG_ON(!ex->parent_port); | |
45 | BUG_ON(sas_port_add(ex->parent_port)); | |
46 | sas_port_mark_backlink(ex->parent_port); | |
47 | } | |
7a165a81 | 48 | sas_port_add_ex_phy(ex->parent_port, ex_phy); |
48032c0b XY |
49 | } |
50 | ||
2908d778 JB |
51 | /* ---------- SMP task management ---------- */ |
52 | ||
2908d778 JB |
53 | /* Give it some long enough timeout. In seconds. */ |
54 | #define SMP_TIMEOUT 10 | |
55 | ||
651a0136 CH |
56 | static int smp_execute_task_sg(struct domain_device *dev, |
57 | struct scatterlist *req, struct scatterlist *resp) | |
2908d778 | 58 | { |
42961ee8 | 59 | int res, retry; |
60 | struct sas_task *task = NULL; | |
2908d778 | 61 | struct sas_internal *i = |
1136a022 | 62 | to_sas_internal(dev->port->ha->shost->transportt); |
0da7ca4c | 63 | struct sas_ha_struct *ha = dev->port->ha; |
2908d778 | 64 | |
0da7ca4c | 65 | pm_runtime_get_sync(ha->dev); |
89d3cf6a | 66 | mutex_lock(&dev->ex_dev.cmd_mutex); |
42961ee8 | 67 | for (retry = 0; retry < 3; retry++) { |
3a9c5560 DW |
68 | if (test_bit(SAS_DEV_GONE, &dev->state)) { |
69 | res = -ECOMM; | |
70 | break; | |
71 | } | |
72 | ||
f0bf750c | 73 | task = sas_alloc_slow_task(GFP_KERNEL); |
89d3cf6a JS |
74 | if (!task) { |
75 | res = -ENOMEM; | |
76 | break; | |
77 | } | |
42961ee8 | 78 | task->dev = dev; |
79 | task->task_proto = dev->tproto; | |
651a0136 CH |
80 | task->smp_task.smp_req = *req; |
81 | task->smp_task.smp_resp = *resp; | |
2908d778 | 82 | |
4aef43b2 | 83 | task->task_done = sas_task_internal_done; |
2908d778 | 84 | |
4aef43b2 | 85 | task->slow_task->timer.function = sas_task_internal_timedout; |
f0bf750c DW |
86 | task->slow_task->timer.expires = jiffies + SMP_TIMEOUT*HZ; |
87 | add_timer(&task->slow_task->timer); | |
2908d778 | 88 | |
79855d17 | 89 | res = i->dft->lldd_execute_task(task, GFP_KERNEL); |
2908d778 | 90 | |
42961ee8 | 91 | if (res) { |
46ba53c3 | 92 | del_timer_sync(&task->slow_task->timer); |
15ba7806 | 93 | pr_notice("executing SMP task failed:%d\n", res); |
89d3cf6a | 94 | break; |
2908d778 | 95 | } |
42961ee8 | 96 | |
f0bf750c | 97 | wait_for_completion(&task->slow_task->completion); |
32e8ae36 | 98 | res = -ECOMM; |
42961ee8 | 99 | if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) { |
15ba7806 | 100 | pr_notice("smp task timed out or aborted\n"); |
42961ee8 | 101 | i->dft->lldd_abort_task(task); |
102 | if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) { | |
15ba7806 | 103 | pr_notice("SMP task aborted and not done\n"); |
89d3cf6a | 104 | break; |
42961ee8 | 105 | } |
106 | } | |
107 | if (task->task_status.resp == SAS_TASK_COMPLETE && | |
d377f415 | 108 | task->task_status.stat == SAS_SAM_STAT_GOOD) { |
42961ee8 | 109 | res = 0; |
110 | break; | |
89d3cf6a JS |
111 | } |
112 | if (task->task_status.resp == SAS_TASK_COMPLETE && | |
113 | task->task_status.stat == SAS_DATA_UNDERRUN) { | |
2d4b63e1 JB |
114 | /* no error, but return the number of bytes of |
115 | * underrun */ | |
116 | res = task->task_status.residual; | |
117 | break; | |
89d3cf6a JS |
118 | } |
119 | if (task->task_status.resp == SAS_TASK_COMPLETE && | |
120 | task->task_status.stat == SAS_DATA_OVERRUN) { | |
2d4b63e1 JB |
121 | res = -EMSGSIZE; |
122 | break; | |
36a39947 DW |
123 | } |
124 | if (task->task_status.resp == SAS_TASK_UNDELIVERED && | |
125 | task->task_status.stat == SAS_DEVICE_UNKNOWN) | |
126 | break; | |
127 | else { | |
15ba7806 JG |
128 | pr_notice("%s: task to dev %016llx response: 0x%x status 0x%x\n", |
129 | __func__, | |
130 | SAS_ADDR(dev->sas_addr), | |
131 | task->task_status.resp, | |
132 | task->task_status.stat); | |
42961ee8 | 133 | sas_free_task(task); |
134 | task = NULL; | |
135 | } | |
2908d778 | 136 | } |
89d3cf6a | 137 | mutex_unlock(&dev->ex_dev.cmd_mutex); |
0da7ca4c | 138 | pm_runtime_put_sync(ha->dev); |
89d3cf6a | 139 | |
42961ee8 | 140 | BUG_ON(retry == 3 && task != NULL); |
89d3cf6a | 141 | sas_free_task(task); |
2908d778 JB |
142 | return res; |
143 | } | |
144 | ||
651a0136 CH |
145 | static int smp_execute_task(struct domain_device *dev, void *req, int req_size, |
146 | void *resp, int resp_size) | |
147 | { | |
148 | struct scatterlist req_sg; | |
149 | struct scatterlist resp_sg; | |
150 | ||
151 | sg_init_one(&req_sg, req, req_size); | |
152 | sg_init_one(&resp_sg, resp, resp_size); | |
153 | return smp_execute_task_sg(dev, &req_sg, &resp_sg); | |
154 | } | |
155 | ||
2908d778 JB |
156 | /* ---------- Allocations ---------- */ |
157 | ||
158 | static inline void *alloc_smp_req(int size) | |
159 | { | |
e675a4fd | 160 | u8 *p = kzalloc(ALIGN(size, ARCH_DMA_MINALIGN), GFP_KERNEL); |
2908d778 JB |
161 | if (p) |
162 | p[0] = SMP_REQUEST; | |
163 | return p; | |
164 | } | |
165 | ||
166 | static inline void *alloc_smp_resp(int size) | |
167 | { | |
168 | return kzalloc(size, GFP_KERNEL); | |
169 | } | |
170 | ||
d214d81e DW |
171 | static char sas_route_char(struct domain_device *dev, struct ex_phy *phy) |
172 | { | |
173 | switch (phy->routing_attr) { | |
174 | case TABLE_ROUTING: | |
175 | if (dev->ex_dev.t2t_supp) | |
176 | return 'U'; | |
177 | else | |
178 | return 'T'; | |
179 | case DIRECT_ROUTING: | |
180 | return 'D'; | |
181 | case SUBTRACTIVE_ROUTING: | |
182 | return 'S'; | |
183 | default: | |
184 | return '?'; | |
185 | } | |
186 | } | |
2908d778 | 187 | |
aa9f8328 | 188 | static enum sas_device_type to_dev_type(struct discover_resp *dr) |
2908d778 | 189 | { |
354cf829 DW |
190 | /* This is detecting a failure to transmit initial dev to host |
191 | * FIS as described in section J.5 of sas-2 r16 | |
192 | */ | |
aa9f8328 | 193 | if (dr->attached_dev_type == SAS_PHY_UNUSED && dr->attached_sata_dev && |
354cf829 | 194 | dr->linkrate >= SAS_LINK_RATE_1_5_GBPS) |
aa9f8328 | 195 | return SAS_SATA_PENDING; |
354cf829 DW |
196 | else |
197 | return dr->attached_dev_type; | |
198 | } | |
199 | ||
c3752f44 DLM |
200 | static void sas_set_ex_phy(struct domain_device *dev, int phy_id, |
201 | struct smp_disc_resp *disc_resp) | |
354cf829 | 202 | { |
aa9f8328 | 203 | enum sas_device_type dev_type; |
354cf829 DW |
204 | enum sas_linkrate linkrate; |
205 | u8 sas_addr[SAS_ADDR_SIZE]; | |
c3752f44 | 206 | struct discover_resp *dr = &disc_resp->disc; |
0f3fce5c | 207 | struct sas_ha_struct *ha = dev->port->ha; |
2908d778 JB |
208 | struct expander_device *ex = &dev->ex_dev; |
209 | struct ex_phy *phy = &ex->ex_phy[phy_id]; | |
2908d778 | 210 | struct sas_rphy *rphy = dev->rphy; |
d214d81e DW |
211 | bool new_phy = !phy->phy; |
212 | char *type; | |
2908d778 | 213 | |
d214d81e | 214 | if (new_phy) { |
0f3fce5c DW |
215 | if (WARN_ON_ONCE(test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state))) |
216 | return; | |
2908d778 JB |
217 | phy->phy = sas_phy_alloc(&rphy->dev, phy_id); |
218 | ||
219 | /* FIXME: error_handling */ | |
220 | BUG_ON(!phy->phy); | |
221 | } | |
222 | ||
c3752f44 | 223 | switch (disc_resp->result) { |
2908d778 JB |
224 | case SMP_RESP_PHY_VACANT: |
225 | phy->phy_state = PHY_VACANT; | |
2bc72c91 | 226 | break; |
2908d778 JB |
227 | default: |
228 | phy->phy_state = PHY_NOT_PRESENT; | |
2bc72c91 | 229 | break; |
2908d778 JB |
230 | case SMP_RESP_FUNC_ACC: |
231 | phy->phy_state = PHY_EMPTY; /* do not know yet */ | |
232 | break; | |
233 | } | |
234 | ||
354cf829 DW |
235 | /* check if anything important changed to squelch debug */ |
236 | dev_type = phy->attached_dev_type; | |
237 | linkrate = phy->linkrate; | |
238 | memcpy(sas_addr, phy->attached_sas_addr, SAS_ADDR_SIZE); | |
239 | ||
d4a2618f LD |
240 | /* Handle vacant phy - rest of dr data is not valid so skip it */ |
241 | if (phy->phy_state == PHY_VACANT) { | |
242 | memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE); | |
aa9f8328 | 243 | phy->attached_dev_type = SAS_PHY_UNUSED; |
d4a2618f LD |
244 | if (!test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state)) { |
245 | phy->phy_id = phy_id; | |
246 | goto skip; | |
247 | } else | |
248 | goto out; | |
249 | } | |
250 | ||
354cf829 | 251 | phy->attached_dev_type = to_dev_type(dr); |
0f3fce5c DW |
252 | if (test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state)) |
253 | goto out; | |
2908d778 | 254 | phy->phy_id = phy_id; |
2908d778 JB |
255 | phy->linkrate = dr->linkrate; |
256 | phy->attached_sata_host = dr->attached_sata_host; | |
257 | phy->attached_sata_dev = dr->attached_sata_dev; | |
258 | phy->attached_sata_ps = dr->attached_sata_ps; | |
259 | phy->attached_iproto = dr->iproto << 1; | |
260 | phy->attached_tproto = dr->tproto << 1; | |
7d1d8651 DW |
261 | /* help some expanders that fail to zero sas_address in the 'no |
262 | * device' case | |
263 | */ | |
06036a0a | 264 | if (phy->attached_dev_type == SAS_PHY_UNUSED) |
7d1d8651 DW |
265 | memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE); |
266 | else | |
267 | memcpy(phy->attached_sas_addr, dr->attached_sas_addr, SAS_ADDR_SIZE); | |
2908d778 JB |
268 | phy->attached_phy_id = dr->attached_phy_id; |
269 | phy->phy_change_count = dr->change_count; | |
270 | phy->routing_attr = dr->routing_attr; | |
271 | phy->virtual = dr->virtual; | |
272 | phy->last_da_index = -1; | |
273 | ||
bb041a0e | 274 | phy->phy->identify.sas_address = SAS_ADDR(phy->attached_sas_addr); |
354cf829 | 275 | phy->phy->identify.device_type = dr->attached_dev_type; |
2908d778 JB |
276 | phy->phy->identify.initiator_port_protocols = phy->attached_iproto; |
277 | phy->phy->identify.target_port_protocols = phy->attached_tproto; | |
77c309f3 DW |
278 | if (!phy->attached_tproto && dr->attached_sata_dev) |
279 | phy->phy->identify.target_port_protocols = SAS_PROTOCOL_SATA; | |
2908d778 | 280 | phy->phy->identify.phy_identifier = phy_id; |
a01e70e5 JB |
281 | phy->phy->minimum_linkrate_hw = dr->hmin_linkrate; |
282 | phy->phy->maximum_linkrate_hw = dr->hmax_linkrate; | |
283 | phy->phy->minimum_linkrate = dr->pmin_linkrate; | |
284 | phy->phy->maximum_linkrate = dr->pmax_linkrate; | |
88edf746 | 285 | phy->phy->negotiated_linkrate = phy->linkrate; |
affc6778 | 286 | phy->phy->enabled = (phy->linkrate != SAS_PHY_DISABLED); |
2908d778 | 287 | |
d4a2618f | 288 | skip: |
d214d81e | 289 | if (new_phy) |
2bc72c91 JW |
290 | if (sas_phy_add(phy->phy)) { |
291 | sas_phy_free(phy->phy); | |
292 | return; | |
293 | } | |
2908d778 | 294 | |
0f3fce5c | 295 | out: |
d214d81e | 296 | switch (phy->attached_dev_type) { |
aa9f8328 | 297 | case SAS_SATA_PENDING: |
354cf829 DW |
298 | type = "stp pending"; |
299 | break; | |
aa9f8328 | 300 | case SAS_PHY_UNUSED: |
d214d81e DW |
301 | type = "no device"; |
302 | break; | |
aa9f8328 | 303 | case SAS_END_DEVICE: |
d214d81e DW |
304 | if (phy->attached_iproto) { |
305 | if (phy->attached_tproto) | |
306 | type = "host+target"; | |
307 | else | |
308 | type = "host"; | |
309 | } else { | |
310 | if (dr->attached_sata_dev) | |
311 | type = "stp"; | |
312 | else | |
313 | type = "ssp"; | |
314 | } | |
315 | break; | |
aa9f8328 JB |
316 | case SAS_EDGE_EXPANDER_DEVICE: |
317 | case SAS_FANOUT_EXPANDER_DEVICE: | |
d214d81e DW |
318 | type = "smp"; |
319 | break; | |
320 | default: | |
321 | type = "unknown"; | |
322 | } | |
2908d778 | 323 | |
354cf829 DW |
324 | /* this routine is polled by libata error recovery so filter |
325 | * unimportant messages | |
326 | */ | |
327 | if (new_phy || phy->attached_dev_type != dev_type || | |
328 | phy->linkrate != linkrate || | |
329 | SAS_ADDR(phy->attached_sas_addr) != SAS_ADDR(sas_addr)) | |
330 | /* pass */; | |
331 | else | |
332 | return; | |
333 | ||
0f3fce5c DW |
334 | /* if the attached device type changed and ata_eh is active, |
335 | * make sure we run revalidation when eh completes (see: | |
336 | * sas_enable_revalidation) | |
337 | */ | |
338 | if (test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state)) | |
339 | set_bit(DISCE_REVALIDATE_DOMAIN, &dev->port->disc.pending); | |
340 | ||
15ba7806 JG |
341 | pr_debug("%sex %016llx phy%02d:%c:%X attached: %016llx (%s)\n", |
342 | test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state) ? "ata: " : "", | |
343 | SAS_ADDR(dev->sas_addr), phy->phy_id, | |
344 | sas_route_char(dev, phy), phy->linkrate, | |
345 | SAS_ADDR(phy->attached_sas_addr), type); | |
2908d778 JB |
346 | } |
347 | ||
b52df417 | 348 | /* check if we have an existing attached ata device on this expander phy */ |
81c757bc | 349 | struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id) |
b52df417 DW |
350 | { |
351 | struct ex_phy *ex_phy = &ex_dev->ex_dev.ex_phy[phy_id]; | |
352 | struct domain_device *dev; | |
353 | struct sas_rphy *rphy; | |
354 | ||
355 | if (!ex_phy->port) | |
356 | return NULL; | |
357 | ||
358 | rphy = ex_phy->port->rphy; | |
359 | if (!rphy) | |
360 | return NULL; | |
361 | ||
362 | dev = sas_find_dev_by_rphy(rphy); | |
363 | ||
364 | if (dev && dev_is_sata(dev)) | |
365 | return dev; | |
366 | ||
367 | return NULL; | |
368 | } | |
369 | ||
2908d778 | 370 | #define DISCOVER_REQ_SIZE 16 |
c3752f44 | 371 | #define DISCOVER_RESP_SIZE sizeof(struct smp_disc_resp) |
2908d778 | 372 | |
1acce194 | 373 | static int sas_ex_phy_discover_helper(struct domain_device *dev, u8 *disc_req, |
c3752f44 DLM |
374 | struct smp_disc_resp *disc_resp, |
375 | int single) | |
1acce194 | 376 | { |
c3752f44 | 377 | struct discover_resp *dr = &disc_resp->disc; |
354cf829 | 378 | int res; |
1acce194 JB |
379 | |
380 | disc_req[9] = single; | |
1acce194 | 381 | |
354cf829 DW |
382 | res = smp_execute_task(dev, disc_req, DISCOVER_REQ_SIZE, |
383 | disc_resp, DISCOVER_RESP_SIZE); | |
384 | if (res) | |
385 | return res; | |
354cf829 | 386 | if (memcmp(dev->sas_addr, dr->attached_sas_addr, SAS_ADDR_SIZE) == 0) { |
71a4a992 | 387 | pr_notice("Found loopback topology, just ignore it!\n"); |
354cf829 | 388 | return 0; |
1acce194 JB |
389 | } |
390 | sas_set_ex_phy(dev, single, disc_resp); | |
391 | return 0; | |
392 | } | |
393 | ||
354cf829 | 394 | int sas_ex_phy_discover(struct domain_device *dev, int single) |
2908d778 JB |
395 | { |
396 | struct expander_device *ex = &dev->ex_dev; | |
397 | int res = 0; | |
398 | u8 *disc_req; | |
c3752f44 | 399 | struct smp_disc_resp *disc_resp; |
2908d778 JB |
400 | |
401 | disc_req = alloc_smp_req(DISCOVER_REQ_SIZE); | |
402 | if (!disc_req) | |
403 | return -ENOMEM; | |
404 | ||
95c9f4d4 | 405 | disc_resp = alloc_smp_resp(DISCOVER_RESP_SIZE); |
2908d778 JB |
406 | if (!disc_resp) { |
407 | kfree(disc_req); | |
408 | return -ENOMEM; | |
409 | } | |
410 | ||
411 | disc_req[1] = SMP_DISCOVER; | |
412 | ||
413 | if (0 <= single && single < ex->num_phys) { | |
1acce194 | 414 | res = sas_ex_phy_discover_helper(dev, disc_req, disc_resp, single); |
2908d778 JB |
415 | } else { |
416 | int i; | |
417 | ||
418 | for (i = 0; i < ex->num_phys; i++) { | |
1acce194 JB |
419 | res = sas_ex_phy_discover_helper(dev, disc_req, |
420 | disc_resp, i); | |
2908d778 JB |
421 | if (res) |
422 | goto out_err; | |
2908d778 JB |
423 | } |
424 | } | |
425 | out_err: | |
426 | kfree(disc_resp); | |
427 | kfree(disc_req); | |
428 | return res; | |
429 | } | |
430 | ||
431 | static int sas_expander_discover(struct domain_device *dev) | |
432 | { | |
433 | struct expander_device *ex = &dev->ex_dev; | |
55eb809f | 434 | int res; |
2908d778 | 435 | |
6396bb22 | 436 | ex->ex_phy = kcalloc(ex->num_phys, sizeof(*ex->ex_phy), GFP_KERNEL); |
2908d778 JB |
437 | if (!ex->ex_phy) |
438 | return -ENOMEM; | |
439 | ||
440 | res = sas_ex_phy_discover(dev, -1); | |
441 | if (res) | |
442 | goto out_err; | |
443 | ||
444 | return 0; | |
445 | out_err: | |
446 | kfree(ex->ex_phy); | |
447 | ex->ex_phy = NULL; | |
448 | return res; | |
449 | } | |
450 | ||
451 | #define MAX_EXPANDER_PHYS 128 | |
452 | ||
2908d778 | 453 | #define RG_REQ_SIZE 8 |
44f2bfe9 | 454 | #define RG_RESP_SIZE sizeof(struct smp_rg_resp) |
2908d778 JB |
455 | |
456 | static int sas_ex_general(struct domain_device *dev) | |
457 | { | |
458 | u8 *rg_req; | |
44f2bfe9 DLM |
459 | struct smp_rg_resp *rg_resp; |
460 | struct report_general_resp *rg; | |
2908d778 JB |
461 | int res; |
462 | int i; | |
463 | ||
464 | rg_req = alloc_smp_req(RG_REQ_SIZE); | |
465 | if (!rg_req) | |
466 | return -ENOMEM; | |
467 | ||
468 | rg_resp = alloc_smp_resp(RG_RESP_SIZE); | |
469 | if (!rg_resp) { | |
470 | kfree(rg_req); | |
471 | return -ENOMEM; | |
472 | } | |
473 | ||
474 | rg_req[1] = SMP_REPORT_GENERAL; | |
475 | ||
476 | for (i = 0; i < 5; i++) { | |
477 | res = smp_execute_task(dev, rg_req, RG_REQ_SIZE, rg_resp, | |
478 | RG_RESP_SIZE); | |
479 | ||
480 | if (res) { | |
15ba7806 JG |
481 | pr_notice("RG to ex %016llx failed:0x%x\n", |
482 | SAS_ADDR(dev->sas_addr), res); | |
2908d778 JB |
483 | goto out; |
484 | } else if (rg_resp->result != SMP_RESP_FUNC_ACC) { | |
15ba7806 JG |
485 | pr_debug("RG:ex %016llx returned SMP result:0x%x\n", |
486 | SAS_ADDR(dev->sas_addr), rg_resp->result); | |
2908d778 JB |
487 | res = rg_resp->result; |
488 | goto out; | |
489 | } | |
490 | ||
44f2bfe9 DLM |
491 | rg = &rg_resp->rg; |
492 | dev->ex_dev.ex_change_count = be16_to_cpu(rg->change_count); | |
493 | dev->ex_dev.max_route_indexes = be16_to_cpu(rg->route_indexes); | |
494 | dev->ex_dev.num_phys = min(rg->num_phys, (u8)MAX_EXPANDER_PHYS); | |
495 | dev->ex_dev.t2t_supp = rg->t2t_supp; | |
496 | dev->ex_dev.conf_route_table = rg->conf_route_table; | |
497 | dev->ex_dev.configuring = rg->configuring; | |
498 | memcpy(dev->ex_dev.enclosure_logical_id, | |
499 | rg->enclosure_logical_id, 8); | |
2908d778 JB |
500 | |
501 | if (dev->ex_dev.configuring) { | |
b3e3d4c6 | 502 | pr_debug("RG: ex %016llx self-configuring...\n", |
15ba7806 | 503 | SAS_ADDR(dev->sas_addr)); |
2908d778 JB |
504 | schedule_timeout_interruptible(5*HZ); |
505 | } else | |
506 | break; | |
507 | } | |
508 | out: | |
509 | kfree(rg_req); | |
510 | kfree(rg_resp); | |
511 | return res; | |
512 | } | |
513 | ||
514 | static void ex_assign_manuf_info(struct domain_device *dev, void | |
515 | *_mi_resp) | |
516 | { | |
517 | u8 *mi_resp = _mi_resp; | |
518 | struct sas_rphy *rphy = dev->rphy; | |
519 | struct sas_expander_device *edev = rphy_to_expander_device(rphy); | |
520 | ||
521 | memcpy(edev->vendor_id, mi_resp + 12, SAS_EXPANDER_VENDOR_ID_LEN); | |
522 | memcpy(edev->product_id, mi_resp + 20, SAS_EXPANDER_PRODUCT_ID_LEN); | |
523 | memcpy(edev->product_rev, mi_resp + 36, | |
524 | SAS_EXPANDER_PRODUCT_REV_LEN); | |
525 | ||
526 | if (mi_resp[8] & 1) { | |
527 | memcpy(edev->component_vendor_id, mi_resp + 40, | |
528 | SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN); | |
529 | edev->component_id = mi_resp[48] << 8 | mi_resp[49]; | |
530 | edev->component_revision_id = mi_resp[50]; | |
531 | } | |
532 | } | |
533 | ||
534 | #define MI_REQ_SIZE 8 | |
535 | #define MI_RESP_SIZE 64 | |
536 | ||
537 | static int sas_ex_manuf_info(struct domain_device *dev) | |
538 | { | |
539 | u8 *mi_req; | |
540 | u8 *mi_resp; | |
541 | int res; | |
542 | ||
543 | mi_req = alloc_smp_req(MI_REQ_SIZE); | |
544 | if (!mi_req) | |
545 | return -ENOMEM; | |
546 | ||
547 | mi_resp = alloc_smp_resp(MI_RESP_SIZE); | |
548 | if (!mi_resp) { | |
549 | kfree(mi_req); | |
550 | return -ENOMEM; | |
551 | } | |
552 | ||
553 | mi_req[1] = SMP_REPORT_MANUF_INFO; | |
554 | ||
857a80bb | 555 | res = smp_execute_task(dev, mi_req, MI_REQ_SIZE, mi_resp, MI_RESP_SIZE); |
2908d778 | 556 | if (res) { |
15ba7806 JG |
557 | pr_notice("MI: ex %016llx failed:0x%x\n", |
558 | SAS_ADDR(dev->sas_addr), res); | |
2908d778 JB |
559 | goto out; |
560 | } else if (mi_resp[2] != SMP_RESP_FUNC_ACC) { | |
15ba7806 JG |
561 | pr_debug("MI ex %016llx returned SMP result:0x%x\n", |
562 | SAS_ADDR(dev->sas_addr), mi_resp[2]); | |
2908d778 JB |
563 | goto out; |
564 | } | |
565 | ||
566 | ex_assign_manuf_info(dev, mi_resp); | |
567 | out: | |
568 | kfree(mi_req); | |
569 | kfree(mi_resp); | |
570 | return res; | |
571 | } | |
572 | ||
573 | #define PC_REQ_SIZE 44 | |
574 | #define PC_RESP_SIZE 8 | |
575 | ||
576 | int sas_smp_phy_control(struct domain_device *dev, int phy_id, | |
a01e70e5 JB |
577 | enum phy_func phy_func, |
578 | struct sas_phy_linkrates *rates) | |
2908d778 JB |
579 | { |
580 | u8 *pc_req; | |
581 | u8 *pc_resp; | |
582 | int res; | |
583 | ||
584 | pc_req = alloc_smp_req(PC_REQ_SIZE); | |
585 | if (!pc_req) | |
586 | return -ENOMEM; | |
587 | ||
588 | pc_resp = alloc_smp_resp(PC_RESP_SIZE); | |
589 | if (!pc_resp) { | |
590 | kfree(pc_req); | |
591 | return -ENOMEM; | |
592 | } | |
593 | ||
594 | pc_req[1] = SMP_PHY_CONTROL; | |
595 | pc_req[9] = phy_id; | |
857a80bb | 596 | pc_req[10] = phy_func; |
a01e70e5 JB |
597 | if (rates) { |
598 | pc_req[32] = rates->minimum_linkrate << 4; | |
599 | pc_req[33] = rates->maximum_linkrate << 4; | |
600 | } | |
2908d778 | 601 | |
857a80bb | 602 | res = smp_execute_task(dev, pc_req, PC_REQ_SIZE, pc_resp, PC_RESP_SIZE); |
01929a65 JG |
603 | if (res) { |
604 | pr_err("ex %016llx phy%02d PHY control failed: %d\n", | |
605 | SAS_ADDR(dev->sas_addr), phy_id, res); | |
606 | } else if (pc_resp[2] != SMP_RESP_FUNC_ACC) { | |
607 | pr_err("ex %016llx phy%02d PHY control failed: function result 0x%x\n", | |
608 | SAS_ADDR(dev->sas_addr), phy_id, pc_resp[2]); | |
609 | res = pc_resp[2]; | |
610 | } | |
2908d778 JB |
611 | kfree(pc_resp); |
612 | kfree(pc_req); | |
613 | return res; | |
614 | } | |
615 | ||
616 | static void sas_ex_disable_phy(struct domain_device *dev, int phy_id) | |
617 | { | |
618 | struct expander_device *ex = &dev->ex_dev; | |
619 | struct ex_phy *phy = &ex->ex_phy[phy_id]; | |
620 | ||
a01e70e5 | 621 | sas_smp_phy_control(dev, phy_id, PHY_FUNC_DISABLE, NULL); |
88edf746 | 622 | phy->linkrate = SAS_PHY_DISABLED; |
2908d778 JB |
623 | } |
624 | ||
625 | static void sas_ex_disable_port(struct domain_device *dev, u8 *sas_addr) | |
626 | { | |
627 | struct expander_device *ex = &dev->ex_dev; | |
628 | int i; | |
629 | ||
630 | for (i = 0; i < ex->num_phys; i++) { | |
631 | struct ex_phy *phy = &ex->ex_phy[i]; | |
632 | ||
633 | if (phy->phy_state == PHY_VACANT || | |
634 | phy->phy_state == PHY_NOT_PRESENT) | |
635 | continue; | |
636 | ||
637 | if (SAS_ADDR(phy->attached_sas_addr) == SAS_ADDR(sas_addr)) | |
638 | sas_ex_disable_phy(dev, i); | |
639 | } | |
640 | } | |
641 | ||
642 | static int sas_dev_present_in_domain(struct asd_sas_port *port, | |
643 | u8 *sas_addr) | |
644 | { | |
645 | struct domain_device *dev; | |
646 | ||
647 | if (SAS_ADDR(port->sas_addr) == SAS_ADDR(sas_addr)) | |
648 | return 1; | |
649 | list_for_each_entry(dev, &port->dev_list, dev_list_node) { | |
650 | if (SAS_ADDR(dev->sas_addr) == SAS_ADDR(sas_addr)) | |
651 | return 1; | |
652 | } | |
653 | return 0; | |
654 | } | |
655 | ||
656 | #define RPEL_REQ_SIZE 16 | |
657 | #define RPEL_RESP_SIZE 32 | |
658 | int sas_smp_get_phy_events(struct sas_phy *phy) | |
659 | { | |
660 | int res; | |
92631fa4 JJ |
661 | u8 *req; |
662 | u8 *resp; | |
2908d778 JB |
663 | struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent); |
664 | struct domain_device *dev = sas_find_dev_by_rphy(rphy); | |
2908d778 | 665 | |
92631fa4 JJ |
666 | req = alloc_smp_req(RPEL_REQ_SIZE); |
667 | if (!req) | |
2908d778 JB |
668 | return -ENOMEM; |
669 | ||
92631fa4 JJ |
670 | resp = alloc_smp_resp(RPEL_RESP_SIZE); |
671 | if (!resp) { | |
672 | kfree(req); | |
673 | return -ENOMEM; | |
674 | } | |
675 | ||
2908d778 JB |
676 | req[1] = SMP_REPORT_PHY_ERR_LOG; |
677 | req[9] = phy->number; | |
678 | ||
679 | res = smp_execute_task(dev, req, RPEL_REQ_SIZE, | |
857a80bb | 680 | resp, RPEL_RESP_SIZE); |
2908d778 | 681 | |
2b23d950 | 682 | if (res) |
2908d778 JB |
683 | goto out; |
684 | ||
59abc8cc BVA |
685 | phy->invalid_dword_count = get_unaligned_be32(&resp[12]); |
686 | phy->running_disparity_error_count = get_unaligned_be32(&resp[16]); | |
687 | phy->loss_of_dword_sync_count = get_unaligned_be32(&resp[20]); | |
688 | phy->phy_reset_problem_count = get_unaligned_be32(&resp[24]); | |
2908d778 JB |
689 | |
690 | out: | |
4a491b1a | 691 | kfree(req); |
2908d778 JB |
692 | kfree(resp); |
693 | return res; | |
694 | ||
695 | } | |
696 | ||
b9142174 JB |
697 | #ifdef CONFIG_SCSI_SAS_ATA |
698 | ||
2908d778 | 699 | #define RPS_REQ_SIZE 16 |
3dafe064 | 700 | #define RPS_RESP_SIZE sizeof(struct smp_rps_resp) |
2908d778 | 701 | |
354cf829 | 702 | int sas_get_report_phy_sata(struct domain_device *dev, int phy_id, |
3dafe064 | 703 | struct smp_rps_resp *rps_resp) |
2908d778 JB |
704 | { |
705 | int res; | |
706 | u8 *rps_req = alloc_smp_req(RPS_REQ_SIZE); | |
1acce194 | 707 | u8 *resp = (u8 *)rps_resp; |
2908d778 JB |
708 | |
709 | if (!rps_req) | |
710 | return -ENOMEM; | |
711 | ||
712 | rps_req[1] = SMP_REPORT_PHY_SATA; | |
713 | rps_req[9] = phy_id; | |
714 | ||
715 | res = smp_execute_task(dev, rps_req, RPS_REQ_SIZE, | |
857a80bb | 716 | rps_resp, RPS_RESP_SIZE); |
2908d778 | 717 | |
1acce194 JB |
718 | /* 0x34 is the FIS type for the D2H fis. There's a potential |
719 | * standards cockup here. sas-2 explicitly specifies the FIS | |
720 | * should be encoded so that FIS type is in resp[24]. | |
721 | * However, some expanders endian reverse this. Undo the | |
722 | * reversal here */ | |
723 | if (!res && resp[27] == 0x34 && resp[24] != 0x34) { | |
724 | int i; | |
725 | ||
726 | for (i = 0; i < 5; i++) { | |
727 | int j = 24 + (i*4); | |
728 | u8 a, b; | |
729 | a = resp[j + 0]; | |
730 | b = resp[j + 1]; | |
731 | resp[j + 0] = resp[j + 3]; | |
732 | resp[j + 1] = resp[j + 2]; | |
733 | resp[j + 2] = b; | |
734 | resp[j + 3] = a; | |
735 | } | |
736 | } | |
737 | ||
2908d778 | 738 | kfree(rps_req); |
1acce194 | 739 | return res; |
2908d778 | 740 | } |
b9142174 | 741 | #endif |
2908d778 JB |
742 | |
743 | static void sas_ex_get_linkrate(struct domain_device *parent, | |
744 | struct domain_device *child, | |
745 | struct ex_phy *parent_phy) | |
746 | { | |
747 | struct expander_device *parent_ex = &parent->ex_dev; | |
748 | struct sas_port *port; | |
749 | int i; | |
750 | ||
751 | child->pathways = 0; | |
752 | ||
753 | port = parent_phy->port; | |
754 | ||
755 | for (i = 0; i < parent_ex->num_phys; i++) { | |
756 | struct ex_phy *phy = &parent_ex->ex_phy[i]; | |
757 | ||
758 | if (phy->phy_state == PHY_VACANT || | |
759 | phy->phy_state == PHY_NOT_PRESENT) | |
760 | continue; | |
761 | ||
ad74d1da | 762 | if (sas_phy_match_dev_addr(child, phy)) { |
2908d778 JB |
763 | child->min_linkrate = min(parent->min_linkrate, |
764 | phy->linkrate); | |
765 | child->max_linkrate = max(parent->max_linkrate, | |
766 | phy->linkrate); | |
767 | child->pathways++; | |
768 | sas_port_add_phy(port, phy->phy); | |
769 | } | |
770 | } | |
771 | child->linkrate = min(parent_phy->linkrate, child->max_linkrate); | |
772 | child->pathways = min(child->pathways, parent->pathways); | |
773 | } | |
774 | ||
5d39b77c JY |
775 | static int sas_ex_add_dev(struct domain_device *parent, struct ex_phy *phy, |
776 | struct domain_device *child, int phy_id) | |
777 | { | |
778 | struct sas_rphy *rphy; | |
779 | int res; | |
780 | ||
781 | child->dev_type = SAS_END_DEVICE; | |
782 | rphy = sas_end_device_alloc(phy->port); | |
783 | if (!rphy) | |
784 | return -ENOMEM; | |
785 | ||
786 | child->tproto = phy->attached_tproto; | |
787 | sas_init_dev(child); | |
788 | ||
789 | child->rphy = rphy; | |
790 | get_device(&rphy->dev); | |
791 | rphy->identify.phy_identifier = phy_id; | |
792 | sas_fill_in_rphy(child, rphy); | |
793 | ||
794 | list_add_tail(&child->disco_list_node, &parent->port->disco_list); | |
795 | ||
796 | res = sas_notify_lldd_dev_found(child); | |
797 | if (res) { | |
798 | pr_notice("notify lldd for device %016llx at %016llx:%02d returned 0x%x\n", | |
799 | SAS_ADDR(child->sas_addr), | |
800 | SAS_ADDR(parent->sas_addr), phy_id, res); | |
801 | sas_rphy_free(child->rphy); | |
802 | list_del(&child->disco_list_node); | |
803 | return res; | |
804 | } | |
805 | ||
806 | return 0; | |
807 | } | |
808 | ||
2908d778 JB |
809 | static struct domain_device *sas_ex_discover_end_dev( |
810 | struct domain_device *parent, int phy_id) | |
811 | { | |
812 | struct expander_device *parent_ex = &parent->ex_dev; | |
813 | struct ex_phy *phy = &parent_ex->ex_phy[phy_id]; | |
814 | struct domain_device *child = NULL; | |
2908d778 JB |
815 | int res; |
816 | ||
817 | if (phy->attached_sata_host || phy->attached_sata_ps) | |
818 | return NULL; | |
819 | ||
735f7d2f | 820 | child = sas_alloc_device(); |
2908d778 JB |
821 | if (!child) |
822 | return NULL; | |
823 | ||
735f7d2f | 824 | kref_get(&parent->kref); |
2908d778 JB |
825 | child->parent = parent; |
826 | child->port = parent->port; | |
827 | child->iproto = phy->attached_iproto; | |
828 | memcpy(child->sas_addr, phy->attached_sas_addr, SAS_ADDR_SIZE); | |
829 | sas_hash_addr(child->hashed_sas_addr, child->sas_addr); | |
024879ea JB |
830 | if (!phy->port) { |
831 | phy->port = sas_port_alloc(&parent->rphy->dev, phy_id); | |
832 | if (unlikely(!phy->port)) | |
833 | goto out_err; | |
834 | if (unlikely(sas_port_add(phy->port) != 0)) { | |
835 | sas_port_free(phy->port); | |
836 | goto out_err; | |
837 | } | |
838 | } | |
2908d778 | 839 | sas_ex_get_linkrate(parent, child, phy); |
f41a0c44 | 840 | sas_device_set_phy(child, phy->port); |
2908d778 | 841 | |
5929faf3 | 842 | if ((phy->attached_tproto & SAS_PROTOCOL_STP) || phy->attached_sata_dev) { |
7cc7646b | 843 | res = sas_ata_add_dev(parent, phy, child, phy_id); |
7cc7646b | 844 | } else if (phy->attached_tproto & SAS_PROTOCOL_SSP) { |
5d39b77c | 845 | res = sas_ex_add_dev(parent, phy, child, phy_id); |
2908d778 | 846 | } else { |
15ba7806 JG |
847 | pr_notice("target proto 0x%x at %016llx:0x%x not handled\n", |
848 | phy->attached_tproto, SAS_ADDR(parent->sas_addr), | |
849 | phy_id); | |
5d39b77c | 850 | res = -ENODEV; |
2908d778 JB |
851 | } |
852 | ||
5d39b77c JY |
853 | if (res) |
854 | goto out_free; | |
855 | ||
2908d778 JB |
856 | list_add_tail(&child->siblings, &parent_ex->children); |
857 | return child; | |
024879ea | 858 | |
024879ea JB |
859 | out_free: |
860 | sas_port_delete(phy->port); | |
861 | out_err: | |
862 | phy->port = NULL; | |
735f7d2f | 863 | sas_put_device(child); |
024879ea | 864 | return NULL; |
2908d778 JB |
865 | } |
866 | ||
423f7cf4 | 867 | /* See if this phy is part of a wide port */ |
b17caa17 | 868 | static bool sas_ex_join_wide_port(struct domain_device *parent, int phy_id) |
423f7cf4 DW |
869 | { |
870 | struct ex_phy *phy = &parent->ex_dev.ex_phy[phy_id]; | |
871 | int i; | |
872 | ||
873 | for (i = 0; i < parent->ex_dev.num_phys; i++) { | |
874 | struct ex_phy *ephy = &parent->ex_dev.ex_phy[i]; | |
875 | ||
876 | if (ephy == phy) | |
877 | continue; | |
878 | ||
879 | if (!memcmp(phy->attached_sas_addr, ephy->attached_sas_addr, | |
880 | SAS_ADDR_SIZE) && ephy->port) { | |
888ea1b1 | 881 | sas_port_add_ex_phy(ephy->port, phy); |
b17caa17 | 882 | return true; |
423f7cf4 DW |
883 | } |
884 | } | |
885 | ||
b17caa17 | 886 | return false; |
423f7cf4 DW |
887 | } |
888 | ||
2908d778 JB |
889 | static struct domain_device *sas_ex_discover_expander( |
890 | struct domain_device *parent, int phy_id) | |
891 | { | |
892 | struct sas_expander_device *parent_ex = rphy_to_expander_device(parent->rphy); | |
893 | struct ex_phy *phy = &parent->ex_dev.ex_phy[phy_id]; | |
894 | struct domain_device *child = NULL; | |
895 | struct sas_rphy *rphy; | |
896 | struct sas_expander_device *edev; | |
897 | struct asd_sas_port *port; | |
898 | int res; | |
899 | ||
900 | if (phy->routing_attr == DIRECT_ROUTING) { | |
3c236f8c | 901 | pr_warn("ex %016llx:%02d:D <--> ex %016llx:0x%x is not allowed\n", |
15ba7806 JG |
902 | SAS_ADDR(parent->sas_addr), phy_id, |
903 | SAS_ADDR(phy->attached_sas_addr), | |
904 | phy->attached_phy_id); | |
2908d778 JB |
905 | return NULL; |
906 | } | |
735f7d2f | 907 | child = sas_alloc_device(); |
2908d778 JB |
908 | if (!child) |
909 | return NULL; | |
910 | ||
911 | phy->port = sas_port_alloc(&parent->rphy->dev, phy_id); | |
912 | /* FIXME: better error handling */ | |
913 | BUG_ON(sas_port_add(phy->port) != 0); | |
914 | ||
915 | ||
916 | switch (phy->attached_dev_type) { | |
aa9f8328 | 917 | case SAS_EDGE_EXPANDER_DEVICE: |
2908d778 JB |
918 | rphy = sas_expander_alloc(phy->port, |
919 | SAS_EDGE_EXPANDER_DEVICE); | |
920 | break; | |
aa9f8328 | 921 | case SAS_FANOUT_EXPANDER_DEVICE: |
2908d778 JB |
922 | rphy = sas_expander_alloc(phy->port, |
923 | SAS_FANOUT_EXPANDER_DEVICE); | |
924 | break; | |
925 | default: | |
926 | rphy = NULL; /* shut gcc up */ | |
927 | BUG(); | |
928 | } | |
929 | port = parent->port; | |
930 | child->rphy = rphy; | |
9487669f | 931 | get_device(&rphy->dev); |
2908d778 JB |
932 | edev = rphy_to_expander_device(rphy); |
933 | child->dev_type = phy->attached_dev_type; | |
735f7d2f | 934 | kref_get(&parent->kref); |
2908d778 JB |
935 | child->parent = parent; |
936 | child->port = port; | |
937 | child->iproto = phy->attached_iproto; | |
938 | child->tproto = phy->attached_tproto; | |
939 | memcpy(child->sas_addr, phy->attached_sas_addr, SAS_ADDR_SIZE); | |
940 | sas_hash_addr(child->hashed_sas_addr, child->sas_addr); | |
941 | sas_ex_get_linkrate(parent, child, phy); | |
942 | edev->level = parent_ex->level + 1; | |
943 | parent->port->disc.max_level = max(parent->port->disc.max_level, | |
944 | edev->level); | |
945 | sas_init_dev(child); | |
946 | sas_fill_in_rphy(child, rphy); | |
947 | sas_rphy_add(rphy); | |
948 | ||
9d720d82 | 949 | spin_lock_irq(&parent->port->dev_list_lock); |
2908d778 | 950 | list_add_tail(&child->dev_list_node, &parent->port->dev_list); |
9d720d82 | 951 | spin_unlock_irq(&parent->port->dev_list_lock); |
2908d778 JB |
952 | |
953 | res = sas_discover_expander(child); | |
954 | if (res) { | |
9487669f | 955 | sas_rphy_delete(rphy); |
5911e963 LT |
956 | spin_lock_irq(&parent->port->dev_list_lock); |
957 | list_del(&child->dev_list_node); | |
958 | spin_unlock_irq(&parent->port->dev_list_lock); | |
735f7d2f | 959 | sas_put_device(child); |
3b054179 JY |
960 | sas_port_delete(phy->port); |
961 | phy->port = NULL; | |
2908d778 JB |
962 | return NULL; |
963 | } | |
964 | list_add_tail(&child->siblings, &parent->ex_dev.children); | |
965 | return child; | |
966 | } | |
967 | ||
968 | static int sas_ex_discover_dev(struct domain_device *dev, int phy_id) | |
969 | { | |
970 | struct expander_device *ex = &dev->ex_dev; | |
971 | struct ex_phy *ex_phy = &ex->ex_phy[phy_id]; | |
972 | struct domain_device *child = NULL; | |
973 | int res = 0; | |
974 | ||
975 | /* Phy state */ | |
88edf746 | 976 | if (ex_phy->linkrate == SAS_SATA_SPINUP_HOLD) { |
a01e70e5 | 977 | if (!sas_smp_phy_control(dev, phy_id, PHY_FUNC_LINK_RESET, NULL)) |
2908d778 JB |
978 | res = sas_ex_phy_discover(dev, phy_id); |
979 | if (res) | |
980 | return res; | |
981 | } | |
982 | ||
983 | /* Parent and domain coherency */ | |
868a8824 | 984 | if (!dev->parent && sas_phy_match_port_addr(dev->port, ex_phy)) { |
48032c0b | 985 | sas_ex_add_parent_port(dev, phy_id); |
2908d778 JB |
986 | return 0; |
987 | } | |
ad74d1da | 988 | if (dev->parent && sas_phy_match_dev_addr(dev->parent, ex_phy)) { |
48032c0b | 989 | sas_ex_add_parent_port(dev, phy_id); |
2908d778 JB |
990 | if (ex_phy->routing_attr == TABLE_ROUTING) |
991 | sas_configure_phy(dev, phy_id, dev->port->sas_addr, 1); | |
992 | return 0; | |
993 | } | |
994 | ||
995 | if (sas_dev_present_in_domain(dev->port, ex_phy->attached_sas_addr)) | |
996 | sas_ex_disable_port(dev, ex_phy->attached_sas_addr); | |
997 | ||
aa9f8328 | 998 | if (ex_phy->attached_dev_type == SAS_PHY_UNUSED) { |
2908d778 JB |
999 | if (ex_phy->routing_attr == DIRECT_ROUTING) { |
1000 | memset(ex_phy->attached_sas_addr, 0, SAS_ADDR_SIZE); | |
1001 | sas_configure_routing(dev, ex_phy->attached_sas_addr); | |
1002 | } | |
1003 | return 0; | |
88edf746 | 1004 | } else if (ex_phy->linkrate == SAS_LINK_RATE_UNKNOWN) |
2908d778 JB |
1005 | return 0; |
1006 | ||
aa9f8328 JB |
1007 | if (ex_phy->attached_dev_type != SAS_END_DEVICE && |
1008 | ex_phy->attached_dev_type != SAS_FANOUT_EXPANDER_DEVICE && | |
1009 | ex_phy->attached_dev_type != SAS_EDGE_EXPANDER_DEVICE && | |
1010 | ex_phy->attached_dev_type != SAS_SATA_PENDING) { | |
3c236f8c | 1011 | pr_warn("unknown device type(0x%x) attached to ex %016llx phy%02d\n", |
15ba7806 JG |
1012 | ex_phy->attached_dev_type, |
1013 | SAS_ADDR(dev->sas_addr), | |
1014 | phy_id); | |
2908d778 JB |
1015 | return 0; |
1016 | } | |
1017 | ||
1018 | res = sas_configure_routing(dev, ex_phy->attached_sas_addr); | |
1019 | if (res) { | |
15ba7806 JG |
1020 | pr_notice("configure routing for dev %016llx reported 0x%x. Forgotten\n", |
1021 | SAS_ADDR(ex_phy->attached_sas_addr), res); | |
2908d778 JB |
1022 | sas_disable_routing(dev, ex_phy->attached_sas_addr); |
1023 | return res; | |
1024 | } | |
1025 | ||
b17caa17 | 1026 | if (sas_ex_join_wide_port(dev, phy_id)) { |
3c236f8c | 1027 | pr_debug("Attaching ex phy%02d to wide port %016llx\n", |
15ba7806 | 1028 | phy_id, SAS_ADDR(ex_phy->attached_sas_addr)); |
423f7cf4 DW |
1029 | return res; |
1030 | } | |
1031 | ||
2908d778 | 1032 | switch (ex_phy->attached_dev_type) { |
aa9f8328 JB |
1033 | case SAS_END_DEVICE: |
1034 | case SAS_SATA_PENDING: | |
2908d778 JB |
1035 | child = sas_ex_discover_end_dev(dev, phy_id); |
1036 | break; | |
aa9f8328 | 1037 | case SAS_FANOUT_EXPANDER_DEVICE: |
2908d778 | 1038 | if (SAS_ADDR(dev->port->disc.fanout_sas_addr)) { |
3c236f8c | 1039 | pr_debug("second fanout expander %016llx phy%02d attached to ex %016llx phy%02d\n", |
15ba7806 JG |
1040 | SAS_ADDR(ex_phy->attached_sas_addr), |
1041 | ex_phy->attached_phy_id, | |
1042 | SAS_ADDR(dev->sas_addr), | |
1043 | phy_id); | |
2908d778 | 1044 | sas_ex_disable_phy(dev, phy_id); |
a1b6fb94 | 1045 | return res; |
2908d778 JB |
1046 | } else |
1047 | memcpy(dev->port->disc.fanout_sas_addr, | |
1048 | ex_phy->attached_sas_addr, SAS_ADDR_SIZE); | |
df561f66 | 1049 | fallthrough; |
aa9f8328 | 1050 | case SAS_EDGE_EXPANDER_DEVICE: |
2908d778 JB |
1051 | child = sas_ex_discover_expander(dev, phy_id); |
1052 | break; | |
1053 | default: | |
1054 | break; | |
1055 | } | |
1056 | ||
a1b6fb94 JY |
1057 | if (!child) |
1058 | pr_notice("ex %016llx phy%02d failed to discover\n", | |
1059 | SAS_ADDR(dev->sas_addr), phy_id); | |
2908d778 JB |
1060 | return res; |
1061 | } | |
1062 | ||
1063 | static int sas_find_sub_addr(struct domain_device *dev, u8 *sub_addr) | |
1064 | { | |
1065 | struct expander_device *ex = &dev->ex_dev; | |
1066 | int i; | |
1067 | ||
1068 | for (i = 0; i < ex->num_phys; i++) { | |
1069 | struct ex_phy *phy = &ex->ex_phy[i]; | |
1070 | ||
1071 | if (phy->phy_state == PHY_VACANT || | |
1072 | phy->phy_state == PHY_NOT_PRESENT) | |
1073 | continue; | |
1074 | ||
924a3541 | 1075 | if (dev_is_expander(phy->attached_dev_type) && |
2908d778 JB |
1076 | phy->routing_attr == SUBTRACTIVE_ROUTING) { |
1077 | ||
7b27c5fe | 1078 | memcpy(sub_addr, phy->attached_sas_addr, SAS_ADDR_SIZE); |
2908d778 JB |
1079 | |
1080 | return 1; | |
1081 | } | |
1082 | } | |
1083 | return 0; | |
1084 | } | |
1085 | ||
1086 | static int sas_check_level_subtractive_boundary(struct domain_device *dev) | |
1087 | { | |
1088 | struct expander_device *ex = &dev->ex_dev; | |
1089 | struct domain_device *child; | |
7b27c5fe | 1090 | u8 sub_addr[SAS_ADDR_SIZE] = {0, }; |
2908d778 JB |
1091 | |
1092 | list_for_each_entry(child, &ex->children, siblings) { | |
924a3541 | 1093 | if (!dev_is_expander(child->dev_type)) |
2908d778 JB |
1094 | continue; |
1095 | if (sub_addr[0] == 0) { | |
1096 | sas_find_sub_addr(child, sub_addr); | |
1097 | continue; | |
1098 | } else { | |
7b27c5fe | 1099 | u8 s2[SAS_ADDR_SIZE]; |
2908d778 JB |
1100 | |
1101 | if (sas_find_sub_addr(child, s2) && | |
1102 | (SAS_ADDR(sub_addr) != SAS_ADDR(s2))) { | |
1103 | ||
15ba7806 JG |
1104 | pr_notice("ex %016llx->%016llx-?->%016llx diverges from subtractive boundary %016llx\n", |
1105 | SAS_ADDR(dev->sas_addr), | |
1106 | SAS_ADDR(child->sas_addr), | |
1107 | SAS_ADDR(s2), | |
1108 | SAS_ADDR(sub_addr)); | |
2908d778 JB |
1109 | |
1110 | sas_ex_disable_port(child, s2); | |
1111 | } | |
1112 | } | |
1113 | } | |
1114 | return 0; | |
1115 | } | |
1116 | /** | |
121246ae BVA |
1117 | * sas_ex_discover_devices - discover devices attached to this expander |
1118 | * @dev: pointer to the expander domain device | |
1119 | * @single: if you want to do a single phy, else set to -1; | |
2908d778 JB |
1120 | * |
1121 | * Configure this expander for use with its devices and register the | |
1122 | * devices of this expander. | |
1123 | */ | |
1124 | static int sas_ex_discover_devices(struct domain_device *dev, int single) | |
1125 | { | |
1126 | struct expander_device *ex = &dev->ex_dev; | |
1127 | int i = 0, end = ex->num_phys; | |
1128 | int res = 0; | |
1129 | ||
1130 | if (0 <= single && single < end) { | |
1131 | i = single; | |
1132 | end = i+1; | |
1133 | } | |
1134 | ||
1135 | for ( ; i < end; i++) { | |
1136 | struct ex_phy *ex_phy = &ex->ex_phy[i]; | |
1137 | ||
1138 | if (ex_phy->phy_state == PHY_VACANT || | |
1139 | ex_phy->phy_state == PHY_NOT_PRESENT || | |
1140 | ex_phy->phy_state == PHY_DEVICE_DISCOVERED) | |
1141 | continue; | |
1142 | ||
1143 | switch (ex_phy->linkrate) { | |
88edf746 JB |
1144 | case SAS_PHY_DISABLED: |
1145 | case SAS_PHY_RESET_PROBLEM: | |
1146 | case SAS_SATA_PORT_SELECTOR: | |
2908d778 JB |
1147 | continue; |
1148 | default: | |
1149 | res = sas_ex_discover_dev(dev, i); | |
1150 | if (res) | |
1151 | break; | |
1152 | continue; | |
1153 | } | |
1154 | } | |
1155 | ||
1156 | if (!res) | |
1157 | sas_check_level_subtractive_boundary(dev); | |
1158 | ||
1159 | return res; | |
1160 | } | |
1161 | ||
1162 | static int sas_check_ex_subtractive_boundary(struct domain_device *dev) | |
1163 | { | |
1164 | struct expander_device *ex = &dev->ex_dev; | |
1165 | int i; | |
1166 | u8 *sub_sas_addr = NULL; | |
1167 | ||
aa9f8328 | 1168 | if (dev->dev_type != SAS_EDGE_EXPANDER_DEVICE) |
2908d778 JB |
1169 | return 0; |
1170 | ||
1171 | for (i = 0; i < ex->num_phys; i++) { | |
1172 | struct ex_phy *phy = &ex->ex_phy[i]; | |
1173 | ||
1174 | if (phy->phy_state == PHY_VACANT || | |
1175 | phy->phy_state == PHY_NOT_PRESENT) | |
1176 | continue; | |
1177 | ||
924a3541 | 1178 | if (dev_is_expander(phy->attached_dev_type) && |
2908d778 JB |
1179 | phy->routing_attr == SUBTRACTIVE_ROUTING) { |
1180 | ||
1181 | if (!sub_sas_addr) | |
1182 | sub_sas_addr = &phy->attached_sas_addr[0]; | |
1183 | else if (SAS_ADDR(sub_sas_addr) != | |
1184 | SAS_ADDR(phy->attached_sas_addr)) { | |
1185 | ||
3c236f8c | 1186 | pr_notice("ex %016llx phy%02d diverges(%016llx) on subtractive boundary(%016llx). Disabled\n", |
15ba7806 JG |
1187 | SAS_ADDR(dev->sas_addr), i, |
1188 | SAS_ADDR(phy->attached_sas_addr), | |
1189 | SAS_ADDR(sub_sas_addr)); | |
2908d778 JB |
1190 | sas_ex_disable_phy(dev, i); |
1191 | } | |
1192 | } | |
1193 | } | |
1194 | return 0; | |
1195 | } | |
1196 | ||
1197 | static void sas_print_parent_topology_bug(struct domain_device *child, | |
1198 | struct ex_phy *parent_phy, | |
1199 | struct ex_phy *child_phy) | |
1200 | { | |
2908d778 | 1201 | static const char *ex_type[] = { |
aa9f8328 JB |
1202 | [SAS_EDGE_EXPANDER_DEVICE] = "edge", |
1203 | [SAS_FANOUT_EXPANDER_DEVICE] = "fanout", | |
2908d778 JB |
1204 | }; |
1205 | struct domain_device *parent = child->parent; | |
1206 | ||
3c236f8c | 1207 | pr_notice("%s ex %016llx phy%02d <--> %s ex %016llx phy%02d has %c:%c routing link!\n", |
71a4a992 JG |
1208 | ex_type[parent->dev_type], |
1209 | SAS_ADDR(parent->sas_addr), | |
1210 | parent_phy->phy_id, | |
2908d778 | 1211 | |
71a4a992 JG |
1212 | ex_type[child->dev_type], |
1213 | SAS_ADDR(child->sas_addr), | |
1214 | child_phy->phy_id, | |
2908d778 | 1215 | |
71a4a992 JG |
1216 | sas_route_char(parent, parent_phy), |
1217 | sas_route_char(child, child_phy)); | |
2908d778 JB |
1218 | } |
1219 | ||
e3be011e JY |
1220 | static bool sas_eeds_valid(struct domain_device *parent, |
1221 | struct domain_device *child) | |
1222 | { | |
1223 | struct sas_discovery *disc = &parent->port->disc; | |
1224 | ||
1225 | return (SAS_ADDR(disc->eeds_a) == SAS_ADDR(parent->sas_addr) || | |
1226 | SAS_ADDR(disc->eeds_a) == SAS_ADDR(child->sas_addr)) && | |
1227 | (SAS_ADDR(disc->eeds_b) == SAS_ADDR(parent->sas_addr) || | |
1228 | SAS_ADDR(disc->eeds_b) == SAS_ADDR(child->sas_addr)); | |
1229 | } | |
1230 | ||
2908d778 | 1231 | static int sas_check_eeds(struct domain_device *child, |
e3be011e JY |
1232 | struct ex_phy *parent_phy, |
1233 | struct ex_phy *child_phy) | |
2908d778 JB |
1234 | { |
1235 | int res = 0; | |
1236 | struct domain_device *parent = child->parent; | |
e3be011e | 1237 | struct sas_discovery *disc = &parent->port->disc; |
2908d778 | 1238 | |
e3be011e | 1239 | if (SAS_ADDR(disc->fanout_sas_addr) != 0) { |
2908d778 | 1240 | res = -ENODEV; |
3c236f8c | 1241 | pr_warn("edge ex %016llx phy S:%02d <--> edge ex %016llx phy S:%02d, while there is a fanout ex %016llx\n", |
15ba7806 JG |
1242 | SAS_ADDR(parent->sas_addr), |
1243 | parent_phy->phy_id, | |
1244 | SAS_ADDR(child->sas_addr), | |
1245 | child_phy->phy_id, | |
e3be011e JY |
1246 | SAS_ADDR(disc->fanout_sas_addr)); |
1247 | } else if (SAS_ADDR(disc->eeds_a) == 0) { | |
1248 | memcpy(disc->eeds_a, parent->sas_addr, SAS_ADDR_SIZE); | |
1249 | memcpy(disc->eeds_b, child->sas_addr, SAS_ADDR_SIZE); | |
1250 | } else if (!sas_eeds_valid(parent, child)) { | |
2908d778 | 1251 | res = -ENODEV; |
3c236f8c | 1252 | pr_warn("edge ex %016llx phy%02d <--> edge ex %016llx phy%02d link forms a third EEDS!\n", |
15ba7806 JG |
1253 | SAS_ADDR(parent->sas_addr), |
1254 | parent_phy->phy_id, | |
1255 | SAS_ADDR(child->sas_addr), | |
1256 | child_phy->phy_id); | |
2908d778 JB |
1257 | } |
1258 | ||
1259 | return res; | |
1260 | } | |
1261 | ||
ba9be7e7 JY |
1262 | static int sas_check_edge_expander_topo(struct domain_device *child, |
1263 | struct ex_phy *parent_phy) | |
1264 | { | |
1265 | struct expander_device *child_ex = &child->ex_dev; | |
1266 | struct expander_device *parent_ex = &child->parent->ex_dev; | |
1267 | struct ex_phy *child_phy; | |
1268 | ||
1269 | child_phy = &child_ex->ex_phy[parent_phy->attached_phy_id]; | |
1270 | ||
1271 | if (child->dev_type == SAS_FANOUT_EXPANDER_DEVICE) { | |
1272 | if (parent_phy->routing_attr != SUBTRACTIVE_ROUTING || | |
1273 | child_phy->routing_attr != TABLE_ROUTING) | |
1274 | goto error; | |
1275 | } else if (parent_phy->routing_attr == SUBTRACTIVE_ROUTING) { | |
1276 | if (child_phy->routing_attr == SUBTRACTIVE_ROUTING) | |
1277 | return sas_check_eeds(child, parent_phy, child_phy); | |
1278 | else if (child_phy->routing_attr != TABLE_ROUTING) | |
1279 | goto error; | |
1280 | } else if (parent_phy->routing_attr == TABLE_ROUTING) { | |
1281 | if (child_phy->routing_attr != SUBTRACTIVE_ROUTING && | |
1282 | (child_phy->routing_attr != TABLE_ROUTING || | |
1283 | !child_ex->t2t_supp || !parent_ex->t2t_supp)) | |
1284 | goto error; | |
1285 | } | |
1286 | ||
1287 | return 0; | |
1288 | error: | |
1289 | sas_print_parent_topology_bug(child, parent_phy, child_phy); | |
1290 | return -ENODEV; | |
1291 | } | |
1292 | ||
cf3cd61e JY |
1293 | static int sas_check_fanout_expander_topo(struct domain_device *child, |
1294 | struct ex_phy *parent_phy) | |
2908d778 JB |
1295 | { |
1296 | struct expander_device *child_ex = &child->ex_dev; | |
cf3cd61e JY |
1297 | struct ex_phy *child_phy; |
1298 | ||
1299 | child_phy = &child_ex->ex_phy[parent_phy->attached_phy_id]; | |
1300 | ||
1301 | if (parent_phy->routing_attr == TABLE_ROUTING && | |
1302 | child_phy->routing_attr == SUBTRACTIVE_ROUTING) | |
1303 | return 0; | |
1304 | ||
1305 | sas_print_parent_topology_bug(child, parent_phy, child_phy); | |
1306 | ||
1307 | return -ENODEV; | |
1308 | } | |
1309 | ||
1310 | static int sas_check_parent_topology(struct domain_device *child) | |
1311 | { | |
2908d778 JB |
1312 | struct expander_device *parent_ex; |
1313 | int i; | |
1314 | int res = 0; | |
1315 | ||
1316 | if (!child->parent) | |
1317 | return 0; | |
1318 | ||
924a3541 | 1319 | if (!dev_is_expander(child->parent->dev_type)) |
2908d778 JB |
1320 | return 0; |
1321 | ||
1322 | parent_ex = &child->parent->ex_dev; | |
1323 | ||
1324 | for (i = 0; i < parent_ex->num_phys; i++) { | |
1325 | struct ex_phy *parent_phy = &parent_ex->ex_phy[i]; | |
2908d778 JB |
1326 | |
1327 | if (parent_phy->phy_state == PHY_VACANT || | |
1328 | parent_phy->phy_state == PHY_NOT_PRESENT) | |
1329 | continue; | |
1330 | ||
ad74d1da | 1331 | if (!sas_phy_match_dev_addr(child, parent_phy)) |
2908d778 JB |
1332 | continue; |
1333 | ||
2908d778 | 1334 | switch (child->parent->dev_type) { |
aa9f8328 | 1335 | case SAS_EDGE_EXPANDER_DEVICE: |
ba9be7e7 JY |
1336 | if (sas_check_edge_expander_topo(child, parent_phy)) |
1337 | res = -ENODEV; | |
2908d778 | 1338 | break; |
aa9f8328 | 1339 | case SAS_FANOUT_EXPANDER_DEVICE: |
cf3cd61e | 1340 | if (sas_check_fanout_expander_topo(child, parent_phy)) |
2908d778 | 1341 | res = -ENODEV; |
2908d778 JB |
1342 | break; |
1343 | default: | |
1344 | break; | |
1345 | } | |
1346 | } | |
1347 | ||
1348 | return res; | |
1349 | } | |
1350 | ||
1351 | #define RRI_REQ_SIZE 16 | |
1352 | #define RRI_RESP_SIZE 44 | |
1353 | ||
1354 | static int sas_configure_present(struct domain_device *dev, int phy_id, | |
1355 | u8 *sas_addr, int *index, int *present) | |
1356 | { | |
1357 | int i, res = 0; | |
1358 | struct expander_device *ex = &dev->ex_dev; | |
1359 | struct ex_phy *phy = &ex->ex_phy[phy_id]; | |
1360 | u8 *rri_req; | |
1361 | u8 *rri_resp; | |
1362 | ||
1363 | *present = 0; | |
1364 | *index = 0; | |
1365 | ||
1366 | rri_req = alloc_smp_req(RRI_REQ_SIZE); | |
1367 | if (!rri_req) | |
1368 | return -ENOMEM; | |
1369 | ||
1370 | rri_resp = alloc_smp_resp(RRI_RESP_SIZE); | |
1371 | if (!rri_resp) { | |
1372 | kfree(rri_req); | |
1373 | return -ENOMEM; | |
1374 | } | |
1375 | ||
1376 | rri_req[1] = SMP_REPORT_ROUTE_INFO; | |
1377 | rri_req[9] = phy_id; | |
1378 | ||
1379 | for (i = 0; i < ex->max_route_indexes ; i++) { | |
1380 | *(__be16 *)(rri_req+6) = cpu_to_be16(i); | |
1381 | res = smp_execute_task(dev, rri_req, RRI_REQ_SIZE, rri_resp, | |
1382 | RRI_RESP_SIZE); | |
1383 | if (res) | |
1384 | goto out; | |
1385 | res = rri_resp[2]; | |
1386 | if (res == SMP_RESP_NO_INDEX) { | |
3c236f8c | 1387 | pr_warn("overflow of indexes: dev %016llx phy%02d index 0x%x\n", |
15ba7806 | 1388 | SAS_ADDR(dev->sas_addr), phy_id, i); |
2908d778 JB |
1389 | goto out; |
1390 | } else if (res != SMP_RESP_FUNC_ACC) { | |
3c236f8c | 1391 | pr_notice("%s: dev %016llx phy%02d index 0x%x result 0x%x\n", |
15ba7806 JG |
1392 | __func__, SAS_ADDR(dev->sas_addr), phy_id, |
1393 | i, res); | |
2908d778 JB |
1394 | goto out; |
1395 | } | |
1396 | if (SAS_ADDR(sas_addr) != 0) { | |
1397 | if (SAS_ADDR(rri_resp+16) == SAS_ADDR(sas_addr)) { | |
1398 | *index = i; | |
1399 | if ((rri_resp[12] & 0x80) == 0x80) | |
1400 | *present = 0; | |
1401 | else | |
1402 | *present = 1; | |
1403 | goto out; | |
1404 | } else if (SAS_ADDR(rri_resp+16) == 0) { | |
1405 | *index = i; | |
1406 | *present = 0; | |
1407 | goto out; | |
1408 | } | |
1409 | } else if (SAS_ADDR(rri_resp+16) == 0 && | |
1410 | phy->last_da_index < i) { | |
1411 | phy->last_da_index = i; | |
1412 | *index = i; | |
1413 | *present = 0; | |
1414 | goto out; | |
1415 | } | |
1416 | } | |
1417 | res = -1; | |
1418 | out: | |
1419 | kfree(rri_req); | |
1420 | kfree(rri_resp); | |
1421 | return res; | |
1422 | } | |
1423 | ||
1424 | #define CRI_REQ_SIZE 44 | |
1425 | #define CRI_RESP_SIZE 8 | |
1426 | ||
1427 | static int sas_configure_set(struct domain_device *dev, int phy_id, | |
1428 | u8 *sas_addr, int index, int include) | |
1429 | { | |
1430 | int res; | |
1431 | u8 *cri_req; | |
1432 | u8 *cri_resp; | |
1433 | ||
1434 | cri_req = alloc_smp_req(CRI_REQ_SIZE); | |
1435 | if (!cri_req) | |
1436 | return -ENOMEM; | |
1437 | ||
1438 | cri_resp = alloc_smp_resp(CRI_RESP_SIZE); | |
1439 | if (!cri_resp) { | |
1440 | kfree(cri_req); | |
1441 | return -ENOMEM; | |
1442 | } | |
1443 | ||
1444 | cri_req[1] = SMP_CONF_ROUTE_INFO; | |
1445 | *(__be16 *)(cri_req+6) = cpu_to_be16(index); | |
1446 | cri_req[9] = phy_id; | |
1447 | if (SAS_ADDR(sas_addr) == 0 || !include) | |
1448 | cri_req[12] |= 0x80; | |
1449 | memcpy(cri_req+16, sas_addr, SAS_ADDR_SIZE); | |
1450 | ||
1451 | res = smp_execute_task(dev, cri_req, CRI_REQ_SIZE, cri_resp, | |
1452 | CRI_RESP_SIZE); | |
1453 | if (res) | |
1454 | goto out; | |
1455 | res = cri_resp[2]; | |
1456 | if (res == SMP_RESP_NO_INDEX) { | |
3c236f8c | 1457 | pr_warn("overflow of indexes: dev %016llx phy%02d index 0x%x\n", |
15ba7806 | 1458 | SAS_ADDR(dev->sas_addr), phy_id, index); |
2908d778 JB |
1459 | } |
1460 | out: | |
1461 | kfree(cri_req); | |
1462 | kfree(cri_resp); | |
1463 | return res; | |
1464 | } | |
1465 | ||
1466 | static int sas_configure_phy(struct domain_device *dev, int phy_id, | |
1467 | u8 *sas_addr, int include) | |
1468 | { | |
1469 | int index; | |
1470 | int present; | |
1471 | int res; | |
1472 | ||
1473 | res = sas_configure_present(dev, phy_id, sas_addr, &index, &present); | |
1474 | if (res) | |
1475 | return res; | |
1476 | if (include ^ present) | |
857a80bb LJ |
1477 | return sas_configure_set(dev, phy_id, sas_addr, index, |
1478 | include); | |
2908d778 JB |
1479 | |
1480 | return res; | |
1481 | } | |
1482 | ||
1483 | /** | |
121246ae BVA |
1484 | * sas_configure_parent - configure routing table of parent |
1485 | * @parent: parent expander | |
1486 | * @child: child expander | |
1487 | * @sas_addr: SAS port identifier of device directly attached to child | |
1488 | * @include: whether or not to include @child in the expander routing table | |
2908d778 JB |
1489 | */ |
1490 | static int sas_configure_parent(struct domain_device *parent, | |
1491 | struct domain_device *child, | |
1492 | u8 *sas_addr, int include) | |
1493 | { | |
1494 | struct expander_device *ex_parent = &parent->ex_dev; | |
1495 | int res = 0; | |
1496 | int i; | |
1497 | ||
1498 | if (parent->parent) { | |
1499 | res = sas_configure_parent(parent->parent, parent, sas_addr, | |
1500 | include); | |
1501 | if (res) | |
1502 | return res; | |
1503 | } | |
1504 | ||
1505 | if (ex_parent->conf_route_table == 0) { | |
15ba7806 JG |
1506 | pr_debug("ex %016llx has self-configuring routing table\n", |
1507 | SAS_ADDR(parent->sas_addr)); | |
2908d778 JB |
1508 | return 0; |
1509 | } | |
1510 | ||
1511 | for (i = 0; i < ex_parent->num_phys; i++) { | |
1512 | struct ex_phy *phy = &ex_parent->ex_phy[i]; | |
1513 | ||
1514 | if ((phy->routing_attr == TABLE_ROUTING) && | |
ad74d1da | 1515 | sas_phy_match_dev_addr(child, phy)) { |
2908d778 JB |
1516 | res = sas_configure_phy(parent, i, sas_addr, include); |
1517 | if (res) | |
1518 | return res; | |
1519 | } | |
1520 | } | |
1521 | ||
1522 | return res; | |
1523 | } | |
1524 | ||
1525 | /** | |
121246ae BVA |
1526 | * sas_configure_routing - configure routing |
1527 | * @dev: expander device | |
1528 | * @sas_addr: port identifier of device directly attached to the expander device | |
2908d778 JB |
1529 | */ |
1530 | static int sas_configure_routing(struct domain_device *dev, u8 *sas_addr) | |
1531 | { | |
1532 | if (dev->parent) | |
1533 | return sas_configure_parent(dev->parent, dev, sas_addr, 1); | |
1534 | return 0; | |
1535 | } | |
1536 | ||
1537 | static int sas_disable_routing(struct domain_device *dev, u8 *sas_addr) | |
1538 | { | |
1539 | if (dev->parent) | |
1540 | return sas_configure_parent(dev->parent, dev, sas_addr, 0); | |
1541 | return 0; | |
1542 | } | |
1543 | ||
2908d778 | 1544 | /** |
121246ae BVA |
1545 | * sas_discover_expander - expander discovery |
1546 | * @dev: pointer to expander domain device | |
2908d778 JB |
1547 | * |
1548 | * See comment in sas_discover_sata(). | |
1549 | */ | |
1550 | static int sas_discover_expander(struct domain_device *dev) | |
1551 | { | |
1552 | int res; | |
1553 | ||
1554 | res = sas_notify_lldd_dev_found(dev); | |
1555 | if (res) | |
1556 | return res; | |
1557 | ||
1558 | res = sas_ex_general(dev); | |
1559 | if (res) | |
1560 | goto out_err; | |
1561 | res = sas_ex_manuf_info(dev); | |
1562 | if (res) | |
1563 | goto out_err; | |
1564 | ||
1565 | res = sas_expander_discover(dev); | |
1566 | if (res) { | |
15ba7806 JG |
1567 | pr_warn("expander %016llx discovery failed(0x%x)\n", |
1568 | SAS_ADDR(dev->sas_addr), res); | |
2908d778 JB |
1569 | goto out_err; |
1570 | } | |
1571 | ||
1572 | sas_check_ex_subtractive_boundary(dev); | |
1573 | res = sas_check_parent_topology(dev); | |
1574 | if (res) | |
1575 | goto out_err; | |
1576 | return 0; | |
1577 | out_err: | |
1578 | sas_notify_lldd_dev_gone(dev); | |
1579 | return res; | |
1580 | } | |
1581 | ||
1582 | static int sas_ex_level_discovery(struct asd_sas_port *port, const int level) | |
1583 | { | |
1584 | int res = 0; | |
1585 | struct domain_device *dev; | |
1586 | ||
1587 | list_for_each_entry(dev, &port->dev_list, dev_list_node) { | |
924a3541 | 1588 | if (dev_is_expander(dev->dev_type)) { |
2908d778 JB |
1589 | struct sas_expander_device *ex = |
1590 | rphy_to_expander_device(dev->rphy); | |
1591 | ||
1592 | if (level == ex->level) | |
1593 | res = sas_ex_discover_devices(dev, -1); | |
1594 | else if (level > 0) | |
1595 | res = sas_ex_discover_devices(port->port_dev, -1); | |
1596 | ||
1597 | } | |
1598 | } | |
1599 | ||
1600 | return res; | |
1601 | } | |
1602 | ||
1603 | static int sas_ex_bfs_disc(struct asd_sas_port *port) | |
1604 | { | |
1605 | int res; | |
1606 | int level; | |
1607 | ||
1608 | do { | |
1609 | level = port->disc.max_level; | |
1610 | res = sas_ex_level_discovery(port, level); | |
1611 | mb(); | |
1612 | } while (level < port->disc.max_level); | |
1613 | ||
1614 | return res; | |
1615 | } | |
1616 | ||
1617 | int sas_discover_root_expander(struct domain_device *dev) | |
1618 | { | |
1619 | int res; | |
1620 | struct sas_expander_device *ex = rphy_to_expander_device(dev->rphy); | |
1621 | ||
bf451207 DW |
1622 | res = sas_rphy_add(dev->rphy); |
1623 | if (res) | |
1624 | goto out_err; | |
2908d778 JB |
1625 | |
1626 | ex->level = dev->port->disc.max_level; /* 0 */ | |
1627 | res = sas_discover_expander(dev); | |
bf451207 DW |
1628 | if (res) |
1629 | goto out_err2; | |
1630 | ||
1631 | sas_ex_bfs_disc(dev->port); | |
2908d778 JB |
1632 | |
1633 | return res; | |
bf451207 DW |
1634 | |
1635 | out_err2: | |
6f63caae | 1636 | sas_rphy_remove(dev->rphy); |
bf451207 | 1637 | out_err: |
bf451207 | 1638 | return res; |
2908d778 JB |
1639 | } |
1640 | ||
1641 | /* ---------- Domain revalidation ---------- */ | |
1642 | ||
a5734527 XY |
1643 | static void sas_get_sas_addr_and_dev_type(struct smp_disc_resp *disc_resp, |
1644 | u8 *sas_addr, | |
1645 | enum sas_device_type *type) | |
1646 | { | |
1647 | memcpy(sas_addr, disc_resp->disc.attached_sas_addr, SAS_ADDR_SIZE); | |
1648 | *type = to_dev_type(&disc_resp->disc); | |
1649 | if (*type == SAS_PHY_UNUSED) | |
1650 | memset(sas_addr, 0, SAS_ADDR_SIZE); | |
1651 | } | |
1652 | ||
2908d778 | 1653 | static int sas_get_phy_discover(struct domain_device *dev, |
c3752f44 | 1654 | int phy_id, struct smp_disc_resp *disc_resp) |
2908d778 JB |
1655 | { |
1656 | int res; | |
1657 | u8 *disc_req; | |
1658 | ||
1659 | disc_req = alloc_smp_req(DISCOVER_REQ_SIZE); | |
1660 | if (!disc_req) | |
1661 | return -ENOMEM; | |
1662 | ||
1663 | disc_req[1] = SMP_DISCOVER; | |
1664 | disc_req[9] = phy_id; | |
1665 | ||
1666 | res = smp_execute_task(dev, disc_req, DISCOVER_REQ_SIZE, | |
1667 | disc_resp, DISCOVER_RESP_SIZE); | |
1668 | if (res) | |
1669 | goto out; | |
c3752f44 | 1670 | if (disc_resp->result != SMP_RESP_FUNC_ACC) |
2908d778 | 1671 | res = disc_resp->result; |
2908d778 JB |
1672 | out: |
1673 | kfree(disc_req); | |
1674 | return res; | |
1675 | } | |
1676 | ||
1677 | static int sas_get_phy_change_count(struct domain_device *dev, | |
1678 | int phy_id, int *pcc) | |
1679 | { | |
1680 | int res; | |
c3752f44 | 1681 | struct smp_disc_resp *disc_resp; |
2908d778 JB |
1682 | |
1683 | disc_resp = alloc_smp_resp(DISCOVER_RESP_SIZE); | |
1684 | if (!disc_resp) | |
1685 | return -ENOMEM; | |
1686 | ||
1687 | res = sas_get_phy_discover(dev, phy_id, disc_resp); | |
1688 | if (!res) | |
1689 | *pcc = disc_resp->disc.change_count; | |
1690 | ||
1691 | kfree(disc_resp); | |
1692 | return res; | |
1693 | } | |
1694 | ||
9181ce3c JZ |
1695 | int sas_get_phy_attached_dev(struct domain_device *dev, int phy_id, |
1696 | u8 *sas_addr, enum sas_device_type *type) | |
2908d778 JB |
1697 | { |
1698 | int res; | |
c3752f44 | 1699 | struct smp_disc_resp *disc_resp; |
2908d778 JB |
1700 | |
1701 | disc_resp = alloc_smp_resp(DISCOVER_RESP_SIZE); | |
1702 | if (!disc_resp) | |
1703 | return -ENOMEM; | |
2908d778 JB |
1704 | |
1705 | res = sas_get_phy_discover(dev, phy_id, disc_resp); | |
a5734527 XY |
1706 | if (res == 0) |
1707 | sas_get_sas_addr_and_dev_type(disc_resp, sas_addr, type); | |
2908d778 JB |
1708 | kfree(disc_resp); |
1709 | return res; | |
1710 | } | |
1711 | ||
1712 | static int sas_find_bcast_phy(struct domain_device *dev, int *phy_id, | |
19252de6 | 1713 | int from_phy, bool update) |
2908d778 JB |
1714 | { |
1715 | struct expander_device *ex = &dev->ex_dev; | |
1716 | int res = 0; | |
1717 | int i; | |
1718 | ||
1719 | for (i = from_phy; i < ex->num_phys; i++) { | |
1720 | int phy_change_count = 0; | |
1721 | ||
1722 | res = sas_get_phy_change_count(dev, i, &phy_change_count); | |
1699490d TJ |
1723 | switch (res) { |
1724 | case SMP_RESP_PHY_VACANT: | |
1725 | case SMP_RESP_NO_PHY: | |
1726 | continue; | |
1727 | case SMP_RESP_FUNC_ACC: | |
1728 | break; | |
1729 | default: | |
1730 | return res; | |
1731 | } | |
1732 | ||
1733 | if (phy_change_count != ex->ex_phy[i].phy_change_count) { | |
19252de6 TP |
1734 | if (update) |
1735 | ex->ex_phy[i].phy_change_count = | |
1736 | phy_change_count; | |
2908d778 JB |
1737 | *phy_id = i; |
1738 | return 0; | |
1739 | } | |
1740 | } | |
1699490d | 1741 | return 0; |
2908d778 JB |
1742 | } |
1743 | ||
1744 | static int sas_get_ex_change_count(struct domain_device *dev, int *ecc) | |
1745 | { | |
1746 | int res; | |
1747 | u8 *rg_req; | |
44f2bfe9 | 1748 | struct smp_rg_resp *rg_resp; |
2908d778 JB |
1749 | |
1750 | rg_req = alloc_smp_req(RG_REQ_SIZE); | |
1751 | if (!rg_req) | |
1752 | return -ENOMEM; | |
1753 | ||
1754 | rg_resp = alloc_smp_resp(RG_RESP_SIZE); | |
1755 | if (!rg_resp) { | |
1756 | kfree(rg_req); | |
1757 | return -ENOMEM; | |
1758 | } | |
1759 | ||
1760 | rg_req[1] = SMP_REPORT_GENERAL; | |
1761 | ||
1762 | res = smp_execute_task(dev, rg_req, RG_REQ_SIZE, rg_resp, | |
1763 | RG_RESP_SIZE); | |
1764 | if (res) | |
1765 | goto out; | |
1766 | if (rg_resp->result != SMP_RESP_FUNC_ACC) { | |
1767 | res = rg_resp->result; | |
1768 | goto out; | |
1769 | } | |
1770 | ||
1771 | *ecc = be16_to_cpu(rg_resp->rg.change_count); | |
1772 | out: | |
1773 | kfree(rg_resp); | |
1774 | kfree(rg_req); | |
1775 | return res; | |
1776 | } | |
19252de6 TP |
1777 | /** |
1778 | * sas_find_bcast_dev - find the device issue BROADCAST(CHANGE). | |
1779 | * @dev:domain device to be detect. | |
1780 | * @src_dev: the device which originated BROADCAST(CHANGE). | |
1781 | * | |
02582e9b | 1782 | * Add self-configuration expander support. Suppose two expander cascading, |
19252de6 TP |
1783 | * when the first level expander is self-configuring, hotplug the disks in |
1784 | * second level expander, BROADCAST(CHANGE) will not only be originated | |
1785 | * in the second level expander, but also be originated in the first level | |
1786 | * expander (see SAS protocol SAS 2r-14, 7.11 for detail), it is to say, | |
1787 | * expander changed count in two level expanders will all increment at least | |
1788 | * once, but the phy which chang count has changed is the source device which | |
1789 | * we concerned. | |
1790 | */ | |
2908d778 JB |
1791 | |
1792 | static int sas_find_bcast_dev(struct domain_device *dev, | |
1793 | struct domain_device **src_dev) | |
1794 | { | |
1795 | struct expander_device *ex = &dev->ex_dev; | |
1796 | int ex_change_count = -1; | |
19252de6 | 1797 | int phy_id = -1; |
2908d778 | 1798 | int res; |
19252de6 | 1799 | struct domain_device *ch; |
2908d778 JB |
1800 | |
1801 | res = sas_get_ex_change_count(dev, &ex_change_count); | |
1802 | if (res) | |
1803 | goto out; | |
19252de6 TP |
1804 | if (ex_change_count != -1 && ex_change_count != ex->ex_change_count) { |
1805 | /* Just detect if this expander phys phy change count changed, | |
1806 | * in order to determine if this expander originate BROADCAST, | |
1807 | * and do not update phy change count field in our structure. | |
1808 | */ | |
1809 | res = sas_find_bcast_phy(dev, &phy_id, 0, false); | |
1810 | if (phy_id != -1) { | |
1811 | *src_dev = dev; | |
1812 | ex->ex_change_count = ex_change_count; | |
3c236f8c | 1813 | pr_info("ex %016llx phy%02d change count has changed\n", |
a5b38d31 | 1814 | SAS_ADDR(dev->sas_addr), phy_id); |
19252de6 TP |
1815 | return res; |
1816 | } else | |
a5b38d31 JG |
1817 | pr_info("ex %016llx phys DID NOT change\n", |
1818 | SAS_ADDR(dev->sas_addr)); | |
19252de6 TP |
1819 | } |
1820 | list_for_each_entry(ch, &ex->children, siblings) { | |
924a3541 | 1821 | if (dev_is_expander(ch->dev_type)) { |
19252de6 | 1822 | res = sas_find_bcast_dev(ch, src_dev); |
24926dad | 1823 | if (*src_dev) |
19252de6 | 1824 | return res; |
2908d778 JB |
1825 | } |
1826 | } | |
1827 | out: | |
1828 | return res; | |
1829 | } | |
1830 | ||
1a34c064 | 1831 | static void sas_unregister_ex_tree(struct asd_sas_port *port, struct domain_device *dev) |
2908d778 JB |
1832 | { |
1833 | struct expander_device *ex = &dev->ex_dev; | |
1834 | struct domain_device *child, *n; | |
1835 | ||
1836 | list_for_each_entry_safe(child, n, &ex->children, siblings) { | |
e139942d | 1837 | set_bit(SAS_DEV_GONE, &child->state); |
924a3541 | 1838 | if (dev_is_expander(child->dev_type)) |
1a34c064 | 1839 | sas_unregister_ex_tree(port, child); |
2908d778 | 1840 | else |
1a34c064 | 1841 | sas_unregister_dev(port, child); |
2908d778 | 1842 | } |
1a34c064 | 1843 | sas_unregister_dev(port, dev); |
2908d778 JB |
1844 | } |
1845 | ||
1846 | static void sas_unregister_devs_sas_addr(struct domain_device *parent, | |
19252de6 | 1847 | int phy_id, bool last) |
2908d778 JB |
1848 | { |
1849 | struct expander_device *ex_dev = &parent->ex_dev; | |
1850 | struct ex_phy *phy = &ex_dev->ex_phy[phy_id]; | |
f41a0c44 | 1851 | struct domain_device *child, *n, *found = NULL; |
19252de6 TP |
1852 | if (last) { |
1853 | list_for_each_entry_safe(child, n, | |
1854 | &ex_dev->children, siblings) { | |
ad74d1da | 1855 | if (sas_phy_match_dev_addr(child, phy)) { |
e139942d | 1856 | set_bit(SAS_DEV_GONE, &child->state); |
924a3541 | 1857 | if (dev_is_expander(child->dev_type)) |
1a34c064 | 1858 | sas_unregister_ex_tree(parent->port, child); |
19252de6 | 1859 | else |
1a34c064 | 1860 | sas_unregister_dev(parent->port, child); |
f41a0c44 | 1861 | found = child; |
19252de6 TP |
1862 | break; |
1863 | } | |
2908d778 | 1864 | } |
19252de6 | 1865 | sas_disable_routing(parent, phy->attached_sas_addr); |
2908d778 | 1866 | } |
2908d778 | 1867 | memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE); |
a73914c3 MS |
1868 | if (phy->port) { |
1869 | sas_port_delete_phy(phy->port, phy->phy); | |
f41a0c44 | 1870 | sas_device_set_phy(found, phy->port); |
7a165a81 | 1871 | if (phy->port->num_phys == 0) { |
0558f33c JY |
1872 | list_add_tail(&phy->port->del_list, |
1873 | &parent->port->sas_port_del_list); | |
7a165a81 XY |
1874 | if (ex_dev->parent_port == phy->port) |
1875 | ex_dev->parent_port = NULL; | |
1876 | } | |
a73914c3 MS |
1877 | phy->port = NULL; |
1878 | } | |
2908d778 JB |
1879 | } |
1880 | ||
1881 | static int sas_discover_bfs_by_root_level(struct domain_device *root, | |
1882 | const int level) | |
1883 | { | |
1884 | struct expander_device *ex_root = &root->ex_dev; | |
1885 | struct domain_device *child; | |
1886 | int res = 0; | |
1887 | ||
1888 | list_for_each_entry(child, &ex_root->children, siblings) { | |
924a3541 | 1889 | if (dev_is_expander(child->dev_type)) { |
2908d778 JB |
1890 | struct sas_expander_device *ex = |
1891 | rphy_to_expander_device(child->rphy); | |
1892 | ||
1893 | if (level > ex->level) | |
1894 | res = sas_discover_bfs_by_root_level(child, | |
1895 | level); | |
1896 | else if (level == ex->level) | |
1897 | res = sas_ex_discover_devices(child, -1); | |
1898 | } | |
1899 | } | |
1900 | return res; | |
1901 | } | |
1902 | ||
1903 | static int sas_discover_bfs_by_root(struct domain_device *dev) | |
1904 | { | |
1905 | int res; | |
1906 | struct sas_expander_device *ex = rphy_to_expander_device(dev->rphy); | |
1907 | int level = ex->level+1; | |
1908 | ||
1909 | res = sas_ex_discover_devices(dev, -1); | |
1910 | if (res) | |
1911 | goto out; | |
1912 | do { | |
1913 | res = sas_discover_bfs_by_root_level(dev, level); | |
1914 | mb(); | |
1915 | level += 1; | |
1916 | } while (level <= dev->port->disc.max_level); | |
1917 | out: | |
1918 | return res; | |
1919 | } | |
1920 | ||
1921 | static int sas_discover_new(struct domain_device *dev, int phy_id) | |
1922 | { | |
1923 | struct ex_phy *ex_phy = &dev->ex_dev.ex_phy[phy_id]; | |
1924 | struct domain_device *child; | |
b17caa17 | 1925 | int res; |
2908d778 | 1926 | |
3c236f8c | 1927 | pr_debug("ex %016llx phy%02d new device attached\n", |
15ba7806 | 1928 | SAS_ADDR(dev->sas_addr), phy_id); |
2908d778 JB |
1929 | res = sas_ex_phy_discover(dev, phy_id); |
1930 | if (res) | |
b17caa17 DW |
1931 | return res; |
1932 | ||
1933 | if (sas_ex_join_wide_port(dev, phy_id)) | |
19252de6 | 1934 | return 0; |
b17caa17 | 1935 | |
2908d778 | 1936 | res = sas_ex_discover_devices(dev, phy_id); |
b17caa17 DW |
1937 | if (res) |
1938 | return res; | |
2908d778 | 1939 | list_for_each_entry(child, &dev->ex_dev.children, siblings) { |
ad74d1da | 1940 | if (sas_phy_match_dev_addr(child, ex_phy)) { |
924a3541 | 1941 | if (dev_is_expander(child->dev_type)) |
2908d778 JB |
1942 | res = sas_discover_bfs_by_root(child); |
1943 | break; | |
1944 | } | |
1945 | } | |
2908d778 JB |
1946 | return res; |
1947 | } | |
1948 | ||
aa9f8328 | 1949 | static bool dev_type_flutter(enum sas_device_type new, enum sas_device_type old) |
354cf829 DW |
1950 | { |
1951 | if (old == new) | |
1952 | return true; | |
1953 | ||
1954 | /* treat device directed resets as flutter, if we went | |
aa9f8328 | 1955 | * SAS_END_DEVICE to SAS_SATA_PENDING the link needs recovery |
354cf829 | 1956 | */ |
aa9f8328 JB |
1957 | if ((old == SAS_SATA_PENDING && new == SAS_END_DEVICE) || |
1958 | (old == SAS_END_DEVICE && new == SAS_SATA_PENDING)) | |
354cf829 DW |
1959 | return true; |
1960 | ||
1961 | return false; | |
1962 | } | |
1963 | ||
a5b38d31 JG |
1964 | static int sas_rediscover_dev(struct domain_device *dev, int phy_id, |
1965 | bool last, int sibling) | |
2908d778 JB |
1966 | { |
1967 | struct expander_device *ex = &dev->ex_dev; | |
1968 | struct ex_phy *phy = &ex->ex_phy[phy_id]; | |
aa9f8328 | 1969 | enum sas_device_type type = SAS_PHY_UNUSED; |
8e68a458 | 1970 | struct smp_disc_resp *disc_resp; |
7b27c5fe | 1971 | u8 sas_addr[SAS_ADDR_SIZE]; |
a5b38d31 | 1972 | char msg[80] = ""; |
2908d778 JB |
1973 | int res; |
1974 | ||
a5b38d31 | 1975 | if (!last) |
3c236f8c | 1976 | sprintf(msg, ", part of a wide port with phy%02d", sibling); |
a5b38d31 | 1977 | |
3c236f8c JG |
1978 | pr_debug("ex %016llx rediscovering phy%02d%s\n", |
1979 | SAS_ADDR(dev->sas_addr), phy_id, msg); | |
a5b38d31 | 1980 | |
7b27c5fe | 1981 | memset(sas_addr, 0, SAS_ADDR_SIZE); |
8e68a458 XY |
1982 | disc_resp = alloc_smp_resp(DISCOVER_RESP_SIZE); |
1983 | if (!disc_resp) | |
1984 | return -ENOMEM; | |
1985 | ||
1986 | res = sas_get_phy_discover(dev, phy_id, disc_resp); | |
2908d778 JB |
1987 | switch (res) { |
1988 | case SMP_RESP_NO_PHY: | |
1989 | phy->phy_state = PHY_NOT_PRESENT; | |
19252de6 | 1990 | sas_unregister_devs_sas_addr(dev, phy_id, last); |
8e68a458 | 1991 | goto out_free_resp; |
2908d778 JB |
1992 | case SMP_RESP_PHY_VACANT: |
1993 | phy->phy_state = PHY_VACANT; | |
19252de6 | 1994 | sas_unregister_devs_sas_addr(dev, phy_id, last); |
8e68a458 | 1995 | goto out_free_resp; |
2908d778 JB |
1996 | case SMP_RESP_FUNC_ACC: |
1997 | break; | |
b2311a28 JS |
1998 | case -ECOMM: |
1999 | break; | |
2000 | default: | |
8e68a458 | 2001 | goto out_free_resp; |
2908d778 JB |
2002 | } |
2003 | ||
8e68a458 XY |
2004 | if (res == 0) |
2005 | sas_get_sas_addr_and_dev_type(disc_resp, sas_addr, &type); | |
2006 | ||
b2311a28 | 2007 | if ((SAS_ADDR(sas_addr) == 0) || (res == -ECOMM)) { |
2908d778 | 2008 | phy->phy_state = PHY_EMPTY; |
19252de6 | 2009 | sas_unregister_devs_sas_addr(dev, phy_id, last); |
d8649fc1 | 2010 | /* |
8e68a458 XY |
2011 | * Even though the PHY is empty, for convenience we update |
2012 | * the PHY info, like negotiated linkrate. | |
d8649fc1 | 2013 | */ |
8e68a458 XY |
2014 | if (res == 0) |
2015 | sas_set_ex_phy(dev, phy_id, disc_resp); | |
2016 | goto out_free_resp; | |
354cf829 DW |
2017 | } else if (SAS_ADDR(sas_addr) == SAS_ADDR(phy->attached_sas_addr) && |
2018 | dev_type_flutter(type, phy->attached_dev_type)) { | |
2019 | struct domain_device *ata_dev = sas_ex_to_ata(dev, phy_id); | |
2020 | char *action = ""; | |
2021 | ||
a01e70e5 | 2022 | sas_ex_phy_discover(dev, phy_id); |
354cf829 | 2023 | |
aa9f8328 | 2024 | if (ata_dev && phy->attached_dev_type == SAS_SATA_PENDING) |
354cf829 | 2025 | action = ", needs recovery"; |
3c236f8c | 2026 | pr_debug("ex %016llx phy%02d broadcast flutter%s\n", |
15ba7806 | 2027 | SAS_ADDR(dev->sas_addr), phy_id, action); |
8e68a458 | 2028 | goto out_free_resp; |
354cf829 DW |
2029 | } |
2030 | ||
32c850bf | 2031 | /* we always have to delete the old device when we went here */ |
3c236f8c | 2032 | pr_info("ex %016llx phy%02d replace %016llx\n", |
15ba7806 JG |
2033 | SAS_ADDR(dev->sas_addr), phy_id, |
2034 | SAS_ADDR(phy->attached_sas_addr)); | |
32c850bf | 2035 | sas_unregister_devs_sas_addr(dev, phy_id, last); |
c666aae6 | 2036 | |
8e68a458 XY |
2037 | res = sas_discover_new(dev, phy_id); |
2038 | out_free_resp: | |
2039 | kfree(disc_resp); | |
2040 | return res; | |
2908d778 JB |
2041 | } |
2042 | ||
19252de6 TP |
2043 | /** |
2044 | * sas_rediscover - revalidate the domain. | |
2045 | * @dev:domain device to be detect. | |
2046 | * @phy_id: the phy id will be detected. | |
2047 | * | |
2048 | * NOTE: this process _must_ quit (return) as soon as any connection | |
2049 | * errors are encountered. Connection recovery is done elsewhere. | |
2050 | * Discover process only interrogates devices in order to discover the | |
2051 | * domain.For plugging out, we un-register the device only when it is | |
2052 | * the last phy in the port, for other phys in this port, we just delete it | |
2053 | * from the port.For inserting, we do discovery when it is the | |
2054 | * first phy,for other phys in this port, we add it to the port to | |
2055 | * forming the wide-port. | |
2056 | */ | |
2908d778 JB |
2057 | static int sas_rediscover(struct domain_device *dev, const int phy_id) |
2058 | { | |
2059 | struct expander_device *ex = &dev->ex_dev; | |
2060 | struct ex_phy *changed_phy = &ex->ex_phy[phy_id]; | |
2061 | int res = 0; | |
2062 | int i; | |
19252de6 | 2063 | bool last = true; /* is this the last phy of the port */ |
2908d778 | 2064 | |
3c236f8c | 2065 | pr_debug("ex %016llx phy%02d originated BROADCAST(CHANGE)\n", |
15ba7806 | 2066 | SAS_ADDR(dev->sas_addr), phy_id); |
2908d778 JB |
2067 | |
2068 | if (SAS_ADDR(changed_phy->attached_sas_addr) != 0) { | |
2069 | for (i = 0; i < ex->num_phys; i++) { | |
2070 | struct ex_phy *phy = &ex->ex_phy[i]; | |
2071 | ||
2072 | if (i == phy_id) | |
2073 | continue; | |
bfa22905 | 2074 | if (sas_phy_addr_match(phy, changed_phy)) { |
19252de6 TP |
2075 | last = false; |
2076 | break; | |
2908d778 JB |
2077 | } |
2078 | } | |
a5b38d31 | 2079 | res = sas_rediscover_dev(dev, phy_id, last, i); |
2908d778 JB |
2080 | } else |
2081 | res = sas_discover_new(dev, phy_id); | |
2908d778 JB |
2082 | return res; |
2083 | } | |
2084 | ||
2085 | /** | |
121246ae BVA |
2086 | * sas_ex_revalidate_domain - revalidate the domain |
2087 | * @port_dev: port domain device. | |
2908d778 JB |
2088 | * |
2089 | * NOTE: this process _must_ quit (return) as soon as any connection | |
2090 | * errors are encountered. Connection recovery is done elsewhere. | |
2091 | * Discover process only interrogates devices in order to discover the | |
2092 | * domain. | |
2093 | */ | |
2094 | int sas_ex_revalidate_domain(struct domain_device *port_dev) | |
2095 | { | |
2096 | int res; | |
2097 | struct domain_device *dev = NULL; | |
2098 | ||
2099 | res = sas_find_bcast_dev(port_dev, &dev); | |
0558f33c | 2100 | if (res == 0 && dev) { |
2908d778 JB |
2101 | struct expander_device *ex = &dev->ex_dev; |
2102 | int i = 0, phy_id; | |
2103 | ||
2104 | do { | |
2105 | phy_id = -1; | |
19252de6 | 2106 | res = sas_find_bcast_phy(dev, &phy_id, i, true); |
2908d778 JB |
2107 | if (phy_id == -1) |
2108 | break; | |
2109 | res = sas_rediscover(dev, phy_id); | |
2110 | i = phy_id + 1; | |
2111 | } while (i < ex->num_phys); | |
2112 | } | |
2908d778 JB |
2113 | return res; |
2114 | } | |
2115 | ||
2d08f329 JY |
2116 | int sas_find_attached_phy_id(struct expander_device *ex_dev, |
2117 | struct domain_device *dev) | |
2118 | { | |
2119 | struct ex_phy *phy; | |
2120 | int phy_id; | |
2121 | ||
2122 | for (phy_id = 0; phy_id < ex_dev->num_phys; phy_id++) { | |
2123 | phy = &ex_dev->ex_phy[phy_id]; | |
2124 | if (sas_phy_match_dev_addr(dev, phy)) | |
2125 | return phy_id; | |
2126 | } | |
2127 | ||
2128 | return -ENODEV; | |
2129 | } | |
2130 | EXPORT_SYMBOL_GPL(sas_find_attached_phy_id); | |
2131 | ||
651a0136 CH |
2132 | void sas_smp_handler(struct bsg_job *job, struct Scsi_Host *shost, |
2133 | struct sas_rphy *rphy) | |
ba1fc175 FT |
2134 | { |
2135 | struct domain_device *dev; | |
621f6401 | 2136 | unsigned int rcvlen = 0; |
651a0136 | 2137 | int ret = -EINVAL; |
ba1fc175 | 2138 | |
2cd614c8 | 2139 | /* no rphy means no smp target support (ie aic94xx host) */ |
b98e66fa | 2140 | if (!rphy) |
651a0136 | 2141 | return sas_smp_host_handler(job, shost); |
ba1fc175 | 2142 | |
651a0136 CH |
2143 | switch (rphy->identify.device_type) { |
2144 | case SAS_EDGE_EXPANDER_DEVICE: | |
2145 | case SAS_FANOUT_EXPANDER_DEVICE: | |
2146 | break; | |
2147 | default: | |
15ba7806 | 2148 | pr_err("%s: can we send a smp request to a device?\n", |
cadbd4a5 | 2149 | __func__); |
651a0136 | 2150 | goto out; |
ba1fc175 FT |
2151 | } |
2152 | ||
2153 | dev = sas_find_dev_by_rphy(rphy); | |
2154 | if (!dev) { | |
15ba7806 | 2155 | pr_err("%s: fail to find a domain_device?\n", __func__); |
651a0136 | 2156 | goto out; |
ba1fc175 FT |
2157 | } |
2158 | ||
2159 | /* do we need to support multiple segments? */ | |
651a0136 CH |
2160 | if (job->request_payload.sg_cnt > 1 || |
2161 | job->reply_payload.sg_cnt > 1) { | |
15ba7806 JG |
2162 | pr_info("%s: multiple segments req %u, rsp %u\n", |
2163 | __func__, job->request_payload.payload_len, | |
2164 | job->reply_payload.payload_len); | |
651a0136 | 2165 | goto out; |
ba1fc175 FT |
2166 | } |
2167 | ||
651a0136 CH |
2168 | ret = smp_execute_task_sg(dev, job->request_payload.sg_list, |
2169 | job->reply_payload.sg_list); | |
621f6401 JY |
2170 | if (ret >= 0) { |
2171 | /* bsg_job_done() requires the length received */ | |
2172 | rcvlen = job->reply_payload.payload_len - ret; | |
2d4b63e1 | 2173 | ret = 0; |
2d4b63e1 | 2174 | } |
ba1fc175 | 2175 | |
651a0136 | 2176 | out: |
621f6401 | 2177 | bsg_job_done(job, ret, rcvlen); |
ba1fc175 | 2178 | } |