0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056 #include "host.h"
0057
0058 #define SCIC_SDS_MPC_RECONFIGURATION_TIMEOUT (10)
0059 #define SCIC_SDS_APC_RECONFIGURATION_TIMEOUT (10)
0060 #define SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION (1000)
0061
0062 enum SCIC_SDS_APC_ACTIVITY {
0063 SCIC_SDS_APC_SKIP_PHY,
0064 SCIC_SDS_APC_ADD_PHY,
0065 SCIC_SDS_APC_START_TIMER,
0066
0067 SCIC_SDS_APC_ACTIVITY_MAX
0068 };
0069
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079
0080
0081
0082
0083
0084
0085
0086 static s32 sci_sas_address_compare(
0087 struct sci_sas_address address_one,
0088 struct sci_sas_address address_two)
0089 {
0090 if (address_one.high > address_two.high) {
0091 return 1;
0092 } else if (address_one.high < address_two.high) {
0093 return -1;
0094 } else if (address_one.low > address_two.low) {
0095 return 1;
0096 } else if (address_one.low < address_two.low) {
0097 return -1;
0098 }
0099
0100
0101 return 0;
0102 }
0103
0104
0105
0106
0107
0108
0109
0110
0111
0112
0113
0114
0115 static struct isci_port *sci_port_configuration_agent_find_port(
0116 struct isci_host *ihost,
0117 struct isci_phy *iphy)
0118 {
0119 u8 i;
0120 struct sci_sas_address port_sas_address;
0121 struct sci_sas_address port_attached_device_address;
0122 struct sci_sas_address phy_sas_address;
0123 struct sci_sas_address phy_attached_device_address;
0124
0125
0126
0127
0128
0129
0130 sci_phy_get_sas_address(iphy, &phy_sas_address);
0131 sci_phy_get_attached_sas_address(iphy, &phy_attached_device_address);
0132
0133 for (i = 0; i < ihost->logical_port_entries; i++) {
0134 struct isci_port *iport = &ihost->ports[i];
0135
0136 sci_port_get_sas_address(iport, &port_sas_address);
0137 sci_port_get_attached_sas_address(iport, &port_attached_device_address);
0138
0139 if (sci_sas_address_compare(port_sas_address, phy_sas_address) == 0 &&
0140 sci_sas_address_compare(port_attached_device_address, phy_attached_device_address) == 0)
0141 return iport;
0142 }
0143
0144 return NULL;
0145 }
0146
0147
0148
0149
0150
0151
0152
0153
0154
0155
0156
0157
0158
0159 static enum sci_status sci_port_configuration_agent_validate_ports(
0160 struct isci_host *ihost,
0161 struct sci_port_configuration_agent *port_agent)
0162 {
0163 struct sci_sas_address first_address;
0164 struct sci_sas_address second_address;
0165
0166
0167
0168
0169 if (port_agent->phy_valid_port_range[0].max_index != 0 ||
0170 port_agent->phy_valid_port_range[1].max_index != 1 ||
0171 port_agent->phy_valid_port_range[2].max_index != 2 ||
0172 port_agent->phy_valid_port_range[3].max_index != 3)
0173 return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
0174
0175
0176
0177
0178 if (port_agent->phy_valid_port_range[0].min_index == 0 &&
0179 port_agent->phy_valid_port_range[1].min_index == 0 &&
0180 port_agent->phy_valid_port_range[2].min_index == 0 &&
0181 port_agent->phy_valid_port_range[3].min_index == 0)
0182 return SCI_SUCCESS;
0183
0184
0185
0186
0187
0188
0189 if (port_agent->phy_valid_port_range[2].min_index == 1) {
0190 return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
0191 }
0192
0193
0194
0195
0196
0197 sci_phy_get_sas_address(&ihost->phys[0], &first_address);
0198 sci_phy_get_sas_address(&ihost->phys[3], &second_address);
0199
0200 if (sci_sas_address_compare(first_address, second_address) == 0) {
0201 return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
0202 }
0203
0204
0205
0206
0207
0208 if (port_agent->phy_valid_port_range[0].min_index == 0 &&
0209 port_agent->phy_valid_port_range[1].min_index == 1) {
0210 sci_phy_get_sas_address(&ihost->phys[0], &first_address);
0211 sci_phy_get_sas_address(&ihost->phys[2], &second_address);
0212
0213 if (sci_sas_address_compare(first_address, second_address) == 0) {
0214 return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
0215 }
0216 }
0217
0218
0219
0220
0221
0222 if (port_agent->phy_valid_port_range[2].min_index == 2 &&
0223 port_agent->phy_valid_port_range[3].min_index == 3) {
0224 sci_phy_get_sas_address(&ihost->phys[1], &first_address);
0225 sci_phy_get_sas_address(&ihost->phys[3], &second_address);
0226
0227 if (sci_sas_address_compare(first_address, second_address) == 0) {
0228 return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
0229 }
0230 }
0231
0232 return SCI_SUCCESS;
0233 }
0234
0235
0236
0237
0238
0239
0240
0241 static enum sci_status
0242 sci_mpc_agent_validate_phy_configuration(struct isci_host *ihost,
0243 struct sci_port_configuration_agent *port_agent)
0244 {
0245 u32 phy_mask;
0246 u32 assigned_phy_mask;
0247 struct sci_sas_address sas_address;
0248 struct sci_sas_address phy_assigned_address;
0249 u8 port_index;
0250 u8 phy_index;
0251
0252 assigned_phy_mask = 0;
0253 sas_address.high = 0;
0254 sas_address.low = 0;
0255
0256 for (port_index = 0; port_index < SCI_MAX_PORTS; port_index++) {
0257 phy_mask = ihost->oem_parameters.ports[port_index].phy_mask;
0258
0259 if (!phy_mask)
0260 continue;
0261
0262
0263
0264 if ((phy_mask & ~assigned_phy_mask) == 0) {
0265 return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
0266 }
0267
0268
0269 for (phy_index = 0; phy_index < SCI_MAX_PHYS; phy_index++) {
0270 if ((phy_mask & (1 << phy_index)) == 0)
0271 continue;
0272 sci_phy_get_sas_address(&ihost->phys[phy_index],
0273 &sas_address);
0274
0275
0276
0277
0278
0279 port_agent->phy_valid_port_range[phy_index].min_index = port_index;
0280 port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
0281
0282 if (phy_index != port_index) {
0283 return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
0284 }
0285
0286 break;
0287 }
0288
0289
0290
0291
0292
0293
0294 for (; phy_index < SCI_MAX_PHYS; phy_index++) {
0295 if ((phy_mask & (1 << phy_index)) == 0)
0296 continue;
0297 sci_phy_get_sas_address(&ihost->phys[phy_index],
0298 &phy_assigned_address);
0299
0300 if (sci_sas_address_compare(sas_address, phy_assigned_address) != 0) {
0301
0302
0303
0304 return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
0305 }
0306
0307 port_agent->phy_valid_port_range[phy_index].min_index = port_index;
0308 port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
0309
0310 sci_port_add_phy(&ihost->ports[port_index],
0311 &ihost->phys[phy_index]);
0312
0313 assigned_phy_mask |= (1 << phy_index);
0314 }
0315
0316 }
0317
0318 return sci_port_configuration_agent_validate_ports(ihost, port_agent);
0319 }
0320
0321 static void mpc_agent_timeout(struct timer_list *t)
0322 {
0323 u8 index;
0324 struct sci_timer *tmr = from_timer(tmr, t, timer);
0325 struct sci_port_configuration_agent *port_agent;
0326 struct isci_host *ihost;
0327 unsigned long flags;
0328 u16 configure_phy_mask;
0329
0330 port_agent = container_of(tmr, typeof(*port_agent), timer);
0331 ihost = container_of(port_agent, typeof(*ihost), port_agent);
0332
0333 spin_lock_irqsave(&ihost->scic_lock, flags);
0334
0335 if (tmr->cancel)
0336 goto done;
0337
0338 port_agent->timer_pending = false;
0339
0340
0341 configure_phy_mask = ~port_agent->phy_configured_mask & port_agent->phy_ready_mask;
0342
0343 for (index = 0; index < SCI_MAX_PHYS; index++) {
0344 struct isci_phy *iphy = &ihost->phys[index];
0345
0346 if (configure_phy_mask & (1 << index)) {
0347 port_agent->link_up_handler(ihost, port_agent,
0348 phy_get_non_dummy_port(iphy),
0349 iphy);
0350 }
0351 }
0352
0353 done:
0354 spin_unlock_irqrestore(&ihost->scic_lock, flags);
0355 }
0356
0357 static void sci_mpc_agent_link_up(struct isci_host *ihost,
0358 struct sci_port_configuration_agent *port_agent,
0359 struct isci_port *iport,
0360 struct isci_phy *iphy)
0361 {
0362
0363
0364
0365
0366 if (!iport)
0367 return;
0368
0369 port_agent->phy_ready_mask |= (1 << iphy->phy_index);
0370 sci_port_link_up(iport, iphy);
0371 if ((iport->active_phy_mask & (1 << iphy->phy_index)))
0372 port_agent->phy_configured_mask |= (1 << iphy->phy_index);
0373 }
0374
0375
0376
0377
0378
0379
0380
0381
0382
0383
0384
0385
0386
0387
0388
0389
0390
0391
0392
0393 static void sci_mpc_agent_link_down(
0394 struct isci_host *ihost,
0395 struct sci_port_configuration_agent *port_agent,
0396 struct isci_port *iport,
0397 struct isci_phy *iphy)
0398 {
0399 if (iport != NULL) {
0400
0401
0402
0403
0404
0405
0406
0407 port_agent->phy_ready_mask &= ~(1 << iphy->phy_index);
0408 port_agent->phy_configured_mask &= ~(1 << iphy->phy_index);
0409
0410
0411
0412
0413
0414
0415
0416 if ((port_agent->phy_configured_mask == 0x0000) &&
0417 (port_agent->phy_ready_mask != 0x0000) &&
0418 !port_agent->timer_pending) {
0419 port_agent->timer_pending = true;
0420
0421 sci_mod_timer(&port_agent->timer,
0422 SCIC_SDS_MPC_RECONFIGURATION_TIMEOUT);
0423 }
0424
0425 sci_port_link_down(iport, iphy);
0426 }
0427 }
0428
0429
0430
0431
0432 static enum sci_status
0433 sci_apc_agent_validate_phy_configuration(struct isci_host *ihost,
0434 struct sci_port_configuration_agent *port_agent)
0435 {
0436 u8 phy_index;
0437 u8 port_index;
0438 struct sci_sas_address sas_address;
0439 struct sci_sas_address phy_assigned_address;
0440
0441 phy_index = 0;
0442
0443 while (phy_index < SCI_MAX_PHYS) {
0444 port_index = phy_index;
0445
0446
0447 sci_phy_get_sas_address(&ihost->phys[phy_index],
0448 &sas_address);
0449
0450 while (++phy_index < SCI_MAX_PHYS) {
0451 sci_phy_get_sas_address(&ihost->phys[phy_index],
0452 &phy_assigned_address);
0453
0454
0455 if (sci_sas_address_compare(sas_address, phy_assigned_address) == 0) {
0456 port_agent->phy_valid_port_range[phy_index].min_index = port_index;
0457 port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
0458 } else {
0459 port_agent->phy_valid_port_range[phy_index].min_index = phy_index;
0460 port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
0461 break;
0462 }
0463 }
0464 }
0465
0466 return sci_port_configuration_agent_validate_ports(ihost, port_agent);
0467 }
0468
0469
0470
0471
0472
0473
0474
0475 static void sci_apc_agent_start_timer(struct sci_port_configuration_agent *port_agent,
0476 u32 timeout)
0477 {
0478 port_agent->timer_pending = true;
0479 sci_mod_timer(&port_agent->timer, timeout);
0480 }
0481
0482 static void sci_apc_agent_configure_ports(struct isci_host *ihost,
0483 struct sci_port_configuration_agent *port_agent,
0484 struct isci_phy *iphy,
0485 bool start_timer)
0486 {
0487 u8 port_index;
0488 enum sci_status status;
0489 struct isci_port *iport;
0490 enum SCIC_SDS_APC_ACTIVITY apc_activity = SCIC_SDS_APC_SKIP_PHY;
0491
0492 iport = sci_port_configuration_agent_find_port(ihost, iphy);
0493
0494 if (iport) {
0495 if (sci_port_is_valid_phy_assignment(iport, iphy->phy_index))
0496 apc_activity = SCIC_SDS_APC_ADD_PHY;
0497 else
0498 apc_activity = SCIC_SDS_APC_SKIP_PHY;
0499 } else {
0500
0501
0502
0503
0504
0505
0506 for (port_index = port_agent->phy_valid_port_range[iphy->phy_index].min_index;
0507 port_index <= port_agent->phy_valid_port_range[iphy->phy_index].max_index;
0508 port_index++) {
0509
0510 iport = &ihost->ports[port_index];
0511
0512
0513 if (sci_port_is_valid_phy_assignment(iport, iphy->phy_index)) {
0514
0515
0516
0517
0518 if (iport->active_phy_mask > (1 << iphy->phy_index)) {
0519 apc_activity = SCIC_SDS_APC_SKIP_PHY;
0520 break;
0521 }
0522
0523
0524
0525
0526
0527 if (iport->physical_port_index == iphy->phy_index) {
0528
0529
0530
0531
0532
0533 if (apc_activity != SCIC_SDS_APC_START_TIMER) {
0534 apc_activity = SCIC_SDS_APC_ADD_PHY;
0535 }
0536
0537 break;
0538 }
0539
0540
0541
0542
0543
0544 if (iport->active_phy_mask == 0) {
0545 apc_activity = SCIC_SDS_APC_START_TIMER;
0546 }
0547 } else if (iport->active_phy_mask != 0) {
0548
0549
0550
0551
0552 apc_activity = SCIC_SDS_APC_SKIP_PHY;
0553 }
0554 }
0555 }
0556
0557
0558
0559
0560
0561
0562
0563
0564 if (
0565 (start_timer == false)
0566 && (apc_activity == SCIC_SDS_APC_START_TIMER)
0567 ) {
0568 apc_activity = SCIC_SDS_APC_ADD_PHY;
0569 }
0570
0571 switch (apc_activity) {
0572 case SCIC_SDS_APC_ADD_PHY:
0573 status = sci_port_add_phy(iport, iphy);
0574
0575 if (status == SCI_SUCCESS) {
0576 port_agent->phy_configured_mask |= (1 << iphy->phy_index);
0577 }
0578 break;
0579
0580 case SCIC_SDS_APC_START_TIMER:
0581 sci_apc_agent_start_timer(port_agent,
0582 SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION);
0583 break;
0584
0585 case SCIC_SDS_APC_SKIP_PHY:
0586 default:
0587
0588 break;
0589 }
0590 }
0591
0592
0593
0594
0595
0596
0597
0598
0599
0600
0601
0602
0603
0604
0605 static void sci_apc_agent_link_up(struct isci_host *ihost,
0606 struct sci_port_configuration_agent *port_agent,
0607 struct isci_port *iport,
0608 struct isci_phy *iphy)
0609 {
0610 u8 phy_index = iphy->phy_index;
0611
0612 if (!iport) {
0613
0614 port_agent->phy_ready_mask |= 1 << phy_index;
0615 sci_apc_agent_start_timer(port_agent,
0616 SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION);
0617 } else {
0618
0619 port_agent->phy_ready_mask |= 1 << phy_index;
0620 sci_port_link_up(iport, iphy);
0621 }
0622 }
0623
0624
0625
0626
0627
0628
0629
0630
0631
0632
0633
0634
0635
0636
0637
0638 static void sci_apc_agent_link_down(
0639 struct isci_host *ihost,
0640 struct sci_port_configuration_agent *port_agent,
0641 struct isci_port *iport,
0642 struct isci_phy *iphy)
0643 {
0644 port_agent->phy_ready_mask &= ~(1 << iphy->phy_index);
0645
0646 if (!iport)
0647 return;
0648 if (port_agent->phy_configured_mask & (1 << iphy->phy_index)) {
0649 enum sci_status status;
0650
0651 status = sci_port_remove_phy(iport, iphy);
0652
0653 if (status == SCI_SUCCESS)
0654 port_agent->phy_configured_mask &= ~(1 << iphy->phy_index);
0655 }
0656 }
0657
0658
0659 static void apc_agent_timeout(struct timer_list *t)
0660 {
0661 u32 index;
0662 struct sci_timer *tmr = from_timer(tmr, t, timer);
0663 struct sci_port_configuration_agent *port_agent;
0664 struct isci_host *ihost;
0665 unsigned long flags;
0666 u16 configure_phy_mask;
0667
0668 port_agent = container_of(tmr, typeof(*port_agent), timer);
0669 ihost = container_of(port_agent, typeof(*ihost), port_agent);
0670
0671 spin_lock_irqsave(&ihost->scic_lock, flags);
0672
0673 if (tmr->cancel)
0674 goto done;
0675
0676 port_agent->timer_pending = false;
0677
0678 configure_phy_mask = ~port_agent->phy_configured_mask & port_agent->phy_ready_mask;
0679
0680 if (!configure_phy_mask)
0681 goto done;
0682
0683 for (index = 0; index < SCI_MAX_PHYS; index++) {
0684 if ((configure_phy_mask & (1 << index)) == 0)
0685 continue;
0686
0687 sci_apc_agent_configure_ports(ihost, port_agent,
0688 &ihost->phys[index], false);
0689 }
0690
0691 if (is_controller_start_complete(ihost))
0692 sci_controller_transition_to_ready(ihost, SCI_SUCCESS);
0693
0694 done:
0695 spin_unlock_irqrestore(&ihost->scic_lock, flags);
0696 }
0697
0698
0699
0700
0701
0702
0703
0704
0705
0706
0707
0708 void sci_port_configuration_agent_construct(
0709 struct sci_port_configuration_agent *port_agent)
0710 {
0711 u32 index;
0712
0713 port_agent->phy_configured_mask = 0x00;
0714 port_agent->phy_ready_mask = 0x00;
0715
0716 port_agent->link_up_handler = NULL;
0717 port_agent->link_down_handler = NULL;
0718
0719 port_agent->timer_pending = false;
0720
0721 for (index = 0; index < SCI_MAX_PORTS; index++) {
0722 port_agent->phy_valid_port_range[index].min_index = 0;
0723 port_agent->phy_valid_port_range[index].max_index = 0;
0724 }
0725 }
0726
0727 bool is_port_config_apc(struct isci_host *ihost)
0728 {
0729 return ihost->port_agent.link_up_handler == sci_apc_agent_link_up;
0730 }
0731
0732 enum sci_status sci_port_configuration_agent_initialize(
0733 struct isci_host *ihost,
0734 struct sci_port_configuration_agent *port_agent)
0735 {
0736 enum sci_status status;
0737 enum sci_port_configuration_mode mode;
0738
0739 mode = ihost->oem_parameters.controller.mode_type;
0740
0741 if (mode == SCIC_PORT_MANUAL_CONFIGURATION_MODE) {
0742 status = sci_mpc_agent_validate_phy_configuration(
0743 ihost, port_agent);
0744
0745 port_agent->link_up_handler = sci_mpc_agent_link_up;
0746 port_agent->link_down_handler = sci_mpc_agent_link_down;
0747
0748 sci_init_timer(&port_agent->timer, mpc_agent_timeout);
0749 } else {
0750 status = sci_apc_agent_validate_phy_configuration(
0751 ihost, port_agent);
0752
0753 port_agent->link_up_handler = sci_apc_agent_link_up;
0754 port_agent->link_down_handler = sci_apc_agent_link_down;
0755
0756 sci_init_timer(&port_agent->timer, apc_agent_timeout);
0757 }
0758
0759 return status;
0760 }