[SCSI] libsas: Add a sysfs knob to enable/disable a phy
authorDarrick J. Wong <djwong@us.ibm.com>
Thu, 11 Jan 2007 22:14:57 +0000 (14:14 -0800)
committerJames Bottomley <jejb@mulgrave.il.steeleye.com>
Sat, 13 Jan 2007 22:13:00 +0000 (16:13 -0600)
This patch lets a user arbitrarily enable or disable a phy via sysfs.
Potential applications include shutting down a phy to replace one
lane of wide port, and (more importantly) providing a method for the
libata SATL to control the phy.

Signed-off-by: Darrick J. Wong <djwong@us.ibm.com>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
drivers/scsi/libsas/sas_init.c
drivers/scsi/libsas/sas_scsi_host.c
drivers/scsi/scsi_transport_sas.c
include/scsi/libsas.h
include/scsi/scsi_transport_sas.h

index 2f0c07fc3f48c1da732c37e4862727b976595432..90cce34cb6f5e20a671e5b8d1b160dda5cf0cf41 100644 (file)
@@ -146,6 +146,36 @@ static int sas_get_linkerrors(struct sas_phy *phy)
        return sas_smp_get_phy_events(phy);
 }
 
+int sas_phy_enable(struct sas_phy *phy, int enable)
+{
+       int ret;
+       enum phy_func command;
+
+       if (enable)
+               command = PHY_FUNC_LINK_RESET;
+       else
+               command = PHY_FUNC_DISABLE;
+
+       if (scsi_is_sas_phy_local(phy)) {
+               struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
+               struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
+               struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number];
+               struct sas_internal *i =
+                       to_sas_internal(sas_ha->core.shost->transportt);
+
+               if (!enable) {
+                       sas_phy_disconnected(asd_phy);
+                       sas_ha->notify_phy_event(asd_phy, PHYE_LOSS_OF_SIGNAL);
+               }
+               ret = i->dft->lldd_control_phy(asd_phy, command, NULL);
+       } else {
+               struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent);
+               struct domain_device *ddev = sas_find_dev_by_rphy(rphy);
+               ret = sas_smp_phy_control(ddev, phy->number, command, NULL);
+       }
+       return ret;
+}
+
 int sas_phy_reset(struct sas_phy *phy, int hard_reset)
 {
        int ret;
@@ -172,8 +202,8 @@ int sas_phy_reset(struct sas_phy *phy, int hard_reset)
        return ret;
 }
 
-static int sas_set_phy_speed(struct sas_phy *phy,
-                            struct sas_phy_linkrates *rates)
+int sas_set_phy_speed(struct sas_phy *phy,
+                     struct sas_phy_linkrates *rates)
 {
        int ret;
 
@@ -212,6 +242,7 @@ static int sas_set_phy_speed(struct sas_phy *phy,
 }
 
 static struct sas_function_template sft = {
+       .phy_enable = sas_phy_enable,
        .phy_reset = sas_phy_reset,
        .set_phy_speed = sas_set_phy_speed,
        .get_linkerrors = sas_get_linkerrors,
index 21bd2477598086c077dc2e80decb850b947adbce..7774eb3628ad99d1ef25888ef23b02262e7c20db 100644 (file)
@@ -875,3 +875,4 @@ EXPORT_SYMBOL_GPL(sas_change_queue_type);
 EXPORT_SYMBOL_GPL(sas_bios_param);
 EXPORT_SYMBOL_GPL(sas_task_abort);
 EXPORT_SYMBOL_GPL(sas_phy_reset);
+EXPORT_SYMBOL_GPL(sas_phy_enable);
index 5c0b75bbfa10c9a78e2a9536ce3d12b83f789441..9e38c1894bb2625562bf847decebf69466868264 100644 (file)
@@ -336,6 +336,51 @@ show_sas_device_type(struct class_device *cdev, char *buf)
 }
 static CLASS_DEVICE_ATTR(device_type, S_IRUGO, show_sas_device_type, NULL);
 
+static ssize_t do_sas_phy_enable(struct class_device *cdev,
+               size_t count, int enable)
+{
+       struct sas_phy *phy = transport_class_to_phy(cdev);
+       struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
+       struct sas_internal *i = to_sas_internal(shost->transportt);
+       int error;
+
+       error = i->f->phy_enable(phy, enable);
+       if (error)
+               return error;
+       phy->enabled = enable;
+       return count;
+};
+
+static ssize_t store_sas_phy_enable(struct class_device *cdev,
+               const char *buf, size_t count)
+{
+       if (count < 1)
+               return -EINVAL;
+
+       switch (buf[0]) {
+       case '0':
+               do_sas_phy_enable(cdev, count, 0);
+               break;
+       case '1':
+               do_sas_phy_enable(cdev, count, 1);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return count;
+}
+
+static ssize_t show_sas_phy_enable(struct class_device *cdev, char *buf)
+{
+       struct sas_phy *phy = transport_class_to_phy(cdev);
+
+       return snprintf(buf, 20, "%d", phy->enabled);
+}
+
+static CLASS_DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, show_sas_phy_enable,
+                        store_sas_phy_enable);
+
 static ssize_t do_sas_phy_reset(struct class_device *cdev,
                size_t count, int hard_reset)
 {
@@ -435,6 +480,7 @@ struct sas_phy *sas_phy_alloc(struct device *parent, int number)
                return NULL;
 
        phy->number = number;
+       phy->enabled = 1;
 
        device_initialize(&phy->dev);
        phy->dev.parent = get_device(parent);
@@ -1389,6 +1435,10 @@ static int sas_user_scan(struct Scsi_Host *shost, uint channel,
        SETUP_TEMPLATE_RW(phy_attrs, field, S_IRUGO | S_IWUSR, 1,       \
                        !i->f->set_phy_speed, S_IRUGO)
 
+#define SETUP_OPTIONAL_PHY_ATTRIBUTE_RW(field, func)                   \
+       SETUP_TEMPLATE_RW(phy_attrs, field, S_IRUGO | S_IWUSR, 1,       \
+                         !i->f->func, S_IRUGO)
+
 #define SETUP_PORT_ATTRIBUTE(field)                                    \
        SETUP_TEMPLATE(port_attrs, field, S_IRUGO, 1)
 
@@ -1479,6 +1529,7 @@ sas_attach_transport(struct sas_function_template *ft)
        SETUP_PHY_ATTRIBUTE(phy_reset_problem_count);
        SETUP_OPTIONAL_PHY_ATTRIBUTE_WRONLY(link_reset, phy_reset);
        SETUP_OPTIONAL_PHY_ATTRIBUTE_WRONLY(hard_reset, phy_reset);
+       SETUP_OPTIONAL_PHY_ATTRIBUTE_RW(enable, phy_enable);
        i->phy_attrs[count] = NULL;
 
        count = 0;
index da96bcfb9e468ebb33dbb8cbb2fcc16688737e6b..0689d6682aa328b94be879e3765fdfb59f109bf5 100644 (file)
@@ -614,6 +614,9 @@ struct sas_domain_function_template {
 extern int sas_register_ha(struct sas_ha_struct *);
 extern int sas_unregister_ha(struct sas_ha_struct *);
 
+int sas_set_phy_speed(struct sas_phy *phy,
+                     struct sas_phy_linkrates *rates);
+int sas_phy_enable(struct sas_phy *phy, int enabled);
 int sas_phy_reset(struct sas_phy *phy, int hard_reset);
 extern int sas_queuecommand(struct scsi_cmnd *,
                     void (*scsi_done)(struct scsi_cmnd *));
index 59633a82de47cd54dc6fa2cb6fcf09b9a1e270d4..45d58598c3eeb7fd80c14db87512ca3525ce5730 100644 (file)
@@ -54,6 +54,7 @@ struct sas_identify {
 struct sas_phy {
        struct device           dev;
        int                     number;
+       int                     enabled;
 
        /* phy identification */
        struct sas_identify     identify;
@@ -163,6 +164,7 @@ struct sas_function_template {
        int (*get_enclosure_identifier)(struct sas_rphy *, u64 *);
        int (*get_bay_identifier)(struct sas_rphy *);
        int (*phy_reset)(struct sas_phy *, int);
+       int (*phy_enable)(struct sas_phy *, int);
        int (*set_phy_speed)(struct sas_phy *, struct sas_phy_linkrates *);
 };