diff options
author | Darrick J. Wong <djwong@us.ibm.com> | 2007-01-11 17:14:57 -0500 |
---|---|---|
committer | James Bottomley <jejb@mulgrave.il.steeleye.com> | 2007-01-13 17:13:00 -0500 |
commit | acbf167d4ad8c27f9743a4b539d51ae9535bf21c (patch) | |
tree | e53f8330f60acaef42d74a745b3dd5131b424e49 /drivers | |
parent | b218a0d8e250e0ae8fd4d4e45bd66a588b380752 (diff) |
[SCSI] libsas: Add a sysfs knob to enable/disable a phy
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>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/scsi/libsas/sas_init.c | 35 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_scsi_host.c | 1 | ||||
-rw-r--r-- | drivers/scsi/scsi_transport_sas.c | 51 |
3 files changed, 85 insertions, 2 deletions
diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index 2f0c07fc3f48..90cce34cb6f5 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c | |||
@@ -146,6 +146,36 @@ static int sas_get_linkerrors(struct sas_phy *phy) | |||
146 | return sas_smp_get_phy_events(phy); | 146 | return sas_smp_get_phy_events(phy); |
147 | } | 147 | } |
148 | 148 | ||
149 | int sas_phy_enable(struct sas_phy *phy, int enable) | ||
150 | { | ||
151 | int ret; | ||
152 | enum phy_func command; | ||
153 | |||
154 | if (enable) | ||
155 | command = PHY_FUNC_LINK_RESET; | ||
156 | else | ||
157 | command = PHY_FUNC_DISABLE; | ||
158 | |||
159 | if (scsi_is_sas_phy_local(phy)) { | ||
160 | struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); | ||
161 | struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); | ||
162 | struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number]; | ||
163 | struct sas_internal *i = | ||
164 | to_sas_internal(sas_ha->core.shost->transportt); | ||
165 | |||
166 | if (!enable) { | ||
167 | sas_phy_disconnected(asd_phy); | ||
168 | sas_ha->notify_phy_event(asd_phy, PHYE_LOSS_OF_SIGNAL); | ||
169 | } | ||
170 | ret = i->dft->lldd_control_phy(asd_phy, command, NULL); | ||
171 | } else { | ||
172 | struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent); | ||
173 | struct domain_device *ddev = sas_find_dev_by_rphy(rphy); | ||
174 | ret = sas_smp_phy_control(ddev, phy->number, command, NULL); | ||
175 | } | ||
176 | return ret; | ||
177 | } | ||
178 | |||
149 | int sas_phy_reset(struct sas_phy *phy, int hard_reset) | 179 | int sas_phy_reset(struct sas_phy *phy, int hard_reset) |
150 | { | 180 | { |
151 | int ret; | 181 | int ret; |
@@ -172,8 +202,8 @@ int sas_phy_reset(struct sas_phy *phy, int hard_reset) | |||
172 | return ret; | 202 | return ret; |
173 | } | 203 | } |
174 | 204 | ||
175 | static int sas_set_phy_speed(struct sas_phy *phy, | 205 | int sas_set_phy_speed(struct sas_phy *phy, |
176 | struct sas_phy_linkrates *rates) | 206 | struct sas_phy_linkrates *rates) |
177 | { | 207 | { |
178 | int ret; | 208 | int ret; |
179 | 209 | ||
@@ -212,6 +242,7 @@ static int sas_set_phy_speed(struct sas_phy *phy, | |||
212 | } | 242 | } |
213 | 243 | ||
214 | static struct sas_function_template sft = { | 244 | static struct sas_function_template sft = { |
245 | .phy_enable = sas_phy_enable, | ||
215 | .phy_reset = sas_phy_reset, | 246 | .phy_reset = sas_phy_reset, |
216 | .set_phy_speed = sas_set_phy_speed, | 247 | .set_phy_speed = sas_set_phy_speed, |
217 | .get_linkerrors = sas_get_linkerrors, | 248 | .get_linkerrors = sas_get_linkerrors, |
diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 21bd24775980..7774eb3628ad 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c | |||
@@ -875,3 +875,4 @@ EXPORT_SYMBOL_GPL(sas_change_queue_type); | |||
875 | EXPORT_SYMBOL_GPL(sas_bios_param); | 875 | EXPORT_SYMBOL_GPL(sas_bios_param); |
876 | EXPORT_SYMBOL_GPL(sas_task_abort); | 876 | EXPORT_SYMBOL_GPL(sas_task_abort); |
877 | EXPORT_SYMBOL_GPL(sas_phy_reset); | 877 | EXPORT_SYMBOL_GPL(sas_phy_reset); |
878 | EXPORT_SYMBOL_GPL(sas_phy_enable); | ||
diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c index 5c0b75bbfa10..9e38c1894bb2 100644 --- a/drivers/scsi/scsi_transport_sas.c +++ b/drivers/scsi/scsi_transport_sas.c | |||
@@ -336,6 +336,51 @@ show_sas_device_type(struct class_device *cdev, char *buf) | |||
336 | } | 336 | } |
337 | static CLASS_DEVICE_ATTR(device_type, S_IRUGO, show_sas_device_type, NULL); | 337 | static CLASS_DEVICE_ATTR(device_type, S_IRUGO, show_sas_device_type, NULL); |
338 | 338 | ||
339 | static ssize_t do_sas_phy_enable(struct class_device *cdev, | ||
340 | size_t count, int enable) | ||
341 | { | ||
342 | struct sas_phy *phy = transport_class_to_phy(cdev); | ||
343 | struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); | ||
344 | struct sas_internal *i = to_sas_internal(shost->transportt); | ||
345 | int error; | ||
346 | |||
347 | error = i->f->phy_enable(phy, enable); | ||
348 | if (error) | ||
349 | return error; | ||
350 | phy->enabled = enable; | ||
351 | return count; | ||
352 | }; | ||
353 | |||
354 | static ssize_t store_sas_phy_enable(struct class_device *cdev, | ||
355 | const char *buf, size_t count) | ||
356 | { | ||
357 | if (count < 1) | ||
358 | return -EINVAL; | ||
359 | |||
360 | switch (buf[0]) { | ||
361 | case '0': | ||
362 | do_sas_phy_enable(cdev, count, 0); | ||
363 | break; | ||
364 | case '1': | ||
365 | do_sas_phy_enable(cdev, count, 1); | ||
366 | break; | ||
367 | default: | ||
368 | return -EINVAL; | ||
369 | } | ||
370 | |||
371 | return count; | ||
372 | } | ||
373 | |||
374 | static ssize_t show_sas_phy_enable(struct class_device *cdev, char *buf) | ||
375 | { | ||
376 | struct sas_phy *phy = transport_class_to_phy(cdev); | ||
377 | |||
378 | return snprintf(buf, 20, "%d", phy->enabled); | ||
379 | } | ||
380 | |||
381 | static CLASS_DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, show_sas_phy_enable, | ||
382 | store_sas_phy_enable); | ||
383 | |||
339 | static ssize_t do_sas_phy_reset(struct class_device *cdev, | 384 | static ssize_t do_sas_phy_reset(struct class_device *cdev, |
340 | size_t count, int hard_reset) | 385 | size_t count, int hard_reset) |
341 | { | 386 | { |
@@ -435,6 +480,7 @@ struct sas_phy *sas_phy_alloc(struct device *parent, int number) | |||
435 | return NULL; | 480 | return NULL; |
436 | 481 | ||
437 | phy->number = number; | 482 | phy->number = number; |
483 | phy->enabled = 1; | ||
438 | 484 | ||
439 | device_initialize(&phy->dev); | 485 | device_initialize(&phy->dev); |
440 | phy->dev.parent = get_device(parent); | 486 | phy->dev.parent = get_device(parent); |
@@ -1389,6 +1435,10 @@ static int sas_user_scan(struct Scsi_Host *shost, uint channel, | |||
1389 | SETUP_TEMPLATE_RW(phy_attrs, field, S_IRUGO | S_IWUSR, 1, \ | 1435 | SETUP_TEMPLATE_RW(phy_attrs, field, S_IRUGO | S_IWUSR, 1, \ |
1390 | !i->f->set_phy_speed, S_IRUGO) | 1436 | !i->f->set_phy_speed, S_IRUGO) |
1391 | 1437 | ||
1438 | #define SETUP_OPTIONAL_PHY_ATTRIBUTE_RW(field, func) \ | ||
1439 | SETUP_TEMPLATE_RW(phy_attrs, field, S_IRUGO | S_IWUSR, 1, \ | ||
1440 | !i->f->func, S_IRUGO) | ||
1441 | |||
1392 | #define SETUP_PORT_ATTRIBUTE(field) \ | 1442 | #define SETUP_PORT_ATTRIBUTE(field) \ |
1393 | SETUP_TEMPLATE(port_attrs, field, S_IRUGO, 1) | 1443 | SETUP_TEMPLATE(port_attrs, field, S_IRUGO, 1) |
1394 | 1444 | ||
@@ -1479,6 +1529,7 @@ sas_attach_transport(struct sas_function_template *ft) | |||
1479 | SETUP_PHY_ATTRIBUTE(phy_reset_problem_count); | 1529 | SETUP_PHY_ATTRIBUTE(phy_reset_problem_count); |
1480 | SETUP_OPTIONAL_PHY_ATTRIBUTE_WRONLY(link_reset, phy_reset); | 1530 | SETUP_OPTIONAL_PHY_ATTRIBUTE_WRONLY(link_reset, phy_reset); |
1481 | SETUP_OPTIONAL_PHY_ATTRIBUTE_WRONLY(hard_reset, phy_reset); | 1531 | SETUP_OPTIONAL_PHY_ATTRIBUTE_WRONLY(hard_reset, phy_reset); |
1532 | SETUP_OPTIONAL_PHY_ATTRIBUTE_RW(enable, phy_enable); | ||
1482 | i->phy_attrs[count] = NULL; | 1533 | i->phy_attrs[count] = NULL; |
1483 | 1534 | ||
1484 | count = 0; | 1535 | count = 0; |