cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

port_config.c (26064B)


      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  (1000)
     61
     62enum 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 * sci_sas_address_compare()
     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 */
     86static 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 * sci_port_configuration_agent_find_port()
    106 * @ihost: The controller object used for the port search.
    107 * @iphy: 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 */
    115static 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 * sci_port_configuration_agent_validate_ports()
    149 * @ihost: This is the controller object that contains the port agent
    150 * @port_agent: This is the port configuration 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 */
    159static 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 */
    241static enum sci_status
    242sci_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		for (; phy_index < SCI_MAX_PHYS; phy_index++) {
    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	}
    317
    318	return sci_port_configuration_agent_validate_ports(ihost, port_agent);
    319}
    320
    321static void mpc_agent_timeout(struct timer_list *t)
    322{
    323	u8 index;
    324	struct sci_timer *tmr = from_timer(tmr, t, timer);
    325	struct sci_port_configuration_agent *port_agent;
    326	struct isci_host *ihost;
    327	unsigned long flags;
    328	u16 configure_phy_mask;
    329
    330	port_agent = container_of(tmr, typeof(*port_agent), timer);
    331	ihost = container_of(port_agent, typeof(*ihost), port_agent);
    332
    333	spin_lock_irqsave(&ihost->scic_lock, flags);
    334
    335	if (tmr->cancel)
    336		goto done;
    337
    338	port_agent->timer_pending = false;
    339
    340	/* Find the mask of phys that are reported read but as yet unconfigured into a port */
    341	configure_phy_mask = ~port_agent->phy_configured_mask & port_agent->phy_ready_mask;
    342
    343	for (index = 0; index < SCI_MAX_PHYS; index++) {
    344		struct isci_phy *iphy = &ihost->phys[index];
    345
    346		if (configure_phy_mask & (1 << index)) {
    347			port_agent->link_up_handler(ihost, port_agent,
    348						    phy_get_non_dummy_port(iphy),
    349						    iphy);
    350		}
    351	}
    352
    353done:
    354	spin_unlock_irqrestore(&ihost->scic_lock, flags);
    355}
    356
    357static void sci_mpc_agent_link_up(struct isci_host *ihost,
    358				       struct sci_port_configuration_agent *port_agent,
    359				       struct isci_port *iport,
    360				       struct isci_phy *iphy)
    361{
    362	/* If the port is NULL then the phy was not assigned to a port.
    363	 * This is because the phy was not given the same SAS Address as
    364	 * the other PHYs in the port.
    365	 */
    366	if (!iport)
    367		return;
    368
    369	port_agent->phy_ready_mask |= (1 << iphy->phy_index);
    370	sci_port_link_up(iport, iphy);
    371	if ((iport->active_phy_mask & (1 << iphy->phy_index)))
    372		port_agent->phy_configured_mask |= (1 << iphy->phy_index);
    373}
    374
    375/**
    376 * sci_mpc_agent_link_down()
    377 * @ihost: This is the controller object that receives the link down
    378 *    notification.
    379 * @port_agent: This is the port configuration agent for the controller.
    380 * @iport: 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 * @iphy: 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 */
    393static 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 */
    432static enum sci_status
    433sci_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/*
    470 * This routine will restart the automatic port configuration timeout
    471 * timer for the next time period. This could be caused by either a link
    472 * down event or a link up event where we can not yet tell to which a phy
    473 * belongs.
    474 */
    475static void sci_apc_agent_start_timer(struct sci_port_configuration_agent *port_agent,
    476				      u32 timeout)
    477{
    478	port_agent->timer_pending = true;
    479	sci_mod_timer(&port_agent->timer, timeout);
    480}
    481
    482static void sci_apc_agent_configure_ports(struct isci_host *ihost,
    483					       struct sci_port_configuration_agent *port_agent,
    484					       struct isci_phy *iphy,
    485					       bool start_timer)
    486{
    487	u8 port_index;
    488	enum sci_status status;
    489	struct isci_port *iport;
    490	enum SCIC_SDS_APC_ACTIVITY apc_activity = SCIC_SDS_APC_SKIP_PHY;
    491
    492	iport = sci_port_configuration_agent_find_port(ihost, iphy);
    493
    494	if (iport) {
    495		if (sci_port_is_valid_phy_assignment(iport, iphy->phy_index))
    496			apc_activity = SCIC_SDS_APC_ADD_PHY;
    497		else
    498			apc_activity = SCIC_SDS_APC_SKIP_PHY;
    499	} else {
    500		/*
    501		 * There is no matching Port for this PHY so lets search through the
    502		 * Ports and see if we can add the PHY to its own port or maybe start
    503		 * the timer and wait to see if a wider port can be made.
    504		 *
    505		 * Note the break when we reach the condition of the port id == phy id */
    506		for (port_index = port_agent->phy_valid_port_range[iphy->phy_index].min_index;
    507		     port_index <= port_agent->phy_valid_port_range[iphy->phy_index].max_index;
    508		     port_index++) {
    509
    510			iport = &ihost->ports[port_index];
    511
    512			/* First we must make sure that this PHY can be added to this Port. */
    513			if (sci_port_is_valid_phy_assignment(iport, iphy->phy_index)) {
    514				/*
    515				 * Port contains a PHY with a greater PHY ID than the current
    516				 * PHY that has gone link up.  This phy can not be part of any
    517				 * port so skip it and move on. */
    518				if (iport->active_phy_mask > (1 << iphy->phy_index)) {
    519					apc_activity = SCIC_SDS_APC_SKIP_PHY;
    520					break;
    521				}
    522
    523				/*
    524				 * We have reached the end of our Port list and have not found
    525				 * any reason why we should not either add the PHY to the port
    526				 * or wait for more phys to become active. */
    527				if (iport->physical_port_index == iphy->phy_index) {
    528					/*
    529					 * The Port either has no active PHYs.
    530					 * Consider that if the port had any active PHYs we would have
    531					 * or active PHYs with
    532					 * a lower PHY Id than this PHY. */
    533					if (apc_activity != SCIC_SDS_APC_START_TIMER) {
    534						apc_activity = SCIC_SDS_APC_ADD_PHY;
    535					}
    536
    537					break;
    538				}
    539
    540				/*
    541				 * The current Port has no active PHYs and this PHY could be part
    542				 * of this Port.  Since we dont know as yet setup to start the
    543				 * timer and see if there is a better configuration. */
    544				if (iport->active_phy_mask == 0) {
    545					apc_activity = SCIC_SDS_APC_START_TIMER;
    546				}
    547			} else if (iport->active_phy_mask != 0) {
    548				/*
    549				 * The Port has an active phy and the current Phy can not
    550				 * participate in this port so skip the PHY and see if
    551				 * there is a better configuration. */
    552				apc_activity = SCIC_SDS_APC_SKIP_PHY;
    553			}
    554		}
    555	}
    556
    557	/*
    558	 * Check to see if the start timer operations should instead map to an
    559	 * add phy operation.  This is caused because we have been waiting to
    560	 * add a phy to a port but could not becuase the automatic port
    561	 * configuration engine had a choice of possible ports for the phy.
    562	 * Since we have gone through a timeout we are going to restrict the
    563	 * choice to the smallest possible port. */
    564	if (
    565		(start_timer == false)
    566		&& (apc_activity == SCIC_SDS_APC_START_TIMER)
    567		) {
    568		apc_activity = SCIC_SDS_APC_ADD_PHY;
    569	}
    570
    571	switch (apc_activity) {
    572	case SCIC_SDS_APC_ADD_PHY:
    573		status = sci_port_add_phy(iport, iphy);
    574
    575		if (status == SCI_SUCCESS) {
    576			port_agent->phy_configured_mask |= (1 << iphy->phy_index);
    577		}
    578		break;
    579
    580	case SCIC_SDS_APC_START_TIMER:
    581		sci_apc_agent_start_timer(port_agent,
    582					  SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION);
    583		break;
    584
    585	case SCIC_SDS_APC_SKIP_PHY:
    586	default:
    587		/* do nothing the PHY can not be made part of a port at this time. */
    588		break;
    589	}
    590}
    591
    592/**
    593 * sci_apc_agent_link_up - handle apc link up events
    594 * @ihost: This is the controller object that receives the link up
    595 *    notification.
    596 * @port_agent: This is the port configuration agent for the controller.
    597 * @iport: This is the port object associated with the phy.  If the is no
    598 *    associated port this is an NULL.
    599 * @iphy: This is the phy object which has gone link up.
    600 *
    601 * This method handles the automatic port configuration for link up
    602 * notifications. Is it possible to get a link down notification from a phy
    603 * that has no assocoated port?
    604 */
    605static void sci_apc_agent_link_up(struct isci_host *ihost,
    606				       struct sci_port_configuration_agent *port_agent,
    607				       struct isci_port *iport,
    608				       struct isci_phy *iphy)
    609{
    610	u8 phy_index  = iphy->phy_index;
    611
    612	if (!iport) {
    613		/* the phy is not the part of this port */
    614		port_agent->phy_ready_mask |= 1 << phy_index;
    615		sci_apc_agent_start_timer(port_agent,
    616					  SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION);
    617	} else {
    618		/* the phy is already the part of the port */
    619		port_agent->phy_ready_mask |= 1 << phy_index;
    620		sci_port_link_up(iport, iphy);
    621	}
    622}
    623
    624/**
    625 * sci_apc_agent_link_down()
    626 * @ihost: This is the controller object that receives the link down
    627 *    notification.
    628 * @port_agent: This is the port configuration agent for the controller.
    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 */
    638static 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 */
    659static void apc_agent_timeout(struct timer_list *t)
    660{
    661	u32 index;
    662	struct sci_timer *tmr = from_timer(tmr, t, timer);
    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		goto done;
    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	if (is_controller_start_complete(ihost))
    692		sci_controller_transition_to_ready(ihost, SCI_SUCCESS);
    693
    694done:
    695	spin_unlock_irqrestore(&ihost->scic_lock, flags);
    696}
    697
    698/*
    699 * ******************************************************************************
    700 * Public port configuration agent routines
    701 * ****************************************************************************** */
    702
    703/*
    704 * This method will construct the port configuration agent for operation. This
    705 * call is universal for both manual port configuration and automatic port
    706 * configuration modes.
    707 */
    708void sci_port_configuration_agent_construct(
    709	struct sci_port_configuration_agent *port_agent)
    710{
    711	u32 index;
    712
    713	port_agent->phy_configured_mask = 0x00;
    714	port_agent->phy_ready_mask = 0x00;
    715
    716	port_agent->link_up_handler = NULL;
    717	port_agent->link_down_handler = NULL;
    718
    719	port_agent->timer_pending = false;
    720
    721	for (index = 0; index < SCI_MAX_PORTS; index++) {
    722		port_agent->phy_valid_port_range[index].min_index = 0;
    723		port_agent->phy_valid_port_range[index].max_index = 0;
    724	}
    725}
    726
    727bool is_port_config_apc(struct isci_host *ihost)
    728{
    729	return ihost->port_agent.link_up_handler == sci_apc_agent_link_up;
    730}
    731
    732enum sci_status sci_port_configuration_agent_initialize(
    733	struct isci_host *ihost,
    734	struct sci_port_configuration_agent *port_agent)
    735{
    736	enum sci_status status;
    737	enum sci_port_configuration_mode mode;
    738
    739	mode = ihost->oem_parameters.controller.mode_type;
    740
    741	if (mode == SCIC_PORT_MANUAL_CONFIGURATION_MODE) {
    742		status = sci_mpc_agent_validate_phy_configuration(
    743				ihost, port_agent);
    744
    745		port_agent->link_up_handler = sci_mpc_agent_link_up;
    746		port_agent->link_down_handler = sci_mpc_agent_link_down;
    747
    748		sci_init_timer(&port_agent->timer, mpc_agent_timeout);
    749	} else {
    750		status = sci_apc_agent_validate_phy_configuration(
    751				ihost, port_agent);
    752
    753		port_agent->link_up_handler = sci_apc_agent_link_up;
    754		port_agent->link_down_handler = sci_apc_agent_link_down;
    755
    756		sci_init_timer(&port_agent->timer, apc_agent_timeout);
    757	}
    758
    759	return status;
    760}