mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-20 02:34:23 +08:00
a01e70e570
This patch implements the ability to set the minimum and maximum linkrates for both libsas (for expanders) and aic94xx (for the host phys). It also tidies up the setting of the hardware min and max to make sure they're updated when the expander emits a change broadcast. Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
159 lines
4.4 KiB
C
159 lines
4.4 KiB
C
/*
|
|
* Serial Attached SCSI (SAS) Phy class
|
|
*
|
|
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
|
|
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
|
|
*
|
|
* This file is licensed under GPLv2.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
*/
|
|
|
|
#include "sas_internal.h"
|
|
#include <scsi/scsi_host.h>
|
|
#include <scsi/scsi_transport.h>
|
|
#include <scsi/scsi_transport_sas.h>
|
|
#include "../scsi_sas_internal.h"
|
|
|
|
/* ---------- Phy events ---------- */
|
|
|
|
static void sas_phye_loss_of_signal(void *data)
|
|
{
|
|
struct asd_sas_phy *phy = data;
|
|
|
|
sas_begin_event(PHYE_LOSS_OF_SIGNAL, &phy->ha->event_lock,
|
|
&phy->phy_events_pending);
|
|
phy->error = 0;
|
|
sas_deform_port(phy);
|
|
}
|
|
|
|
static void sas_phye_oob_done(void *data)
|
|
{
|
|
struct asd_sas_phy *phy = data;
|
|
|
|
sas_begin_event(PHYE_OOB_DONE, &phy->ha->event_lock,
|
|
&phy->phy_events_pending);
|
|
phy->error = 0;
|
|
}
|
|
|
|
static void sas_phye_oob_error(void *data)
|
|
{
|
|
struct asd_sas_phy *phy = data;
|
|
struct sas_ha_struct *sas_ha = phy->ha;
|
|
struct asd_sas_port *port = phy->port;
|
|
struct sas_internal *i =
|
|
to_sas_internal(sas_ha->core.shost->transportt);
|
|
|
|
sas_begin_event(PHYE_OOB_ERROR, &phy->ha->event_lock,
|
|
&phy->phy_events_pending);
|
|
|
|
sas_deform_port(phy);
|
|
|
|
if (!port && phy->enabled && i->dft->lldd_control_phy) {
|
|
phy->error++;
|
|
switch (phy->error) {
|
|
case 1:
|
|
case 2:
|
|
i->dft->lldd_control_phy(phy, PHY_FUNC_HARD_RESET,
|
|
NULL);
|
|
break;
|
|
case 3:
|
|
default:
|
|
phy->error = 0;
|
|
phy->enabled = 0;
|
|
i->dft->lldd_control_phy(phy, PHY_FUNC_DISABLE, NULL);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void sas_phye_spinup_hold(void *data)
|
|
{
|
|
struct asd_sas_phy *phy = data;
|
|
struct sas_ha_struct *sas_ha = phy->ha;
|
|
struct sas_internal *i =
|
|
to_sas_internal(sas_ha->core.shost->transportt);
|
|
|
|
sas_begin_event(PHYE_SPINUP_HOLD, &phy->ha->event_lock,
|
|
&phy->phy_events_pending);
|
|
|
|
phy->error = 0;
|
|
i->dft->lldd_control_phy(phy, PHY_FUNC_RELEASE_SPINUP_HOLD, NULL);
|
|
}
|
|
|
|
/* ---------- Phy class registration ---------- */
|
|
|
|
int sas_register_phys(struct sas_ha_struct *sas_ha)
|
|
{
|
|
int i;
|
|
|
|
static void (*sas_phy_event_fns[PHY_NUM_EVENTS])(void *) = {
|
|
[PHYE_LOSS_OF_SIGNAL] = sas_phye_loss_of_signal,
|
|
[PHYE_OOB_DONE] = sas_phye_oob_done,
|
|
[PHYE_OOB_ERROR] = sas_phye_oob_error,
|
|
[PHYE_SPINUP_HOLD] = sas_phye_spinup_hold,
|
|
};
|
|
|
|
static void (*sas_port_event_fns[PORT_NUM_EVENTS])(void *) = {
|
|
[PORTE_BYTES_DMAED] = sas_porte_bytes_dmaed,
|
|
[PORTE_BROADCAST_RCVD] = sas_porte_broadcast_rcvd,
|
|
[PORTE_LINK_RESET_ERR] = sas_porte_link_reset_err,
|
|
[PORTE_TIMER_EVENT] = sas_porte_timer_event,
|
|
[PORTE_HARD_RESET] = sas_porte_hard_reset,
|
|
};
|
|
|
|
/* Now register the phys. */
|
|
for (i = 0; i < sas_ha->num_phys; i++) {
|
|
int k;
|
|
struct asd_sas_phy *phy = sas_ha->sas_phy[i];
|
|
|
|
phy->error = 0;
|
|
INIT_LIST_HEAD(&phy->port_phy_el);
|
|
for (k = 0; k < PORT_NUM_EVENTS; k++)
|
|
INIT_WORK(&phy->port_events[k], sas_port_event_fns[k],
|
|
phy);
|
|
|
|
for (k = 0; k < PHY_NUM_EVENTS; k++)
|
|
INIT_WORK(&phy->phy_events[k], sas_phy_event_fns[k],
|
|
phy);
|
|
phy->port = NULL;
|
|
phy->ha = sas_ha;
|
|
spin_lock_init(&phy->frame_rcvd_lock);
|
|
spin_lock_init(&phy->sas_prim_lock);
|
|
phy->frame_rcvd_size = 0;
|
|
|
|
phy->phy = sas_phy_alloc(&sas_ha->core.shost->shost_gendev,
|
|
i);
|
|
if (!phy->phy)
|
|
return -ENOMEM;
|
|
|
|
phy->phy->identify.initiator_port_protocols =
|
|
phy->iproto;
|
|
phy->phy->identify.target_port_protocols = phy->tproto;
|
|
phy->phy->identify.sas_address = SAS_ADDR(sas_ha->sas_addr);
|
|
phy->phy->identify.phy_identifier = i;
|
|
phy->phy->minimum_linkrate_hw = SAS_LINK_RATE_UNKNOWN;
|
|
phy->phy->maximum_linkrate_hw = SAS_LINK_RATE_UNKNOWN;
|
|
phy->phy->minimum_linkrate = SAS_LINK_RATE_UNKNOWN;
|
|
phy->phy->maximum_linkrate = SAS_LINK_RATE_UNKNOWN;
|
|
phy->phy->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN;
|
|
|
|
sas_phy_add(phy->phy);
|
|
}
|
|
|
|
return 0;
|
|
}
|