mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	[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>
This commit is contained in:
		
							parent
							
								
									b218a0d8e2
								
							
						
					
					
						commit
						acbf167d4a
					
				
					 5 changed files with 90 additions and 2 deletions
				
			
		| 
						 | 
					@ -146,6 +146,36 @@ static int sas_get_linkerrors(struct sas_phy *phy)
 | 
				
			||||||
	return sas_smp_get_phy_events(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 sas_phy_reset(struct sas_phy *phy, int hard_reset)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
| 
						 | 
					@ -172,7 +202,7 @@ int sas_phy_reset(struct sas_phy *phy, int hard_reset)
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int sas_set_phy_speed(struct sas_phy *phy,
 | 
					int sas_set_phy_speed(struct sas_phy *phy,
 | 
				
			||||||
		      struct sas_phy_linkrates *rates)
 | 
							      struct sas_phy_linkrates *rates)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
| 
						 | 
					@ -212,6 +242,7 @@ static int sas_set_phy_speed(struct sas_phy *phy,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct sas_function_template sft = {
 | 
					static struct sas_function_template sft = {
 | 
				
			||||||
 | 
						.phy_enable = sas_phy_enable,
 | 
				
			||||||
	.phy_reset = sas_phy_reset,
 | 
						.phy_reset = sas_phy_reset,
 | 
				
			||||||
	.set_phy_speed = sas_set_phy_speed,
 | 
						.set_phy_speed = sas_set_phy_speed,
 | 
				
			||||||
	.get_linkerrors = sas_get_linkerrors,
 | 
						.get_linkerrors = sas_get_linkerrors,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -875,3 +875,4 @@ EXPORT_SYMBOL_GPL(sas_change_queue_type);
 | 
				
			||||||
EXPORT_SYMBOL_GPL(sas_bios_param);
 | 
					EXPORT_SYMBOL_GPL(sas_bios_param);
 | 
				
			||||||
EXPORT_SYMBOL_GPL(sas_task_abort);
 | 
					EXPORT_SYMBOL_GPL(sas_task_abort);
 | 
				
			||||||
EXPORT_SYMBOL_GPL(sas_phy_reset);
 | 
					EXPORT_SYMBOL_GPL(sas_phy_reset);
 | 
				
			||||||
 | 
					EXPORT_SYMBOL_GPL(sas_phy_enable);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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 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,
 | 
					static ssize_t do_sas_phy_reset(struct class_device *cdev,
 | 
				
			||||||
		size_t count, int hard_reset)
 | 
							size_t count, int hard_reset)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -435,6 +480,7 @@ struct sas_phy *sas_phy_alloc(struct device *parent, int number)
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	phy->number = number;
 | 
						phy->number = number;
 | 
				
			||||||
 | 
						phy->enabled = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	device_initialize(&phy->dev);
 | 
						device_initialize(&phy->dev);
 | 
				
			||||||
	phy->dev.parent = get_device(parent);
 | 
						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,	\
 | 
						SETUP_TEMPLATE_RW(phy_attrs, field, S_IRUGO | S_IWUSR, 1,	\
 | 
				
			||||||
			!i->f->set_phy_speed, S_IRUGO)
 | 
								!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)					\
 | 
					#define SETUP_PORT_ATTRIBUTE(field)					\
 | 
				
			||||||
	SETUP_TEMPLATE(port_attrs, field, S_IRUGO, 1)
 | 
						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_PHY_ATTRIBUTE(phy_reset_problem_count);
 | 
				
			||||||
	SETUP_OPTIONAL_PHY_ATTRIBUTE_WRONLY(link_reset, phy_reset);
 | 
						SETUP_OPTIONAL_PHY_ATTRIBUTE_WRONLY(link_reset, phy_reset);
 | 
				
			||||||
	SETUP_OPTIONAL_PHY_ATTRIBUTE_WRONLY(hard_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;
 | 
						i->phy_attrs[count] = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	count = 0;
 | 
						count = 0;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -614,6 +614,9 @@ struct sas_domain_function_template {
 | 
				
			||||||
extern int sas_register_ha(struct sas_ha_struct *);
 | 
					extern int sas_register_ha(struct sas_ha_struct *);
 | 
				
			||||||
extern int sas_unregister_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);
 | 
					int sas_phy_reset(struct sas_phy *phy, int hard_reset);
 | 
				
			||||||
extern int sas_queuecommand(struct scsi_cmnd *,
 | 
					extern int sas_queuecommand(struct scsi_cmnd *,
 | 
				
			||||||
		     void (*scsi_done)(struct scsi_cmnd *));
 | 
							     void (*scsi_done)(struct scsi_cmnd *));
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -54,6 +54,7 @@ struct sas_identify {
 | 
				
			||||||
struct sas_phy {
 | 
					struct sas_phy {
 | 
				
			||||||
	struct device		dev;
 | 
						struct device		dev;
 | 
				
			||||||
	int			number;
 | 
						int			number;
 | 
				
			||||||
 | 
						int			enabled;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* phy identification */
 | 
						/* phy identification */
 | 
				
			||||||
	struct sas_identify	identify;
 | 
						struct sas_identify	identify;
 | 
				
			||||||
| 
						 | 
					@ -163,6 +164,7 @@ struct sas_function_template {
 | 
				
			||||||
	int (*get_enclosure_identifier)(struct sas_rphy *, u64 *);
 | 
						int (*get_enclosure_identifier)(struct sas_rphy *, u64 *);
 | 
				
			||||||
	int (*get_bay_identifier)(struct sas_rphy *);
 | 
						int (*get_bay_identifier)(struct sas_rphy *);
 | 
				
			||||||
	int (*phy_reset)(struct sas_phy *, int);
 | 
						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 *);
 | 
						int (*set_phy_speed)(struct sas_phy *, struct sas_phy_linkrates *);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue