linux/drivers/scsi/libsas/sas_phy.c
<<
>>
Prefs
   1/*
   2 * Serial Attached SCSI (SAS) Phy class
   3 *
   4 * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
   5 * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
   6 *
   7 * This file is licensed under GPLv2.
   8 *
   9 * This program is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU General Public License as
  11 * published by the Free Software Foundation; either version 2 of the
  12 * License, or (at your option) any later version.
  13 *
  14 * This program is distributed in the hope that it will be useful, but
  15 * WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17 * General Public License for more details.
  18 *
  19 * You should have received a copy of the GNU General Public License
  20 * along with this program; if not, write to the Free Software
  21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  22 *
  23 */
  24
  25#include "sas_internal.h"
  26#include <scsi/scsi_host.h>
  27#include <scsi/scsi_transport.h>
  28#include <scsi/scsi_transport_sas.h>
  29#include "../scsi_sas_internal.h"
  30
  31/* ---------- Phy events ---------- */
  32
  33static void sas_phye_loss_of_signal(struct work_struct *work)
  34{
  35        struct asd_sas_event *ev = to_asd_sas_event(work);
  36        struct asd_sas_phy *phy = ev->phy;
  37
  38        clear_bit(PHYE_LOSS_OF_SIGNAL, &phy->phy_events_pending);
  39        phy->error = 0;
  40        sas_deform_port(phy, 1);
  41}
  42
  43static void sas_phye_oob_done(struct work_struct *work)
  44{
  45        struct asd_sas_event *ev = to_asd_sas_event(work);
  46        struct asd_sas_phy *phy = ev->phy;
  47
  48        clear_bit(PHYE_OOB_DONE, &phy->phy_events_pending);
  49        phy->error = 0;
  50}
  51
  52static void sas_phye_oob_error(struct work_struct *work)
  53{
  54        struct asd_sas_event *ev = to_asd_sas_event(work);
  55        struct asd_sas_phy *phy = ev->phy;
  56        struct sas_ha_struct *sas_ha = phy->ha;
  57        struct asd_sas_port *port = phy->port;
  58        struct sas_internal *i =
  59                to_sas_internal(sas_ha->core.shost->transportt);
  60
  61        clear_bit(PHYE_OOB_ERROR, &phy->phy_events_pending);
  62
  63        sas_deform_port(phy, 1);
  64
  65        if (!port && phy->enabled && i->dft->lldd_control_phy) {
  66                phy->error++;
  67                switch (phy->error) {
  68                case 1:
  69                case 2:
  70                        i->dft->lldd_control_phy(phy, PHY_FUNC_HARD_RESET,
  71                                                 NULL);
  72                        break;
  73                case 3:
  74                default:
  75                        phy->error = 0;
  76                        phy->enabled = 0;
  77                        i->dft->lldd_control_phy(phy, PHY_FUNC_DISABLE, NULL);
  78                        break;
  79                }
  80        }
  81}
  82
  83static void sas_phye_spinup_hold(struct work_struct *work)
  84{
  85        struct asd_sas_event *ev = to_asd_sas_event(work);
  86        struct asd_sas_phy *phy = ev->phy;
  87        struct sas_ha_struct *sas_ha = phy->ha;
  88        struct sas_internal *i =
  89                to_sas_internal(sas_ha->core.shost->transportt);
  90
  91        clear_bit(PHYE_SPINUP_HOLD, &phy->phy_events_pending);
  92
  93        phy->error = 0;
  94        i->dft->lldd_control_phy(phy, PHY_FUNC_RELEASE_SPINUP_HOLD, NULL);
  95}
  96
  97static void sas_phye_resume_timeout(struct work_struct *work)
  98{
  99        struct asd_sas_event *ev = to_asd_sas_event(work);
 100        struct asd_sas_phy *phy = ev->phy;
 101
 102        clear_bit(PHYE_RESUME_TIMEOUT, &phy->phy_events_pending);
 103
 104        /* phew, lldd got the phy back in the nick of time */
 105        if (!phy->suspended) {
 106                dev_info(&phy->phy->dev, "resume timeout cancelled\n");
 107                return;
 108        }
 109
 110        phy->error = 0;
 111        phy->suspended = 0;
 112        sas_deform_port(phy, 1);
 113}
 114
 115
 116/* ---------- Phy class registration ---------- */
 117
 118int sas_register_phys(struct sas_ha_struct *sas_ha)
 119{
 120        int i;
 121
 122        static const work_func_t sas_phy_event_fns[PHY_NUM_EVENTS] = {
 123                [PHYE_LOSS_OF_SIGNAL] = sas_phye_loss_of_signal,
 124                [PHYE_OOB_DONE] = sas_phye_oob_done,
 125                [PHYE_OOB_ERROR] = sas_phye_oob_error,
 126                [PHYE_SPINUP_HOLD] = sas_phye_spinup_hold,
 127                [PHYE_RESUME_TIMEOUT] = sas_phye_resume_timeout,
 128
 129        };
 130
 131        static const work_func_t sas_port_event_fns[PORT_NUM_EVENTS] = {
 132                [PORTE_BYTES_DMAED] = sas_porte_bytes_dmaed,
 133                [PORTE_BROADCAST_RCVD] = sas_porte_broadcast_rcvd,
 134                [PORTE_LINK_RESET_ERR] = sas_porte_link_reset_err,
 135                [PORTE_TIMER_EVENT] = sas_porte_timer_event,
 136                [PORTE_HARD_RESET] = sas_porte_hard_reset,
 137        };
 138
 139        /* Now register the phys. */
 140        for (i = 0; i < sas_ha->num_phys; i++) {
 141                int k;
 142                struct asd_sas_phy *phy = sas_ha->sas_phy[i];
 143
 144                phy->error = 0;
 145                INIT_LIST_HEAD(&phy->port_phy_el);
 146                for (k = 0; k < PORT_NUM_EVENTS; k++) {
 147                        INIT_SAS_WORK(&phy->port_events[k].work, sas_port_event_fns[k]);
 148                        phy->port_events[k].phy = phy;
 149                }
 150
 151                for (k = 0; k < PHY_NUM_EVENTS; k++) {
 152                        INIT_SAS_WORK(&phy->phy_events[k].work, sas_phy_event_fns[k]);
 153                        phy->phy_events[k].phy = phy;
 154                }
 155
 156                phy->port = NULL;
 157                phy->ha = sas_ha;
 158                spin_lock_init(&phy->frame_rcvd_lock);
 159                spin_lock_init(&phy->sas_prim_lock);
 160                phy->frame_rcvd_size = 0;
 161
 162                phy->phy = sas_phy_alloc(&sas_ha->core.shost->shost_gendev, i);
 163                if (!phy->phy)
 164                        return -ENOMEM;
 165
 166                phy->phy->identify.initiator_port_protocols =
 167                        phy->iproto;
 168                phy->phy->identify.target_port_protocols = phy->tproto;
 169                phy->phy->identify.sas_address = SAS_ADDR(sas_ha->sas_addr);
 170                phy->phy->identify.phy_identifier = i;
 171                phy->phy->minimum_linkrate_hw = SAS_LINK_RATE_UNKNOWN;
 172                phy->phy->maximum_linkrate_hw = SAS_LINK_RATE_UNKNOWN;
 173                phy->phy->minimum_linkrate = SAS_LINK_RATE_UNKNOWN;
 174                phy->phy->maximum_linkrate = SAS_LINK_RATE_UNKNOWN;
 175                phy->phy->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN;
 176
 177                sas_phy_add(phy->phy);
 178        }
 179
 180        return 0;
 181}
 182