~ubuntu-branches/ubuntu/precise/linux-ti-omap4/precise

« back to all changes in this revision

Viewing changes to drivers/scsi/isci/port_config.c

  • Committer: Bazaar Package Importer
  • Author(s): Paolo Pisati
  • Date: 2011-06-29 15:23:51 UTC
  • mfrom: (26.1.1 natty-proposed)
  • Revision ID: james.westby@ubuntu.com-20110629152351-xs96tm303d95rpbk
Tags: 3.0.0-1200.2
* Rebased against 3.0.0-6.7
* BSP from TI based on 3.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * This file is provided under a dual BSD/GPLv2 license.  When using or
 
3
 * redistributing this file, you may do so under either license.
 
4
 *
 
5
 * GPL LICENSE SUMMARY
 
6
 *
 
7
 * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
 
8
 *
 
9
 * This program is free software; you can redistribute it and/or modify
 
10
 * it under the terms of version 2 of the GNU General Public License as
 
11
 * published by the Free Software Foundation.
 
12
 *
 
13
 * This program is distributed in the hope that it will be useful, but
 
14
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
16
 * General Public License for more details.
 
17
 *
 
18
 * You should have received a copy of the GNU General Public License
 
19
 * along with this program; if not, write to the Free Software
 
20
 * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
 
21
 * The full GNU General Public License is included in this distribution
 
22
 * in the file called LICENSE.GPL.
 
23
 *
 
24
 * BSD LICENSE
 
25
 *
 
26
 * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
 
27
 * All rights reserved.
 
28
 *
 
29
 * Redistribution and use in source and binary forms, with or without
 
30
 * modification, are permitted provided that the following conditions
 
31
 * are met:
 
32
 *
 
33
 *   * Redistributions of source code must retain the above copyright
 
34
 *     notice, this list of conditions and the following disclaimer.
 
35
 *   * Redistributions in binary form must reproduce the above copyright
 
36
 *     notice, this list of conditions and the following disclaimer in
 
37
 *     the documentation and/or other materials provided with the
 
38
 *     distribution.
 
39
 *   * Neither the name of Intel Corporation nor the names of its
 
40
 *     contributors may be used to endorse or promote products derived
 
41
 *     from this software without specific prior written permission.
 
42
 *
 
43
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 
44
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 
45
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 
46
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 
47
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 
48
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 
49
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 
50
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 
51
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
52
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 
53
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
54
 */
 
55
 
 
56
#include "host.h"
 
57
 
 
58
#define SCIC_SDS_MPC_RECONFIGURATION_TIMEOUT    (10)
 
59
#define SCIC_SDS_APC_RECONFIGURATION_TIMEOUT    (10)
 
60
#define SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION  (100)
 
61
 
 
62
enum SCIC_SDS_APC_ACTIVITY {
 
63
        SCIC_SDS_APC_SKIP_PHY,
 
64
        SCIC_SDS_APC_ADD_PHY,
 
65
        SCIC_SDS_APC_START_TIMER,
 
66
 
 
67
        SCIC_SDS_APC_ACTIVITY_MAX
 
68
};
 
69
 
 
70
/*
 
71
 * ******************************************************************************
 
72
 * General port configuration agent routines
 
73
 * ****************************************************************************** */
 
74
 
 
75
/**
 
76
 *
 
77
 * @address_one: A SAS Address to be compared.
 
78
 * @address_two: A SAS Address to be compared.
 
79
 *
 
80
 * Compare the two SAS Address and if SAS Address One is greater than SAS
 
81
 * Address Two then return > 0 else if SAS Address One is less than SAS Address
 
82
 * Two return < 0 Otherwise they are the same return 0 A signed value of x > 0
 
83
 * > y where x is returned for Address One > Address Two y is returned for
 
84
 * Address One < Address Two 0 is returned ofr Address One = Address Two
 
85
 */
 
86
static s32 sci_sas_address_compare(
 
87
        struct sci_sas_address address_one,
 
88
        struct sci_sas_address address_two)
 
89
{
 
90
        if (address_one.high > address_two.high) {
 
91
                return 1;
 
92
        } else if (address_one.high < address_two.high) {
 
93
                return -1;
 
94
        } else if (address_one.low > address_two.low) {
 
95
                return 1;
 
96
        } else if (address_one.low < address_two.low) {
 
97
                return -1;
 
98
        }
 
99
 
 
100
        /* The two SAS Address must be identical */
 
101
        return 0;
 
102
}
 
103
 
 
104
/**
 
105
 *
 
106
 * @controller: The controller object used for the port search.
 
107
 * @phy: The phy object to match.
 
108
 *
 
109
 * This routine will find a matching port for the phy.  This means that the
 
110
 * port and phy both have the same broadcast sas address and same received sas
 
111
 * address. The port address or the NULL if there is no matching
 
112
 * port. port address if the port can be found to match the phy.
 
113
 * NULL if there is no matching port for the phy.
 
114
 */
 
115
static struct isci_port *sci_port_configuration_agent_find_port(
 
116
        struct isci_host *ihost,
 
117
        struct isci_phy *iphy)
 
118
{
 
119
        u8 i;
 
120
        struct sci_sas_address port_sas_address;
 
121
        struct sci_sas_address port_attached_device_address;
 
122
        struct sci_sas_address phy_sas_address;
 
123
        struct sci_sas_address phy_attached_device_address;
 
124
 
 
125
        /*
 
126
         * Since this phy can be a member of a wide port check to see if one or
 
127
         * more phys match the sent and received SAS address as this phy in which
 
128
         * case it should participate in the same port.
 
129
         */
 
130
        sci_phy_get_sas_address(iphy, &phy_sas_address);
 
131
        sci_phy_get_attached_sas_address(iphy, &phy_attached_device_address);
 
132
 
 
133
        for (i = 0; i < ihost->logical_port_entries; i++) {
 
134
                struct isci_port *iport = &ihost->ports[i];
 
135
 
 
136
                sci_port_get_sas_address(iport, &port_sas_address);
 
137
                sci_port_get_attached_sas_address(iport, &port_attached_device_address);
 
138
 
 
139
                if (sci_sas_address_compare(port_sas_address, phy_sas_address) == 0 &&
 
140
                    sci_sas_address_compare(port_attached_device_address, phy_attached_device_address) == 0)
 
141
                        return iport;
 
142
        }
 
143
 
 
144
        return NULL;
 
145
}
 
146
 
 
147
/**
 
148
 *
 
149
 * @controller: This is the controller object that contains the port agent
 
150
 * @port_agent: This is the port configruation agent for the controller.
 
151
 *
 
152
 * This routine will validate the port configuration is correct for the SCU
 
153
 * hardware.  The SCU hardware allows for port configurations as follows. LP0
 
154
 * -> (PE0), (PE0, PE1), (PE0, PE1, PE2, PE3) LP1 -> (PE1) LP2 -> (PE2), (PE2,
 
155
 * PE3) LP3 -> (PE3) enum sci_status SCI_SUCCESS the port configuration is valid for
 
156
 * this port configuration agent. SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION
 
157
 * the port configuration is not valid for this port configuration agent.
 
158
 */
 
159
static enum sci_status sci_port_configuration_agent_validate_ports(
 
160
        struct isci_host *ihost,
 
161
        struct sci_port_configuration_agent *port_agent)
 
162
{
 
163
        struct sci_sas_address first_address;
 
164
        struct sci_sas_address second_address;
 
165
 
 
166
        /*
 
167
         * Sanity check the max ranges for all the phys the max index
 
168
         * is always equal to the port range index */
 
169
        if (port_agent->phy_valid_port_range[0].max_index != 0 ||
 
170
            port_agent->phy_valid_port_range[1].max_index != 1 ||
 
171
            port_agent->phy_valid_port_range[2].max_index != 2 ||
 
172
            port_agent->phy_valid_port_range[3].max_index != 3)
 
173
                return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
 
174
 
 
175
        /*
 
176
         * This is a request to configure a single x4 port or at least attempt
 
177
         * to make all the phys into a single port */
 
178
        if (port_agent->phy_valid_port_range[0].min_index == 0 &&
 
179
            port_agent->phy_valid_port_range[1].min_index == 0 &&
 
180
            port_agent->phy_valid_port_range[2].min_index == 0 &&
 
181
            port_agent->phy_valid_port_range[3].min_index == 0)
 
182
                return SCI_SUCCESS;
 
183
 
 
184
        /*
 
185
         * This is a degenerate case where phy 1 and phy 2 are assigned
 
186
         * to the same port this is explicitly disallowed by the hardware
 
187
         * unless they are part of the same x4 port and this condition was
 
188
         * already checked above. */
 
189
        if (port_agent->phy_valid_port_range[2].min_index == 1) {
 
190
                return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
 
191
        }
 
192
 
 
193
        /*
 
194
         * PE0 and PE3 can never have the same SAS Address unless they
 
195
         * are part of the same x4 wide port and we have already checked
 
196
         * for this condition. */
 
197
        sci_phy_get_sas_address(&ihost->phys[0], &first_address);
 
198
        sci_phy_get_sas_address(&ihost->phys[3], &second_address);
 
199
 
 
200
        if (sci_sas_address_compare(first_address, second_address) == 0) {
 
201
                return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
 
202
        }
 
203
 
 
204
        /*
 
205
         * PE0 and PE1 are configured into a 2x1 ports make sure that the
 
206
         * SAS Address for PE0 and PE2 are different since they can not be
 
207
         * part of the same port. */
 
208
        if (port_agent->phy_valid_port_range[0].min_index == 0 &&
 
209
            port_agent->phy_valid_port_range[1].min_index == 1) {
 
210
                sci_phy_get_sas_address(&ihost->phys[0], &first_address);
 
211
                sci_phy_get_sas_address(&ihost->phys[2], &second_address);
 
212
 
 
213
                if (sci_sas_address_compare(first_address, second_address) == 0) {
 
214
                        return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
 
215
                }
 
216
        }
 
217
 
 
218
        /*
 
219
         * PE2 and PE3 are configured into a 2x1 ports make sure that the
 
220
         * SAS Address for PE1 and PE3 are different since they can not be
 
221
         * part of the same port. */
 
222
        if (port_agent->phy_valid_port_range[2].min_index == 2 &&
 
223
            port_agent->phy_valid_port_range[3].min_index == 3) {
 
224
                sci_phy_get_sas_address(&ihost->phys[1], &first_address);
 
225
                sci_phy_get_sas_address(&ihost->phys[3], &second_address);
 
226
 
 
227
                if (sci_sas_address_compare(first_address, second_address) == 0) {
 
228
                        return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
 
229
                }
 
230
        }
 
231
 
 
232
        return SCI_SUCCESS;
 
233
}
 
234
 
 
235
/*
 
236
 * ******************************************************************************
 
237
 * Manual port configuration agent routines
 
238
 * ****************************************************************************** */
 
239
 
 
240
/* verify all of the phys in the same port are using the same SAS address */
 
241
static enum sci_status
 
242
sci_mpc_agent_validate_phy_configuration(struct isci_host *ihost,
 
243
                                              struct sci_port_configuration_agent *port_agent)
 
244
{
 
245
        u32 phy_mask;
 
246
        u32 assigned_phy_mask;
 
247
        struct sci_sas_address sas_address;
 
248
        struct sci_sas_address phy_assigned_address;
 
249
        u8 port_index;
 
250
        u8 phy_index;
 
251
 
 
252
        assigned_phy_mask = 0;
 
253
        sas_address.high = 0;
 
254
        sas_address.low = 0;
 
255
 
 
256
        for (port_index = 0; port_index < SCI_MAX_PORTS; port_index++) {
 
257
                phy_mask = ihost->oem_parameters.ports[port_index].phy_mask;
 
258
 
 
259
                if (!phy_mask)
 
260
                        continue;
 
261
                /*
 
262
                 * Make sure that one or more of the phys were not already assinged to
 
263
                 * a different port. */
 
264
                if ((phy_mask & ~assigned_phy_mask) == 0) {
 
265
                        return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
 
266
                }
 
267
 
 
268
                /* Find the starting phy index for this round through the loop */
 
269
                for (phy_index = 0; phy_index < SCI_MAX_PHYS; phy_index++) {
 
270
                        if ((phy_mask & (1 << phy_index)) == 0)
 
271
                                continue;
 
272
                        sci_phy_get_sas_address(&ihost->phys[phy_index],
 
273
                                                     &sas_address);
 
274
 
 
275
                        /*
 
276
                         * The phy_index can be used as the starting point for the
 
277
                         * port range since the hardware starts all logical ports
 
278
                         * the same as the PE index. */
 
279
                        port_agent->phy_valid_port_range[phy_index].min_index = port_index;
 
280
                        port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
 
281
 
 
282
                        if (phy_index != port_index) {
 
283
                                return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
 
284
                        }
 
285
 
 
286
                        break;
 
287
                }
 
288
 
 
289
                /*
 
290
                 * See how many additional phys are being added to this logical port.
 
291
                 * Note: We have not moved the current phy_index so we will actually
 
292
                 *       compare the startting phy with itself.
 
293
                 *       This is expected and required to add the phy to the port. */
 
294
                while (phy_index < SCI_MAX_PHYS) {
 
295
                        if ((phy_mask & (1 << phy_index)) == 0)
 
296
                                continue;
 
297
                        sci_phy_get_sas_address(&ihost->phys[phy_index],
 
298
                                                     &phy_assigned_address);
 
299
 
 
300
                        if (sci_sas_address_compare(sas_address, phy_assigned_address) != 0) {
 
301
                                /*
 
302
                                 * The phy mask specified that this phy is part of the same port
 
303
                                 * as the starting phy and it is not so fail this configuration */
 
304
                                return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION;
 
305
                        }
 
306
 
 
307
                        port_agent->phy_valid_port_range[phy_index].min_index = port_index;
 
308
                        port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
 
309
 
 
310
                        sci_port_add_phy(&ihost->ports[port_index],
 
311
                                              &ihost->phys[phy_index]);
 
312
 
 
313
                        assigned_phy_mask |= (1 << phy_index);
 
314
                }
 
315
 
 
316
                phy_index++;
 
317
        }
 
318
 
 
319
        return sci_port_configuration_agent_validate_ports(ihost, port_agent);
 
320
}
 
321
 
 
322
static void mpc_agent_timeout(unsigned long data)
 
323
{
 
324
        u8 index;
 
325
        struct sci_timer *tmr = (struct sci_timer *)data;
 
326
        struct sci_port_configuration_agent *port_agent;
 
327
        struct isci_host *ihost;
 
328
        unsigned long flags;
 
329
        u16 configure_phy_mask;
 
330
 
 
331
        port_agent = container_of(tmr, typeof(*port_agent), timer);
 
332
        ihost = container_of(port_agent, typeof(*ihost), port_agent);
 
333
 
 
334
        spin_lock_irqsave(&ihost->scic_lock, flags);
 
335
 
 
336
        if (tmr->cancel)
 
337
                goto done;
 
338
 
 
339
        port_agent->timer_pending = false;
 
340
 
 
341
        /* Find the mask of phys that are reported read but as yet unconfigured into a port */
 
342
        configure_phy_mask = ~port_agent->phy_configured_mask & port_agent->phy_ready_mask;
 
343
 
 
344
        for (index = 0; index < SCI_MAX_PHYS; index++) {
 
345
                struct isci_phy *iphy = &ihost->phys[index];
 
346
 
 
347
                if (configure_phy_mask & (1 << index)) {
 
348
                        port_agent->link_up_handler(ihost, port_agent,
 
349
                                                    phy_get_non_dummy_port(iphy),
 
350
                                                    iphy);
 
351
                }
 
352
        }
 
353
 
 
354
done:
 
355
        spin_unlock_irqrestore(&ihost->scic_lock, flags);
 
356
}
 
357
 
 
358
static void sci_mpc_agent_link_up(struct isci_host *ihost,
 
359
                                       struct sci_port_configuration_agent *port_agent,
 
360
                                       struct isci_port *iport,
 
361
                                       struct isci_phy *iphy)
 
362
{
 
363
        /* If the port is NULL then the phy was not assigned to a port.
 
364
         * This is because the phy was not given the same SAS Address as
 
365
         * the other PHYs in the port.
 
366
         */
 
367
        if (!iport)
 
368
                return;
 
369
 
 
370
        port_agent->phy_ready_mask |= (1 << iphy->phy_index);
 
371
        sci_port_link_up(iport, iphy);
 
372
        if ((iport->active_phy_mask & (1 << iphy->phy_index)))
 
373
                port_agent->phy_configured_mask |= (1 << iphy->phy_index);
 
374
}
 
375
 
 
376
/**
 
377
 *
 
378
 * @controller: This is the controller object that receives the link down
 
379
 *    notification.
 
380
 * @port: This is the port object associated with the phy.  If the is no
 
381
 *    associated port this is an NULL.  The port is an invalid
 
382
 *    handle only if the phy was never port of this port.  This happens when
 
383
 *    the phy is not broadcasting the same SAS address as the other phys in the
 
384
 *    assigned port.
 
385
 * @phy: This is the phy object which has gone link down.
 
386
 *
 
387
 * This function handles the manual port configuration link down notifications.
 
388
 * Since all ports and phys are associated at initialization time we just turn
 
389
 * around and notifiy the port object of the link down event.  If this PHY is
 
390
 * not associated with a port there is no action taken. Is it possible to get a
 
391
 * link down notification from a phy that has no assocoated port?
 
392
 */
 
393
static void sci_mpc_agent_link_down(
 
394
        struct isci_host *ihost,
 
395
        struct sci_port_configuration_agent *port_agent,
 
396
        struct isci_port *iport,
 
397
        struct isci_phy *iphy)
 
398
{
 
399
        if (iport != NULL) {
 
400
                /*
 
401
                 * If we can form a new port from the remainder of the phys
 
402
                 * then we want to start the timer to allow the SCI User to
 
403
                 * cleanup old devices and rediscover the port before
 
404
                 * rebuilding the port with the phys that remain in the ready
 
405
                 * state.
 
406
                 */
 
407
                port_agent->phy_ready_mask &= ~(1 << iphy->phy_index);
 
408
                port_agent->phy_configured_mask &= ~(1 << iphy->phy_index);
 
409
 
 
410
                /*
 
411
                 * Check to see if there are more phys waiting to be
 
412
                 * configured into a port. If there are allow the SCI User
 
413
                 * to tear down this port, if necessary, and then reconstruct
 
414
                 * the port after the timeout.
 
415
                 */
 
416
                if ((port_agent->phy_configured_mask == 0x0000) &&
 
417
                    (port_agent->phy_ready_mask != 0x0000) &&
 
418
                    !port_agent->timer_pending) {
 
419
                        port_agent->timer_pending = true;
 
420
 
 
421
                        sci_mod_timer(&port_agent->timer,
 
422
                                      SCIC_SDS_MPC_RECONFIGURATION_TIMEOUT);
 
423
                }
 
424
 
 
425
                sci_port_link_down(iport, iphy);
 
426
        }
 
427
}
 
428
 
 
429
/* verify phys are assigned a valid SAS address for automatic port
 
430
 * configuration mode.
 
431
 */
 
432
static enum sci_status
 
433
sci_apc_agent_validate_phy_configuration(struct isci_host *ihost,
 
434
                                              struct sci_port_configuration_agent *port_agent)
 
435
{
 
436
        u8 phy_index;
 
437
        u8 port_index;
 
438
        struct sci_sas_address sas_address;
 
439
        struct sci_sas_address phy_assigned_address;
 
440
 
 
441
        phy_index = 0;
 
442
 
 
443
        while (phy_index < SCI_MAX_PHYS) {
 
444
                port_index = phy_index;
 
445
 
 
446
                /* Get the assigned SAS Address for the first PHY on the controller. */
 
447
                sci_phy_get_sas_address(&ihost->phys[phy_index],
 
448
                                            &sas_address);
 
449
 
 
450
                while (++phy_index < SCI_MAX_PHYS) {
 
451
                        sci_phy_get_sas_address(&ihost->phys[phy_index],
 
452
                                                     &phy_assigned_address);
 
453
 
 
454
                        /* Verify each of the SAS address are all the same for every PHY */
 
455
                        if (sci_sas_address_compare(sas_address, phy_assigned_address) == 0) {
 
456
                                port_agent->phy_valid_port_range[phy_index].min_index = port_index;
 
457
                                port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
 
458
                        } else {
 
459
                                port_agent->phy_valid_port_range[phy_index].min_index = phy_index;
 
460
                                port_agent->phy_valid_port_range[phy_index].max_index = phy_index;
 
461
                                break;
 
462
                        }
 
463
                }
 
464
        }
 
465
 
 
466
        return sci_port_configuration_agent_validate_ports(ihost, port_agent);
 
467
}
 
468
 
 
469
static void sci_apc_agent_configure_ports(struct isci_host *ihost,
 
470
                                               struct sci_port_configuration_agent *port_agent,
 
471
                                               struct isci_phy *iphy,
 
472
                                               bool start_timer)
 
473
{
 
474
        u8 port_index;
 
475
        enum sci_status status;
 
476
        struct isci_port *iport;
 
477
        enum SCIC_SDS_APC_ACTIVITY apc_activity = SCIC_SDS_APC_SKIP_PHY;
 
478
 
 
479
        iport = sci_port_configuration_agent_find_port(ihost, iphy);
 
480
 
 
481
        if (iport) {
 
482
                if (sci_port_is_valid_phy_assignment(iport, iphy->phy_index))
 
483
                        apc_activity = SCIC_SDS_APC_ADD_PHY;
 
484
                else
 
485
                        apc_activity = SCIC_SDS_APC_SKIP_PHY;
 
486
        } else {
 
487
                /*
 
488
                 * There is no matching Port for this PHY so lets search through the
 
489
                 * Ports and see if we can add the PHY to its own port or maybe start
 
490
                 * the timer and wait to see if a wider port can be made.
 
491
                 *
 
492
                 * Note the break when we reach the condition of the port id == phy id */
 
493
                for (port_index = port_agent->phy_valid_port_range[iphy->phy_index].min_index;
 
494
                     port_index <= port_agent->phy_valid_port_range[iphy->phy_index].max_index;
 
495
                     port_index++) {
 
496
 
 
497
                        iport = &ihost->ports[port_index];
 
498
 
 
499
                        /* First we must make sure that this PHY can be added to this Port. */
 
500
                        if (sci_port_is_valid_phy_assignment(iport, iphy->phy_index)) {
 
501
                                /*
 
502
                                 * Port contains a PHY with a greater PHY ID than the current
 
503
                                 * PHY that has gone link up.  This phy can not be part of any
 
504
                                 * port so skip it and move on. */
 
505
                                if (iport->active_phy_mask > (1 << iphy->phy_index)) {
 
506
                                        apc_activity = SCIC_SDS_APC_SKIP_PHY;
 
507
                                        break;
 
508
                                }
 
509
 
 
510
                                /*
 
511
                                 * We have reached the end of our Port list and have not found
 
512
                                 * any reason why we should not either add the PHY to the port
 
513
                                 * or wait for more phys to become active. */
 
514
                                if (iport->physical_port_index == iphy->phy_index) {
 
515
                                        /*
 
516
                                         * The Port either has no active PHYs.
 
517
                                         * Consider that if the port had any active PHYs we would have
 
518
                                         * or active PHYs with
 
519
                                         * a lower PHY Id than this PHY. */
 
520
                                        if (apc_activity != SCIC_SDS_APC_START_TIMER) {
 
521
                                                apc_activity = SCIC_SDS_APC_ADD_PHY;
 
522
                                        }
 
523
 
 
524
                                        break;
 
525
                                }
 
526
 
 
527
                                /*
 
528
                                 * The current Port has no active PHYs and this PHY could be part
 
529
                                 * of this Port.  Since we dont know as yet setup to start the
 
530
                                 * timer and see if there is a better configuration. */
 
531
                                if (iport->active_phy_mask == 0) {
 
532
                                        apc_activity = SCIC_SDS_APC_START_TIMER;
 
533
                                }
 
534
                        } else if (iport->active_phy_mask != 0) {
 
535
                                /*
 
536
                                 * The Port has an active phy and the current Phy can not
 
537
                                 * participate in this port so skip the PHY and see if
 
538
                                 * there is a better configuration. */
 
539
                                apc_activity = SCIC_SDS_APC_SKIP_PHY;
 
540
                        }
 
541
                }
 
542
        }
 
543
 
 
544
        /*
 
545
         * Check to see if the start timer operations should instead map to an
 
546
         * add phy operation.  This is caused because we have been waiting to
 
547
         * add a phy to a port but could not becuase the automatic port
 
548
         * configuration engine had a choice of possible ports for the phy.
 
549
         * Since we have gone through a timeout we are going to restrict the
 
550
         * choice to the smallest possible port. */
 
551
        if (
 
552
                (start_timer == false)
 
553
                && (apc_activity == SCIC_SDS_APC_START_TIMER)
 
554
                ) {
 
555
                apc_activity = SCIC_SDS_APC_ADD_PHY;
 
556
        }
 
557
 
 
558
        switch (apc_activity) {
 
559
        case SCIC_SDS_APC_ADD_PHY:
 
560
                status = sci_port_add_phy(iport, iphy);
 
561
 
 
562
                if (status == SCI_SUCCESS) {
 
563
                        port_agent->phy_configured_mask |= (1 << iphy->phy_index);
 
564
                }
 
565
                break;
 
566
 
 
567
        case SCIC_SDS_APC_START_TIMER:
 
568
                /*
 
569
                 * This can occur for either a link down event, or a link
 
570
                 * up event where we cannot yet tell the port to which a
 
571
                 * phy belongs.
 
572
                 */
 
573
                if (port_agent->timer_pending)
 
574
                        sci_del_timer(&port_agent->timer);
 
575
 
 
576
                port_agent->timer_pending = true;
 
577
                sci_mod_timer(&port_agent->timer,
 
578
                              SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION);
 
579
                break;
 
580
 
 
581
        case SCIC_SDS_APC_SKIP_PHY:
 
582
        default:
 
583
                /* do nothing the PHY can not be made part of a port at this time. */
 
584
                break;
 
585
        }
 
586
}
 
587
 
 
588
/**
 
589
 * sci_apc_agent_link_up - handle apc link up events
 
590
 * @scic: This is the controller object that receives the link up
 
591
 *    notification.
 
592
 * @sci_port: This is the port object associated with the phy.  If the is no
 
593
 *    associated port this is an NULL.
 
594
 * @sci_phy: This is the phy object which has gone link up.
 
595
 *
 
596
 * This method handles the automatic port configuration for link up
 
597
 * notifications. Is it possible to get a link down notification from a phy
 
598
 * that has no assocoated port?
 
599
 */
 
600
static void sci_apc_agent_link_up(struct isci_host *ihost,
 
601
                                       struct sci_port_configuration_agent *port_agent,
 
602
                                       struct isci_port *iport,
 
603
                                       struct isci_phy *iphy)
 
604
{
 
605
        u8 phy_index  = iphy->phy_index;
 
606
 
 
607
        if (!iport) {
 
608
                /* the phy is not the part of this port */
 
609
                port_agent->phy_ready_mask |= 1 << phy_index;
 
610
                sci_apc_agent_configure_ports(ihost, port_agent, iphy, true);
 
611
        } else {
 
612
                /* the phy is already the part of the port */
 
613
                u32 port_state = iport->sm.current_state_id;
 
614
 
 
615
                /* if the PORT'S state is resetting then the link up is from
 
616
                 * port hard reset in this case, we need to tell the port
 
617
                 * that link up is recieved
 
618
                 */
 
619
                BUG_ON(port_state != SCI_PORT_RESETTING);
 
620
                port_agent->phy_ready_mask |= 1 << phy_index;
 
621
                sci_port_link_up(iport, iphy);
 
622
        }
 
623
}
 
624
 
 
625
/**
 
626
 *
 
627
 * @controller: This is the controller object that receives the link down
 
628
 *    notification.
 
629
 * @iport: This is the port object associated with the phy.  If the is no
 
630
 *    associated port this is an NULL.
 
631
 * @iphy: This is the phy object which has gone link down.
 
632
 *
 
633
 * This method handles the automatic port configuration link down
 
634
 * notifications. not associated with a port there is no action taken. Is it
 
635
 * possible to get a link down notification from a phy that has no assocoated
 
636
 * port?
 
637
 */
 
638
static void sci_apc_agent_link_down(
 
639
        struct isci_host *ihost,
 
640
        struct sci_port_configuration_agent *port_agent,
 
641
        struct isci_port *iport,
 
642
        struct isci_phy *iphy)
 
643
{
 
644
        port_agent->phy_ready_mask &= ~(1 << iphy->phy_index);
 
645
 
 
646
        if (!iport)
 
647
                return;
 
648
        if (port_agent->phy_configured_mask & (1 << iphy->phy_index)) {
 
649
                enum sci_status status;
 
650
 
 
651
                status = sci_port_remove_phy(iport, iphy);
 
652
 
 
653
                if (status == SCI_SUCCESS)
 
654
                        port_agent->phy_configured_mask &= ~(1 << iphy->phy_index);
 
655
        }
 
656
}
 
657
 
 
658
/* configure the phys into ports when the timer fires */
 
659
static void apc_agent_timeout(unsigned long data)
 
660
{
 
661
        u32 index;
 
662
        struct sci_timer *tmr = (struct sci_timer *)data;
 
663
        struct sci_port_configuration_agent *port_agent;
 
664
        struct isci_host *ihost;
 
665
        unsigned long flags;
 
666
        u16 configure_phy_mask;
 
667
 
 
668
        port_agent = container_of(tmr, typeof(*port_agent), timer);
 
669
        ihost = container_of(port_agent, typeof(*ihost), port_agent);
 
670
 
 
671
        spin_lock_irqsave(&ihost->scic_lock, flags);
 
672
 
 
673
        if (tmr->cancel)
 
674
                goto done;
 
675
 
 
676
        port_agent->timer_pending = false;
 
677
 
 
678
        configure_phy_mask = ~port_agent->phy_configured_mask & port_agent->phy_ready_mask;
 
679
 
 
680
        if (!configure_phy_mask)
 
681
                return;
 
682
 
 
683
        for (index = 0; index < SCI_MAX_PHYS; index++) {
 
684
                if ((configure_phy_mask & (1 << index)) == 0)
 
685
                        continue;
 
686
 
 
687
                sci_apc_agent_configure_ports(ihost, port_agent,
 
688
                                                   &ihost->phys[index], false);
 
689
        }
 
690
 
 
691
done:
 
692
        spin_unlock_irqrestore(&ihost->scic_lock, flags);
 
693
}
 
694
 
 
695
/*
 
696
 * ******************************************************************************
 
697
 * Public port configuration agent routines
 
698
 * ****************************************************************************** */
 
699
 
 
700
/**
 
701
 *
 
702
 *
 
703
 * This method will construct the port configuration agent for operation. This
 
704
 * call is universal for both manual port configuration and automatic port
 
705
 * configuration modes.
 
706
 */
 
707
void sci_port_configuration_agent_construct(
 
708
        struct sci_port_configuration_agent *port_agent)
 
709
{
 
710
        u32 index;
 
711
 
 
712
        port_agent->phy_configured_mask = 0x00;
 
713
        port_agent->phy_ready_mask = 0x00;
 
714
 
 
715
        port_agent->link_up_handler = NULL;
 
716
        port_agent->link_down_handler = NULL;
 
717
 
 
718
        port_agent->timer_pending = false;
 
719
 
 
720
        for (index = 0; index < SCI_MAX_PORTS; index++) {
 
721
                port_agent->phy_valid_port_range[index].min_index = 0;
 
722
                port_agent->phy_valid_port_range[index].max_index = 0;
 
723
        }
 
724
}
 
725
 
 
726
enum sci_status sci_port_configuration_agent_initialize(
 
727
        struct isci_host *ihost,
 
728
        struct sci_port_configuration_agent *port_agent)
 
729
{
 
730
        enum sci_status status;
 
731
        enum sci_port_configuration_mode mode;
 
732
 
 
733
        mode = ihost->oem_parameters.controller.mode_type;
 
734
 
 
735
        if (mode == SCIC_PORT_MANUAL_CONFIGURATION_MODE) {
 
736
                status = sci_mpc_agent_validate_phy_configuration(
 
737
                                ihost, port_agent);
 
738
 
 
739
                port_agent->link_up_handler = sci_mpc_agent_link_up;
 
740
                port_agent->link_down_handler = sci_mpc_agent_link_down;
 
741
 
 
742
                sci_init_timer(&port_agent->timer, mpc_agent_timeout);
 
743
        } else {
 
744
                status = sci_apc_agent_validate_phy_configuration(
 
745
                                ihost, port_agent);
 
746
 
 
747
                port_agent->link_up_handler = sci_apc_agent_link_up;
 
748
                port_agent->link_down_handler = sci_apc_agent_link_down;
 
749
 
 
750
                sci_init_timer(&port_agent->timer, apc_agent_timeout);
 
751
        }
 
752
 
 
753
        return status;
 
754
}