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 =
  36                container_of(work, struct asd_sas_event, work);
  37        struct asd_sas_phy *phy = ev->phy;
  38
  39        sas_begin_event(PHYE_LOSS_OF_SIGNAL, &phy->ha->event_lock,
  40                        &phy->phy_events_pending);
  41        phy->error = 0;
  42        sas_deform_port(phy);
  43}
  44
  45static void sas_phye_oob_done(struct work_struct *work)
  46{
  47        struct asd_sas_event *ev =
  48                container_of(work, struct asd_sas_event, work);
  49        struct asd_sas_phy *phy = ev->phy;
  50
  51        sas_begin_event(PHYE_OOB_DONE, &phy->ha->event_lock,
  52                        &phy->phy_events_pending);
  53        phy->error = 0;
  54}
  55
  56static void sas_phye_oob_error(struct work_struct *work)
  57{
  58        struct asd_sas_event *ev =
  59                container_of(work, struct asd_sas_event, work);
  60        struct asd_sas_phy *phy = ev->phy;
  61        struct sas_ha_struct *sas_ha = phy->ha;
  62        struct asd_sas_port *port = phy->port;
  63        struct sas_internal *i =
  64                to_sas_internal(sas_ha->core.shost->transportt);
  65
  66        sas_begin_event(PHYE_OOB_ERROR, &phy->ha->event_lock,
  67                        &phy->phy_events_pending);
  68
  69        sas_deform_port(phy);
  70
  71        if (!port && phy->enabled && i->dft->lldd_control_phy) {
  72                phy->error++;
  73                switch (phy->error) {
  74                case 1:
  75                case 2:
  76                        i->dft->lldd_control_phy(phy, PHY_FUNC_HARD_RESET,
  77                                                 NULL);
  78                        break;
  79                case 3:
  80                default:
  81                        phy->error = 0;
  82                        phy->enabled = 0;
  83                        i->dft->lldd_control_phy(phy, PHY_FUNC_DISABLE, NULL);
  84                        break;
  85                }
  86        }
  87}
  88
  89static void sas_phye_spinup_hold(struct work_struct *work)
  90{
  91        struct asd_sas_event *ev =
  92                container_of(work, struct asd_sas_event, work);
  93        struct asd_sas_phy *phy = ev->phy;
  94        struct sas_ha_struct *sas_ha = phy->ha;
  95        struct sas_internal *i =
  96                to_sas_internal(sas_ha->core.shost->transportt);
  97
  98        sas_begin_event(PHYE_SPINUP_HOLD, &phy->ha->event_lock,
  99                        &phy->phy_events_pending);
 100
 101        phy->error = 0;
 102        i->dft->lldd_control_phy(phy, PHY_FUNC_RELEASE_SPINUP_HOLD, NULL);
 103}
 104
 105/* ---------- Phy class registration ---------- */
 106
 107int sas_register_phys(struct sas_ha_struct *sas_ha)
 108{
 109        int i;
 110
 111        static const work_func_t sas_phy_event_fns[PHY_NUM_EVENTS] = {
 112                [PHYE_LOSS_OF_SIGNAL] = sas_phye_loss_of_signal,
 113                [PHYE_OOB_DONE] = sas_phye_oob_done,
 114                [PHYE_OOB_ERROR] = sas_phye_oob_error,
 115                [PHYE_SPINUP_HOLD] = sas_phye_spinup_hold,
 116        };
 117
 118        static const work_func_t sas_port_event_fns[PORT_NUM_EVENTS] = {
 119                [PORTE_BYTES_DMAED] = sas_porte_bytes_dmaed,
 120                [PORTE_BROADCAST_RCVD] = sas_porte_broadcast_rcvd,
 121                [PORTE_LINK_RESET_ERR] = sas_porte_link_reset_err,
 122                [PORTE_TIMER_EVENT] = sas_porte_timer_event,
 123                [PORTE_HARD_RESET] = sas_porte_hard_reset,
 124        };
 125
 126        /* Now register the phys. */
 127        for (i = 0; i < sas_ha->num_phys; i++) {
 128                int k;
 129                struct asd_sas_phy *phy = sas_ha->sas_phy[i];
 130
 131                phy->error = 0;
 132                INIT_LIST_HEAD(&phy->port_phy_el);
 133                for (k = 0; k < PORT_NUM_EVENTS; k++) {
 134                        INIT_WORK(&phy->port_events[k].work,
 135                                  sas_port_event_fns[k]);
 136                        phy->port_events[k].phy = phy;
 137                }
 138
 139                for (k = 0; k < PHY_NUM_EVENTS; k++) {
 140                        INIT_WORK(&phy->phy_events[k].work,
 141                                  sas_phy_event_fns[k]);
 142                        phy->phy_events[k].phy = phy;
 143                }
 144
 145                phy->port = NULL;
 146                phy->ha = sas_ha;
 147                spin_lock_init(&phy->frame_rcvd_lock);
 148                spin_lock_init(&phy->sas_prim_lock);
 149                phy->frame_rcvd_size = 0;
 150
 151                phy->phy = sas_phy_alloc(&sas_ha->core.shost->shost_gendev,
 152                                         i);
 153                if (!phy->phy)
 154                        return -ENOMEM;
 155
 156                phy->phy->identify.initiator_port_protocols =
 157                        phy->iproto;
 158                phy->phy->identify.target_port_protocols = phy->tproto;
 159                phy->phy->identify.sas_address = SAS_ADDR(sas_ha->sas_addr);
 160                phy->phy->identify.phy_identifier = i;
 161                phy->phy->minimum_linkrate_hw = SAS_LINK_RATE_UNKNOWN;
 162                phy->phy->maximum_linkrate_hw = SAS_LINK_RATE_UNKNOWN;
 163                phy->phy->minimum_linkrate = SAS_LINK_RATE_UNKNOWN;
 164                phy->phy->maximum_linkrate = SAS_LINK_RATE_UNKNOWN;
 165                phy->phy->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN;
 166
 167                sas_phy_add(phy->phy);
 168        }
 169
 170        return 0;
 171}
 172