1c7ebbbceSChristoph Hellwig /*
2c7ebbbceSChristoph Hellwig  * Copyright (C) 2005 Dell Inc.
3c7ebbbceSChristoph Hellwig  *	Released under GPL v2.
4c7ebbbceSChristoph Hellwig  *
5c7ebbbceSChristoph Hellwig  * Serial Attached SCSI (SAS) transport class.
6c7ebbbceSChristoph Hellwig  *
7c7ebbbceSChristoph Hellwig  * The SAS transport class contains common code to deal with SAS HBAs,
8c7ebbbceSChristoph Hellwig  * an aproximated representation of SAS topologies in the driver model,
9c7ebbbceSChristoph Hellwig  * and various sysfs attributes to expose these topologies and managment
10c7ebbbceSChristoph Hellwig  * interfaces to userspace.
11c7ebbbceSChristoph Hellwig  *
12c7ebbbceSChristoph Hellwig  * In addition to the basic SCSI core objects this transport class
13c7ebbbceSChristoph Hellwig  * introduces two additional intermediate objects:  The SAS PHY
14c7ebbbceSChristoph Hellwig  * as represented by struct sas_phy defines an "outgoing" PHY on
15c7ebbbceSChristoph Hellwig  * a SAS HBA or Expander, and the SAS remote PHY represented by
16c7ebbbceSChristoph Hellwig  * struct sas_rphy defines an "incoming" PHY on a SAS Expander or
17c7ebbbceSChristoph Hellwig  * end device.  Note that this is purely a software concept, the
18c7ebbbceSChristoph Hellwig  * underlying hardware for a PHY and a remote PHY is the exactly
19c7ebbbceSChristoph Hellwig  * the same.
20c7ebbbceSChristoph Hellwig  *
21c7ebbbceSChristoph Hellwig  * There is no concept of a SAS port in this code, users can see
22c7ebbbceSChristoph Hellwig  * what PHYs form a wide port based on the port_identifier attribute,
23c7ebbbceSChristoph Hellwig  * which is the same for all PHYs in a port.
24c7ebbbceSChristoph Hellwig  */
25c7ebbbceSChristoph Hellwig 
26c7ebbbceSChristoph Hellwig #include <linux/init.h>
27c7ebbbceSChristoph Hellwig #include <linux/module.h>
28c7ebbbceSChristoph Hellwig #include <linux/err.h>
29c7ebbbceSChristoph Hellwig 
30c7ebbbceSChristoph Hellwig #include <scsi/scsi_device.h>
31c7ebbbceSChristoph Hellwig #include <scsi/scsi_host.h>
32c7ebbbceSChristoph Hellwig #include <scsi/scsi_transport.h>
33c7ebbbceSChristoph Hellwig #include <scsi/scsi_transport_sas.h>
34c7ebbbceSChristoph Hellwig 
35c7ebbbceSChristoph Hellwig 
36c7ebbbceSChristoph Hellwig #define SAS_HOST_ATTRS		0
3707ba3a95SChristoph Hellwig #define SAS_PORT_ATTRS		17
38c7ebbbceSChristoph Hellwig #define SAS_RPORT_ATTRS		5
39c7ebbbceSChristoph Hellwig 
40c7ebbbceSChristoph Hellwig struct sas_internal {
41c7ebbbceSChristoph Hellwig 	struct scsi_transport_template t;
42c7ebbbceSChristoph Hellwig 	struct sas_function_template *f;
43c7ebbbceSChristoph Hellwig 
44c7ebbbceSChristoph Hellwig 	struct class_device_attribute private_host_attrs[SAS_HOST_ATTRS];
45c7ebbbceSChristoph Hellwig 	struct class_device_attribute private_phy_attrs[SAS_PORT_ATTRS];
46c7ebbbceSChristoph Hellwig 	struct class_device_attribute private_rphy_attrs[SAS_RPORT_ATTRS];
47c7ebbbceSChristoph Hellwig 
48c7ebbbceSChristoph Hellwig 	struct transport_container phy_attr_cont;
49c7ebbbceSChristoph Hellwig 	struct transport_container rphy_attr_cont;
50c7ebbbceSChristoph Hellwig 
51c7ebbbceSChristoph Hellwig 	/*
52c7ebbbceSChristoph Hellwig 	 * The array of null terminated pointers to attributes
53c7ebbbceSChristoph Hellwig 	 * needed by scsi_sysfs.c
54c7ebbbceSChristoph Hellwig 	 */
55c7ebbbceSChristoph Hellwig 	struct class_device_attribute *host_attrs[SAS_HOST_ATTRS + 1];
56c7ebbbceSChristoph Hellwig 	struct class_device_attribute *phy_attrs[SAS_PORT_ATTRS + 1];
57c7ebbbceSChristoph Hellwig 	struct class_device_attribute *rphy_attrs[SAS_RPORT_ATTRS + 1];
58c7ebbbceSChristoph Hellwig };
59c7ebbbceSChristoph Hellwig #define to_sas_internal(tmpl)	container_of(tmpl, struct sas_internal, t)
60c7ebbbceSChristoph Hellwig 
61c7ebbbceSChristoph Hellwig struct sas_host_attrs {
62c7ebbbceSChristoph Hellwig 	struct list_head rphy_list;
63c7ebbbceSChristoph Hellwig 	spinlock_t lock;
64c7ebbbceSChristoph Hellwig 	u32 next_target_id;
65c7ebbbceSChristoph Hellwig };
66c7ebbbceSChristoph Hellwig #define to_sas_host_attrs(host)	((struct sas_host_attrs *)(host)->shost_data)
67c7ebbbceSChristoph Hellwig 
68c7ebbbceSChristoph Hellwig 
69c7ebbbceSChristoph Hellwig /*
70c7ebbbceSChristoph Hellwig  * Hack to allow attributes of the same name in different objects.
71c7ebbbceSChristoph Hellwig  */
72c7ebbbceSChristoph Hellwig #define SAS_CLASS_DEVICE_ATTR(_prefix,_name,_mode,_show,_store) \
73c7ebbbceSChristoph Hellwig 	struct class_device_attribute class_device_attr_##_prefix##_##_name = \
74c7ebbbceSChristoph Hellwig 	__ATTR(_name,_mode,_show,_store)
75c7ebbbceSChristoph Hellwig 
76c7ebbbceSChristoph Hellwig 
77c7ebbbceSChristoph Hellwig /*
78c7ebbbceSChristoph Hellwig  * Pretty printing helpers
79c7ebbbceSChristoph Hellwig  */
80c7ebbbceSChristoph Hellwig 
81c7ebbbceSChristoph Hellwig #define sas_bitfield_name_match(title, table)			\
82c7ebbbceSChristoph Hellwig static ssize_t							\
83c7ebbbceSChristoph Hellwig get_sas_##title##_names(u32 table_key, char *buf)		\
84c7ebbbceSChristoph Hellwig {								\
85c7ebbbceSChristoph Hellwig 	char *prefix = "";					\
86c7ebbbceSChristoph Hellwig 	ssize_t len = 0;					\
87c7ebbbceSChristoph Hellwig 	int i;							\
88c7ebbbceSChristoph Hellwig 								\
89c7ebbbceSChristoph Hellwig 	for (i = 0; i < sizeof(table)/sizeof(table[0]); i++) {	\
90c7ebbbceSChristoph Hellwig 		if (table[i].value & table_key) {		\
91c7ebbbceSChristoph Hellwig 			len += sprintf(buf + len, "%s%s",	\
92c7ebbbceSChristoph Hellwig 				prefix, table[i].name);		\
93c7ebbbceSChristoph Hellwig 			prefix = ", ";				\
94c7ebbbceSChristoph Hellwig 		}						\
95c7ebbbceSChristoph Hellwig 	}							\
96c7ebbbceSChristoph Hellwig 	len += sprintf(buf + len, "\n");			\
97c7ebbbceSChristoph Hellwig 	return len;						\
98c7ebbbceSChristoph Hellwig }
99c7ebbbceSChristoph Hellwig 
100c7ebbbceSChristoph Hellwig #define sas_bitfield_name_search(title, table)			\
101c7ebbbceSChristoph Hellwig static ssize_t							\
102c7ebbbceSChristoph Hellwig get_sas_##title##_names(u32 table_key, char *buf)		\
103c7ebbbceSChristoph Hellwig {								\
104c7ebbbceSChristoph Hellwig 	ssize_t len = 0;					\
105c7ebbbceSChristoph Hellwig 	int i;							\
106c7ebbbceSChristoph Hellwig 								\
107c7ebbbceSChristoph Hellwig 	for (i = 0; i < sizeof(table)/sizeof(table[0]); i++) {	\
108c7ebbbceSChristoph Hellwig 		if (table[i].value == table_key) {		\
109c7ebbbceSChristoph Hellwig 			len += sprintf(buf + len, "%s",		\
110c7ebbbceSChristoph Hellwig 				table[i].name);			\
111c7ebbbceSChristoph Hellwig 			break;					\
112c7ebbbceSChristoph Hellwig 		}						\
113c7ebbbceSChristoph Hellwig 	}							\
114c7ebbbceSChristoph Hellwig 	len += sprintf(buf + len, "\n");			\
115c7ebbbceSChristoph Hellwig 	return len;						\
116c7ebbbceSChristoph Hellwig }
117c7ebbbceSChristoph Hellwig 
118c7ebbbceSChristoph Hellwig static struct {
119c7ebbbceSChristoph Hellwig 	u32		value;
120c7ebbbceSChristoph Hellwig 	char		*name;
121c7ebbbceSChristoph Hellwig } sas_device_type_names[] = {
122c7ebbbceSChristoph Hellwig 	{ SAS_PHY_UNUSED,		"unused" },
123c7ebbbceSChristoph Hellwig 	{ SAS_END_DEVICE,		"end device" },
124c7ebbbceSChristoph Hellwig 	{ SAS_EDGE_EXPANDER_DEVICE,	"edge expander" },
125c7ebbbceSChristoph Hellwig 	{ SAS_FANOUT_EXPANDER_DEVICE,	"fanout expander" },
126c7ebbbceSChristoph Hellwig };
127c7ebbbceSChristoph Hellwig sas_bitfield_name_search(device_type, sas_device_type_names)
128c7ebbbceSChristoph Hellwig 
129c7ebbbceSChristoph Hellwig 
130c7ebbbceSChristoph Hellwig static struct {
131c7ebbbceSChristoph Hellwig 	u32		value;
132c7ebbbceSChristoph Hellwig 	char		*name;
133c7ebbbceSChristoph Hellwig } sas_protocol_names[] = {
134c7ebbbceSChristoph Hellwig 	{ SAS_PROTOCOL_SATA,		"sata" },
135c7ebbbceSChristoph Hellwig 	{ SAS_PROTOCOL_SMP,		"smp" },
136c7ebbbceSChristoph Hellwig 	{ SAS_PROTOCOL_STP,		"stp" },
137c7ebbbceSChristoph Hellwig 	{ SAS_PROTOCOL_SSP,		"ssp" },
138c7ebbbceSChristoph Hellwig };
139c7ebbbceSChristoph Hellwig sas_bitfield_name_match(protocol, sas_protocol_names)
140c7ebbbceSChristoph Hellwig 
141c7ebbbceSChristoph Hellwig static struct {
142c7ebbbceSChristoph Hellwig 	u32		value;
143c7ebbbceSChristoph Hellwig 	char		*name;
144c7ebbbceSChristoph Hellwig } sas_linkspeed_names[] = {
145c7ebbbceSChristoph Hellwig 	{ SAS_LINK_RATE_UNKNOWN,	"Unknown" },
146c7ebbbceSChristoph Hellwig 	{ SAS_PHY_DISABLED,		"Phy disabled" },
147c7ebbbceSChristoph Hellwig 	{ SAS_LINK_RATE_FAILED,		"Link Rate failed" },
148c7ebbbceSChristoph Hellwig 	{ SAS_SATA_SPINUP_HOLD,		"Spin-up hold" },
149c7ebbbceSChristoph Hellwig 	{ SAS_LINK_RATE_1_5_GBPS,	"1.5 Gbit" },
150c7ebbbceSChristoph Hellwig 	{ SAS_LINK_RATE_3_0_GBPS,	"3.0 Gbit" },
151c7ebbbceSChristoph Hellwig };
152c7ebbbceSChristoph Hellwig sas_bitfield_name_search(linkspeed, sas_linkspeed_names)
153c7ebbbceSChristoph Hellwig 
154c7ebbbceSChristoph Hellwig 
155c7ebbbceSChristoph Hellwig /*
156c7ebbbceSChristoph Hellwig  * SAS host attributes
157c7ebbbceSChristoph Hellwig  */
158c7ebbbceSChristoph Hellwig 
15937be6eebSJames Bottomley static int sas_host_setup(struct transport_container *tc, struct device *dev,
16037be6eebSJames Bottomley 			  struct class_device *cdev)
161c7ebbbceSChristoph Hellwig {
162c7ebbbceSChristoph Hellwig 	struct Scsi_Host *shost = dev_to_shost(dev);
163c7ebbbceSChristoph Hellwig 	struct sas_host_attrs *sas_host = to_sas_host_attrs(shost);
164c7ebbbceSChristoph Hellwig 
165c7ebbbceSChristoph Hellwig 	INIT_LIST_HEAD(&sas_host->rphy_list);
166c7ebbbceSChristoph Hellwig 	spin_lock_init(&sas_host->lock);
167c7ebbbceSChristoph Hellwig 	sas_host->next_target_id = 0;
168c7ebbbceSChristoph Hellwig 	return 0;
169c7ebbbceSChristoph Hellwig }
170c7ebbbceSChristoph Hellwig 
171c7ebbbceSChristoph Hellwig static DECLARE_TRANSPORT_CLASS(sas_host_class,
172c7ebbbceSChristoph Hellwig 		"sas_host", sas_host_setup, NULL, NULL);
173c7ebbbceSChristoph Hellwig 
174c7ebbbceSChristoph Hellwig static int sas_host_match(struct attribute_container *cont,
175c7ebbbceSChristoph Hellwig 			    struct device *dev)
176c7ebbbceSChristoph Hellwig {
177c7ebbbceSChristoph Hellwig 	struct Scsi_Host *shost;
178c7ebbbceSChristoph Hellwig 	struct sas_internal *i;
179c7ebbbceSChristoph Hellwig 
180c7ebbbceSChristoph Hellwig 	if (!scsi_is_host_device(dev))
181c7ebbbceSChristoph Hellwig 		return 0;
182c7ebbbceSChristoph Hellwig 	shost = dev_to_shost(dev);
183c7ebbbceSChristoph Hellwig 
184c7ebbbceSChristoph Hellwig 	if (!shost->transportt)
185c7ebbbceSChristoph Hellwig 		return 0;
186c7ebbbceSChristoph Hellwig 	if (shost->transportt->host_attrs.ac.class !=
187c7ebbbceSChristoph Hellwig 			&sas_host_class.class)
188c7ebbbceSChristoph Hellwig 		return 0;
189c7ebbbceSChristoph Hellwig 
190c7ebbbceSChristoph Hellwig 	i = to_sas_internal(shost->transportt);
191c7ebbbceSChristoph Hellwig 	return &i->t.host_attrs.ac == cont;
192c7ebbbceSChristoph Hellwig }
193c7ebbbceSChristoph Hellwig 
194c7ebbbceSChristoph Hellwig static int do_sas_phy_delete(struct device *dev, void *data)
195c7ebbbceSChristoph Hellwig {
196c7ebbbceSChristoph Hellwig 	if (scsi_is_sas_phy(dev))
197c7ebbbceSChristoph Hellwig 		sas_phy_delete(dev_to_phy(dev));
198c7ebbbceSChristoph Hellwig 	return 0;
199c7ebbbceSChristoph Hellwig }
200c7ebbbceSChristoph Hellwig 
201c7ebbbceSChristoph Hellwig /**
202c7ebbbceSChristoph Hellwig  * sas_remove_host  --  tear down a Scsi_Host's SAS data structures
203c7ebbbceSChristoph Hellwig  * @shost:	Scsi Host that is torn down
204c7ebbbceSChristoph Hellwig  *
205c7ebbbceSChristoph Hellwig  * Removes all SAS PHYs and remote PHYs for a given Scsi_Host.
206c7ebbbceSChristoph Hellwig  * Must be called just before scsi_remove_host for SAS HBAs.
207c7ebbbceSChristoph Hellwig  */
208c7ebbbceSChristoph Hellwig void sas_remove_host(struct Scsi_Host *shost)
209c7ebbbceSChristoph Hellwig {
210c7ebbbceSChristoph Hellwig 	device_for_each_child(&shost->shost_gendev, NULL, do_sas_phy_delete);
211c7ebbbceSChristoph Hellwig }
212c7ebbbceSChristoph Hellwig EXPORT_SYMBOL(sas_remove_host);
213c7ebbbceSChristoph Hellwig 
214c7ebbbceSChristoph Hellwig 
215c7ebbbceSChristoph Hellwig /*
216c7ebbbceSChristoph Hellwig  * SAS Port attributes
217c7ebbbceSChristoph Hellwig  */
218c7ebbbceSChristoph Hellwig 
219c7ebbbceSChristoph Hellwig #define sas_phy_show_simple(field, name, format_string, cast)		\
220c7ebbbceSChristoph Hellwig static ssize_t								\
221c7ebbbceSChristoph Hellwig show_sas_phy_##name(struct class_device *cdev, char *buf)		\
222c7ebbbceSChristoph Hellwig {									\
223c7ebbbceSChristoph Hellwig 	struct sas_phy *phy = transport_class_to_phy(cdev);		\
224c7ebbbceSChristoph Hellwig 									\
225c7ebbbceSChristoph Hellwig 	return snprintf(buf, 20, format_string, cast phy->field);	\
226c7ebbbceSChristoph Hellwig }
227c7ebbbceSChristoph Hellwig 
228c7ebbbceSChristoph Hellwig #define sas_phy_simple_attr(field, name, format_string, type)		\
229c7ebbbceSChristoph Hellwig 	sas_phy_show_simple(field, name, format_string, (type))	\
230c7ebbbceSChristoph Hellwig static CLASS_DEVICE_ATTR(name, S_IRUGO, show_sas_phy_##name, NULL)
231c7ebbbceSChristoph Hellwig 
232c7ebbbceSChristoph Hellwig #define sas_phy_show_protocol(field, name)				\
233c7ebbbceSChristoph Hellwig static ssize_t								\
234c7ebbbceSChristoph Hellwig show_sas_phy_##name(struct class_device *cdev, char *buf)		\
235c7ebbbceSChristoph Hellwig {									\
236c7ebbbceSChristoph Hellwig 	struct sas_phy *phy = transport_class_to_phy(cdev);		\
237c7ebbbceSChristoph Hellwig 									\
238c7ebbbceSChristoph Hellwig 	if (!phy->field)						\
239c7ebbbceSChristoph Hellwig 		return snprintf(buf, 20, "none\n");			\
240c7ebbbceSChristoph Hellwig 	return get_sas_protocol_names(phy->field, buf);		\
241c7ebbbceSChristoph Hellwig }
242c7ebbbceSChristoph Hellwig 
243c7ebbbceSChristoph Hellwig #define sas_phy_protocol_attr(field, name)				\
244c7ebbbceSChristoph Hellwig 	sas_phy_show_protocol(field, name)				\
245c7ebbbceSChristoph Hellwig static CLASS_DEVICE_ATTR(name, S_IRUGO, show_sas_phy_##name, NULL)
246c7ebbbceSChristoph Hellwig 
247c7ebbbceSChristoph Hellwig #define sas_phy_show_linkspeed(field)					\
248c7ebbbceSChristoph Hellwig static ssize_t								\
249c7ebbbceSChristoph Hellwig show_sas_phy_##field(struct class_device *cdev, char *buf)		\
250c7ebbbceSChristoph Hellwig {									\
251c7ebbbceSChristoph Hellwig 	struct sas_phy *phy = transport_class_to_phy(cdev);		\
252c7ebbbceSChristoph Hellwig 									\
253c7ebbbceSChristoph Hellwig 	return get_sas_linkspeed_names(phy->field, buf);		\
254c7ebbbceSChristoph Hellwig }
255c7ebbbceSChristoph Hellwig 
256c7ebbbceSChristoph Hellwig #define sas_phy_linkspeed_attr(field)					\
257c7ebbbceSChristoph Hellwig 	sas_phy_show_linkspeed(field)					\
258c7ebbbceSChristoph Hellwig static CLASS_DEVICE_ATTR(field, S_IRUGO, show_sas_phy_##field, NULL)
259c7ebbbceSChristoph Hellwig 
260c3ee74c4SChristoph Hellwig #define sas_phy_show_linkerror(field)					\
261c3ee74c4SChristoph Hellwig static ssize_t								\
262c3ee74c4SChristoph Hellwig show_sas_phy_##field(struct class_device *cdev, char *buf)		\
263c3ee74c4SChristoph Hellwig {									\
264c3ee74c4SChristoph Hellwig 	struct sas_phy *phy = transport_class_to_phy(cdev);		\
265c3ee74c4SChristoph Hellwig 	struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);	\
266c3ee74c4SChristoph Hellwig 	struct sas_internal *i = to_sas_internal(shost->transportt);	\
267c3ee74c4SChristoph Hellwig 	int error;							\
268c3ee74c4SChristoph Hellwig 									\
269ac01bbbdSChristoph Hellwig 	if (!phy->local_attached)					\
270ac01bbbdSChristoph Hellwig 		return -EINVAL;						\
271ac01bbbdSChristoph Hellwig 									\
272c3ee74c4SChristoph Hellwig 	error = i->f->get_linkerrors(phy);				\
273c3ee74c4SChristoph Hellwig 	if (error)							\
274c3ee74c4SChristoph Hellwig 		return error;						\
275c3ee74c4SChristoph Hellwig 	return snprintf(buf, 20, "%u\n", phy->field);			\
276c3ee74c4SChristoph Hellwig }
277c3ee74c4SChristoph Hellwig 
278c3ee74c4SChristoph Hellwig #define sas_phy_linkerror_attr(field)					\
279c3ee74c4SChristoph Hellwig 	sas_phy_show_linkerror(field)					\
280c3ee74c4SChristoph Hellwig static CLASS_DEVICE_ATTR(field, S_IRUGO, show_sas_phy_##field, NULL)
281c3ee74c4SChristoph Hellwig 
282c3ee74c4SChristoph Hellwig 
283c7ebbbceSChristoph Hellwig static ssize_t
284c7ebbbceSChristoph Hellwig show_sas_device_type(struct class_device *cdev, char *buf)
285c7ebbbceSChristoph Hellwig {
286c7ebbbceSChristoph Hellwig 	struct sas_phy *phy = transport_class_to_phy(cdev);
287c7ebbbceSChristoph Hellwig 
288c7ebbbceSChristoph Hellwig 	if (!phy->identify.device_type)
289c7ebbbceSChristoph Hellwig 		return snprintf(buf, 20, "none\n");
290c7ebbbceSChristoph Hellwig 	return get_sas_device_type_names(phy->identify.device_type, buf);
291c7ebbbceSChristoph Hellwig }
292c7ebbbceSChristoph Hellwig static CLASS_DEVICE_ATTR(device_type, S_IRUGO, show_sas_device_type, NULL);
293c7ebbbceSChristoph Hellwig 
29407ba3a95SChristoph Hellwig static ssize_t do_sas_phy_reset(struct class_device *cdev,
29507ba3a95SChristoph Hellwig 		size_t count, int hard_reset)
29607ba3a95SChristoph Hellwig {
29707ba3a95SChristoph Hellwig 	struct sas_phy *phy = transport_class_to_phy(cdev);
29807ba3a95SChristoph Hellwig 	struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
29907ba3a95SChristoph Hellwig 	struct sas_internal *i = to_sas_internal(shost->transportt);
30007ba3a95SChristoph Hellwig 	int error;
30107ba3a95SChristoph Hellwig 
30207ba3a95SChristoph Hellwig 	if (!phy->local_attached)
30307ba3a95SChristoph Hellwig 		return -EINVAL;
30407ba3a95SChristoph Hellwig 
30507ba3a95SChristoph Hellwig 	error = i->f->phy_reset(phy, hard_reset);
30607ba3a95SChristoph Hellwig 	if (error)
30707ba3a95SChristoph Hellwig 		return error;
30807ba3a95SChristoph Hellwig 	return count;
30907ba3a95SChristoph Hellwig };
31007ba3a95SChristoph Hellwig 
31107ba3a95SChristoph Hellwig static ssize_t store_sas_link_reset(struct class_device *cdev,
31207ba3a95SChristoph Hellwig 		const char *buf, size_t count)
31307ba3a95SChristoph Hellwig {
31407ba3a95SChristoph Hellwig 	return do_sas_phy_reset(cdev, count, 0);
31507ba3a95SChristoph Hellwig }
31607ba3a95SChristoph Hellwig static CLASS_DEVICE_ATTR(link_reset, S_IWUSR, NULL, store_sas_link_reset);
31707ba3a95SChristoph Hellwig 
31807ba3a95SChristoph Hellwig static ssize_t store_sas_hard_reset(struct class_device *cdev,
31907ba3a95SChristoph Hellwig 		const char *buf, size_t count)
32007ba3a95SChristoph Hellwig {
32107ba3a95SChristoph Hellwig 	return do_sas_phy_reset(cdev, count, 1);
32207ba3a95SChristoph Hellwig }
32307ba3a95SChristoph Hellwig static CLASS_DEVICE_ATTR(hard_reset, S_IWUSR, NULL, store_sas_hard_reset);
32407ba3a95SChristoph Hellwig 
325c7ebbbceSChristoph Hellwig sas_phy_protocol_attr(identify.initiator_port_protocols,
326c7ebbbceSChristoph Hellwig 		initiator_port_protocols);
327c7ebbbceSChristoph Hellwig sas_phy_protocol_attr(identify.target_port_protocols,
328c7ebbbceSChristoph Hellwig 		target_port_protocols);
329c7ebbbceSChristoph Hellwig sas_phy_simple_attr(identify.sas_address, sas_address, "0x%016llx\n",
330c7ebbbceSChristoph Hellwig 		unsigned long long);
331c7ebbbceSChristoph Hellwig sas_phy_simple_attr(identify.phy_identifier, phy_identifier, "%d\n", u8);
332c7ebbbceSChristoph Hellwig sas_phy_simple_attr(port_identifier, port_identifier, "%d\n", u8);
333c7ebbbceSChristoph Hellwig sas_phy_linkspeed_attr(negotiated_linkrate);
334c7ebbbceSChristoph Hellwig sas_phy_linkspeed_attr(minimum_linkrate_hw);
335c7ebbbceSChristoph Hellwig sas_phy_linkspeed_attr(minimum_linkrate);
336c7ebbbceSChristoph Hellwig sas_phy_linkspeed_attr(maximum_linkrate_hw);
337c7ebbbceSChristoph Hellwig sas_phy_linkspeed_attr(maximum_linkrate);
338c3ee74c4SChristoph Hellwig sas_phy_linkerror_attr(invalid_dword_count);
339c3ee74c4SChristoph Hellwig sas_phy_linkerror_attr(running_disparity_error_count);
340c3ee74c4SChristoph Hellwig sas_phy_linkerror_attr(loss_of_dword_sync_count);
341c3ee74c4SChristoph Hellwig sas_phy_linkerror_attr(phy_reset_problem_count);
342c7ebbbceSChristoph Hellwig 
343c7ebbbceSChristoph Hellwig 
344c7ebbbceSChristoph Hellwig static DECLARE_TRANSPORT_CLASS(sas_phy_class,
345c7ebbbceSChristoph Hellwig 		"sas_phy", NULL, NULL, NULL);
346c7ebbbceSChristoph Hellwig 
347c7ebbbceSChristoph Hellwig static int sas_phy_match(struct attribute_container *cont, struct device *dev)
348c7ebbbceSChristoph Hellwig {
349c7ebbbceSChristoph Hellwig 	struct Scsi_Host *shost;
350c7ebbbceSChristoph Hellwig 	struct sas_internal *i;
351c7ebbbceSChristoph Hellwig 
352c7ebbbceSChristoph Hellwig 	if (!scsi_is_sas_phy(dev))
353c7ebbbceSChristoph Hellwig 		return 0;
354c7ebbbceSChristoph Hellwig 	shost = dev_to_shost(dev->parent);
355c7ebbbceSChristoph Hellwig 
356c7ebbbceSChristoph Hellwig 	if (!shost->transportt)
357c7ebbbceSChristoph Hellwig 		return 0;
358c7ebbbceSChristoph Hellwig 	if (shost->transportt->host_attrs.ac.class !=
359c7ebbbceSChristoph Hellwig 			&sas_host_class.class)
360c7ebbbceSChristoph Hellwig 		return 0;
361c7ebbbceSChristoph Hellwig 
362c7ebbbceSChristoph Hellwig 	i = to_sas_internal(shost->transportt);
363c7ebbbceSChristoph Hellwig 	return &i->phy_attr_cont.ac == cont;
364c7ebbbceSChristoph Hellwig }
365c7ebbbceSChristoph Hellwig 
366c7ebbbceSChristoph Hellwig static void sas_phy_release(struct device *dev)
367c7ebbbceSChristoph Hellwig {
368c7ebbbceSChristoph Hellwig 	struct sas_phy *phy = dev_to_phy(dev);
369c7ebbbceSChristoph Hellwig 
370c7ebbbceSChristoph Hellwig 	put_device(dev->parent);
371c7ebbbceSChristoph Hellwig 	kfree(phy);
372c7ebbbceSChristoph Hellwig }
373c7ebbbceSChristoph Hellwig 
374c7ebbbceSChristoph Hellwig /**
375c7ebbbceSChristoph Hellwig  * sas_phy_alloc  --  allocates and initialize a SAS PHY structure
376c7ebbbceSChristoph Hellwig  * @parent:	Parent device
377c7ebbbceSChristoph Hellwig  * @number:	Port number
378c7ebbbceSChristoph Hellwig  *
379c7ebbbceSChristoph Hellwig  * Allocates an SAS PHY structure.  It will be added in the device tree
380c7ebbbceSChristoph Hellwig  * below the device specified by @parent, which has to be either a Scsi_Host
381c7ebbbceSChristoph Hellwig  * or sas_rphy.
382c7ebbbceSChristoph Hellwig  *
383c7ebbbceSChristoph Hellwig  * Returns:
384c7ebbbceSChristoph Hellwig  *	SAS PHY allocated or %NULL if the allocation failed.
385c7ebbbceSChristoph Hellwig  */
386c7ebbbceSChristoph Hellwig struct sas_phy *sas_phy_alloc(struct device *parent, int number)
387c7ebbbceSChristoph Hellwig {
388c7ebbbceSChristoph Hellwig 	struct Scsi_Host *shost = dev_to_shost(parent);
389c7ebbbceSChristoph Hellwig 	struct sas_phy *phy;
390c7ebbbceSChristoph Hellwig 
391c7ebbbceSChristoph Hellwig 	phy = kmalloc(sizeof(*phy), GFP_KERNEL);
392c7ebbbceSChristoph Hellwig 	if (!phy)
393c7ebbbceSChristoph Hellwig 		return NULL;
394c7ebbbceSChristoph Hellwig 	memset(phy, 0, sizeof(*phy));
395c7ebbbceSChristoph Hellwig 
396c7ebbbceSChristoph Hellwig 	get_device(parent);
397c7ebbbceSChristoph Hellwig 
398c7ebbbceSChristoph Hellwig 	phy->number = number;
399c7ebbbceSChristoph Hellwig 
400c7ebbbceSChristoph Hellwig 	device_initialize(&phy->dev);
401c7ebbbceSChristoph Hellwig 	phy->dev.parent = get_device(parent);
402c7ebbbceSChristoph Hellwig 	phy->dev.release = sas_phy_release;
403c7ebbbceSChristoph Hellwig 	sprintf(phy->dev.bus_id, "phy-%d:%d", shost->host_no, number);
404c7ebbbceSChristoph Hellwig 
405c7ebbbceSChristoph Hellwig 	transport_setup_device(&phy->dev);
406c7ebbbceSChristoph Hellwig 
407c7ebbbceSChristoph Hellwig 	return phy;
408c7ebbbceSChristoph Hellwig }
409c7ebbbceSChristoph Hellwig EXPORT_SYMBOL(sas_phy_alloc);
410c7ebbbceSChristoph Hellwig 
411c7ebbbceSChristoph Hellwig /**
412c7ebbbceSChristoph Hellwig  * sas_phy_add  --  add a SAS PHY to the device hierachy
413c7ebbbceSChristoph Hellwig  * @phy:	The PHY to be added
414c7ebbbceSChristoph Hellwig  *
415c7ebbbceSChristoph Hellwig  * Publishes a SAS PHY to the rest of the system.
416c7ebbbceSChristoph Hellwig  */
417c7ebbbceSChristoph Hellwig int sas_phy_add(struct sas_phy *phy)
418c7ebbbceSChristoph Hellwig {
419c7ebbbceSChristoph Hellwig 	int error;
420c7ebbbceSChristoph Hellwig 
421c7ebbbceSChristoph Hellwig 	error = device_add(&phy->dev);
422c7ebbbceSChristoph Hellwig 	if (!error) {
423c7ebbbceSChristoph Hellwig 		transport_add_device(&phy->dev);
424c7ebbbceSChristoph Hellwig 		transport_configure_device(&phy->dev);
425c7ebbbceSChristoph Hellwig 	}
426c7ebbbceSChristoph Hellwig 
427c7ebbbceSChristoph Hellwig 	return error;
428c7ebbbceSChristoph Hellwig }
429c7ebbbceSChristoph Hellwig EXPORT_SYMBOL(sas_phy_add);
430c7ebbbceSChristoph Hellwig 
431c7ebbbceSChristoph Hellwig /**
432c7ebbbceSChristoph Hellwig  * sas_phy_free  --  free a SAS PHY
433c7ebbbceSChristoph Hellwig  * @phy:	SAS PHY to free
434c7ebbbceSChristoph Hellwig  *
435c7ebbbceSChristoph Hellwig  * Frees the specified SAS PHY.
436c7ebbbceSChristoph Hellwig  *
437c7ebbbceSChristoph Hellwig  * Note:
438c7ebbbceSChristoph Hellwig  *   This function must only be called on a PHY that has not
439c7ebbbceSChristoph Hellwig  *   sucessfully been added using sas_phy_add().
440c7ebbbceSChristoph Hellwig  */
441c7ebbbceSChristoph Hellwig void sas_phy_free(struct sas_phy *phy)
442c7ebbbceSChristoph Hellwig {
443c7ebbbceSChristoph Hellwig 	transport_destroy_device(&phy->dev);
444c7ebbbceSChristoph Hellwig 	put_device(phy->dev.parent);
445c7ebbbceSChristoph Hellwig 	put_device(phy->dev.parent);
446c7ebbbceSChristoph Hellwig 	put_device(phy->dev.parent);
447c7ebbbceSChristoph Hellwig 	kfree(phy);
448c7ebbbceSChristoph Hellwig }
449c7ebbbceSChristoph Hellwig EXPORT_SYMBOL(sas_phy_free);
450c7ebbbceSChristoph Hellwig 
451c7ebbbceSChristoph Hellwig /**
452c7ebbbceSChristoph Hellwig  * sas_phy_delete  --  remove SAS PHY
453c7ebbbceSChristoph Hellwig  * @phy:	SAS PHY to remove
454c7ebbbceSChristoph Hellwig  *
455c7ebbbceSChristoph Hellwig  * Removes the specified SAS PHY.  If the SAS PHY has an
456c7ebbbceSChristoph Hellwig  * associated remote PHY it is removed before.
457c7ebbbceSChristoph Hellwig  */
458c7ebbbceSChristoph Hellwig void
459c7ebbbceSChristoph Hellwig sas_phy_delete(struct sas_phy *phy)
460c7ebbbceSChristoph Hellwig {
461c7ebbbceSChristoph Hellwig 	struct device *dev = &phy->dev;
462c7ebbbceSChristoph Hellwig 
463c7ebbbceSChristoph Hellwig 	if (phy->rphy)
464c7ebbbceSChristoph Hellwig 		sas_rphy_delete(phy->rphy);
465c7ebbbceSChristoph Hellwig 
466c7ebbbceSChristoph Hellwig 	transport_remove_device(dev);
467c7ebbbceSChristoph Hellwig 	device_del(dev);
468c7ebbbceSChristoph Hellwig 	transport_destroy_device(dev);
469c7ebbbceSChristoph Hellwig 	put_device(dev->parent);
470c7ebbbceSChristoph Hellwig }
471c7ebbbceSChristoph Hellwig EXPORT_SYMBOL(sas_phy_delete);
472c7ebbbceSChristoph Hellwig 
473c7ebbbceSChristoph Hellwig /**
474c7ebbbceSChristoph Hellwig  * scsi_is_sas_phy  --  check if a struct device represents a SAS PHY
475c7ebbbceSChristoph Hellwig  * @dev:	device to check
476c7ebbbceSChristoph Hellwig  *
477c7ebbbceSChristoph Hellwig  * Returns:
478c7ebbbceSChristoph Hellwig  *	%1 if the device represents a SAS PHY, %0 else
479c7ebbbceSChristoph Hellwig  */
480c7ebbbceSChristoph Hellwig int scsi_is_sas_phy(const struct device *dev)
481c7ebbbceSChristoph Hellwig {
482c7ebbbceSChristoph Hellwig 	return dev->release == sas_phy_release;
483c7ebbbceSChristoph Hellwig }
484c7ebbbceSChristoph Hellwig EXPORT_SYMBOL(scsi_is_sas_phy);
485c7ebbbceSChristoph Hellwig 
486c7ebbbceSChristoph Hellwig /*
487c7ebbbceSChristoph Hellwig  * SAS remote PHY attributes.
488c7ebbbceSChristoph Hellwig  */
489c7ebbbceSChristoph Hellwig 
490c7ebbbceSChristoph Hellwig #define sas_rphy_show_simple(field, name, format_string, cast)		\
491c7ebbbceSChristoph Hellwig static ssize_t								\
492c7ebbbceSChristoph Hellwig show_sas_rphy_##name(struct class_device *cdev, char *buf)		\
493c7ebbbceSChristoph Hellwig {									\
494c7ebbbceSChristoph Hellwig 	struct sas_rphy *rphy = transport_class_to_rphy(cdev);	\
495c7ebbbceSChristoph Hellwig 									\
496c7ebbbceSChristoph Hellwig 	return snprintf(buf, 20, format_string, cast rphy->field);	\
497c7ebbbceSChristoph Hellwig }
498c7ebbbceSChristoph Hellwig 
499c7ebbbceSChristoph Hellwig #define sas_rphy_simple_attr(field, name, format_string, type)		\
500c7ebbbceSChristoph Hellwig 	sas_rphy_show_simple(field, name, format_string, (type))	\
501c7ebbbceSChristoph Hellwig static SAS_CLASS_DEVICE_ATTR(rphy, name, S_IRUGO, 			\
502c7ebbbceSChristoph Hellwig 		show_sas_rphy_##name, NULL)
503c7ebbbceSChristoph Hellwig 
504c7ebbbceSChristoph Hellwig #define sas_rphy_show_protocol(field, name)				\
505c7ebbbceSChristoph Hellwig static ssize_t								\
506c7ebbbceSChristoph Hellwig show_sas_rphy_##name(struct class_device *cdev, char *buf)		\
507c7ebbbceSChristoph Hellwig {									\
508c7ebbbceSChristoph Hellwig 	struct sas_rphy *rphy = transport_class_to_rphy(cdev);	\
509c7ebbbceSChristoph Hellwig 									\
510c7ebbbceSChristoph Hellwig 	if (!rphy->field)					\
511c7ebbbceSChristoph Hellwig 		return snprintf(buf, 20, "none\n");			\
512c7ebbbceSChristoph Hellwig 	return get_sas_protocol_names(rphy->field, buf);	\
513c7ebbbceSChristoph Hellwig }
514c7ebbbceSChristoph Hellwig 
515c7ebbbceSChristoph Hellwig #define sas_rphy_protocol_attr(field, name)				\
516c7ebbbceSChristoph Hellwig 	sas_rphy_show_protocol(field, name)				\
517c7ebbbceSChristoph Hellwig static SAS_CLASS_DEVICE_ATTR(rphy, name, S_IRUGO,			\
518c7ebbbceSChristoph Hellwig 		show_sas_rphy_##name, NULL)
519c7ebbbceSChristoph Hellwig 
520c7ebbbceSChristoph Hellwig static ssize_t
521c7ebbbceSChristoph Hellwig show_sas_rphy_device_type(struct class_device *cdev, char *buf)
522c7ebbbceSChristoph Hellwig {
523c7ebbbceSChristoph Hellwig 	struct sas_rphy *rphy = transport_class_to_rphy(cdev);
524c7ebbbceSChristoph Hellwig 
525c7ebbbceSChristoph Hellwig 	if (!rphy->identify.device_type)
526c7ebbbceSChristoph Hellwig 		return snprintf(buf, 20, "none\n");
527c7ebbbceSChristoph Hellwig 	return get_sas_device_type_names(
528c7ebbbceSChristoph Hellwig 			rphy->identify.device_type, buf);
529c7ebbbceSChristoph Hellwig }
530c7ebbbceSChristoph Hellwig 
531c7ebbbceSChristoph Hellwig static SAS_CLASS_DEVICE_ATTR(rphy, device_type, S_IRUGO,
532c7ebbbceSChristoph Hellwig 		show_sas_rphy_device_type, NULL);
533c7ebbbceSChristoph Hellwig 
534c7ebbbceSChristoph Hellwig sas_rphy_protocol_attr(identify.initiator_port_protocols,
535c7ebbbceSChristoph Hellwig 		initiator_port_protocols);
536c7ebbbceSChristoph Hellwig sas_rphy_protocol_attr(identify.target_port_protocols, target_port_protocols);
537c7ebbbceSChristoph Hellwig sas_rphy_simple_attr(identify.sas_address, sas_address, "0x%016llx\n",
538c7ebbbceSChristoph Hellwig 		unsigned long long);
539c7ebbbceSChristoph Hellwig sas_rphy_simple_attr(identify.phy_identifier, phy_identifier, "%d\n", u8);
540c7ebbbceSChristoph Hellwig 
541c7ebbbceSChristoph Hellwig static DECLARE_TRANSPORT_CLASS(sas_rphy_class,
542c7ebbbceSChristoph Hellwig 		"sas_rphy", NULL, NULL, NULL);
543c7ebbbceSChristoph Hellwig 
544c7ebbbceSChristoph Hellwig static int sas_rphy_match(struct attribute_container *cont, struct device *dev)
545c7ebbbceSChristoph Hellwig {
546c7ebbbceSChristoph Hellwig 	struct Scsi_Host *shost;
547c7ebbbceSChristoph Hellwig 	struct sas_internal *i;
548c7ebbbceSChristoph Hellwig 
549c7ebbbceSChristoph Hellwig 	if (!scsi_is_sas_rphy(dev))
550c7ebbbceSChristoph Hellwig 		return 0;
551c7ebbbceSChristoph Hellwig 	shost = dev_to_shost(dev->parent->parent);
552c7ebbbceSChristoph Hellwig 
553c7ebbbceSChristoph Hellwig 	if (!shost->transportt)
554c7ebbbceSChristoph Hellwig 		return 0;
555c7ebbbceSChristoph Hellwig 	if (shost->transportt->host_attrs.ac.class !=
556c7ebbbceSChristoph Hellwig 			&sas_host_class.class)
557c7ebbbceSChristoph Hellwig 		return 0;
558c7ebbbceSChristoph Hellwig 
559c7ebbbceSChristoph Hellwig 	i = to_sas_internal(shost->transportt);
560c7ebbbceSChristoph Hellwig 	return &i->rphy_attr_cont.ac == cont;
561c7ebbbceSChristoph Hellwig }
562c7ebbbceSChristoph Hellwig 
563c7ebbbceSChristoph Hellwig static void sas_rphy_release(struct device *dev)
564c7ebbbceSChristoph Hellwig {
565c7ebbbceSChristoph Hellwig 	struct sas_rphy *rphy = dev_to_rphy(dev);
566c7ebbbceSChristoph Hellwig 
567c7ebbbceSChristoph Hellwig 	put_device(dev->parent);
568c7ebbbceSChristoph Hellwig 	kfree(rphy);
569c7ebbbceSChristoph Hellwig }
570c7ebbbceSChristoph Hellwig 
571c7ebbbceSChristoph Hellwig /**
572c7ebbbceSChristoph Hellwig  * sas_rphy_alloc  --  allocates and initialize a SAS remote PHY structure
573c7ebbbceSChristoph Hellwig  * @parent:		SAS PHY this remote PHY is conneted to
574c7ebbbceSChristoph Hellwig  *
575c7ebbbceSChristoph Hellwig  * Allocates an SAS remote PHY structure, connected to @parent.
576c7ebbbceSChristoph Hellwig  *
577c7ebbbceSChristoph Hellwig  * Returns:
578c7ebbbceSChristoph Hellwig  *	SAS PHY allocated or %NULL if the allocation failed.
579c7ebbbceSChristoph Hellwig  */
580c7ebbbceSChristoph Hellwig struct sas_rphy *sas_rphy_alloc(struct sas_phy *parent)
581c7ebbbceSChristoph Hellwig {
582c7ebbbceSChristoph Hellwig 	struct Scsi_Host *shost = dev_to_shost(&parent->dev);
583c7ebbbceSChristoph Hellwig 	struct sas_rphy *rphy;
584c7ebbbceSChristoph Hellwig 
585c7ebbbceSChristoph Hellwig 	rphy = kmalloc(sizeof(*rphy), GFP_KERNEL);
586c7ebbbceSChristoph Hellwig 	if (!rphy) {
587c7ebbbceSChristoph Hellwig 		put_device(&parent->dev);
588c7ebbbceSChristoph Hellwig 		return NULL;
589c7ebbbceSChristoph Hellwig 	}
590c7ebbbceSChristoph Hellwig 	memset(rphy, 0, sizeof(*rphy));
591c7ebbbceSChristoph Hellwig 
592c7ebbbceSChristoph Hellwig 	device_initialize(&rphy->dev);
593c7ebbbceSChristoph Hellwig 	rphy->dev.parent = get_device(&parent->dev);
594c7ebbbceSChristoph Hellwig 	rphy->dev.release = sas_rphy_release;
595c7ebbbceSChristoph Hellwig 	sprintf(rphy->dev.bus_id, "rphy-%d:%d",
596c7ebbbceSChristoph Hellwig 		shost->host_no, parent->number);
597c7ebbbceSChristoph Hellwig 	transport_setup_device(&rphy->dev);
598c7ebbbceSChristoph Hellwig 
599c7ebbbceSChristoph Hellwig 	return rphy;
600c7ebbbceSChristoph Hellwig }
601c7ebbbceSChristoph Hellwig EXPORT_SYMBOL(sas_rphy_alloc);
602c7ebbbceSChristoph Hellwig 
603c7ebbbceSChristoph Hellwig /**
604c7ebbbceSChristoph Hellwig  * sas_rphy_add  --  add a SAS remote PHY to the device hierachy
605c7ebbbceSChristoph Hellwig  * @rphy:	The remote PHY to be added
606c7ebbbceSChristoph Hellwig  *
607c7ebbbceSChristoph Hellwig  * Publishes a SAS remote PHY to the rest of the system.
608c7ebbbceSChristoph Hellwig  */
609c7ebbbceSChristoph Hellwig int sas_rphy_add(struct sas_rphy *rphy)
610c7ebbbceSChristoph Hellwig {
611c7ebbbceSChristoph Hellwig 	struct sas_phy *parent = dev_to_phy(rphy->dev.parent);
612c7ebbbceSChristoph Hellwig 	struct Scsi_Host *shost = dev_to_shost(parent->dev.parent);
613c7ebbbceSChristoph Hellwig 	struct sas_host_attrs *sas_host = to_sas_host_attrs(shost);
614c7ebbbceSChristoph Hellwig 	struct sas_identify *identify = &rphy->identify;
615c7ebbbceSChristoph Hellwig 	int error;
616c7ebbbceSChristoph Hellwig 
617c7ebbbceSChristoph Hellwig 	if (parent->rphy)
618c7ebbbceSChristoph Hellwig 		return -ENXIO;
619c7ebbbceSChristoph Hellwig 	parent->rphy = rphy;
620c7ebbbceSChristoph Hellwig 
621c7ebbbceSChristoph Hellwig 	error = device_add(&rphy->dev);
622c7ebbbceSChristoph Hellwig 	if (error)
623c7ebbbceSChristoph Hellwig 		return error;
624c7ebbbceSChristoph Hellwig 	transport_add_device(&rphy->dev);
625c7ebbbceSChristoph Hellwig 	transport_configure_device(&rphy->dev);
626c7ebbbceSChristoph Hellwig 
627c7ebbbceSChristoph Hellwig 	spin_lock(&sas_host->lock);
628c7ebbbceSChristoph Hellwig 	list_add_tail(&rphy->list, &sas_host->rphy_list);
629c7ebbbceSChristoph Hellwig 	if (identify->device_type == SAS_END_DEVICE &&
630c7ebbbceSChristoph Hellwig 	    (identify->target_port_protocols &
631c7ebbbceSChristoph Hellwig 	     (SAS_PROTOCOL_SSP|SAS_PROTOCOL_STP|SAS_PROTOCOL_SATA)))
632c7ebbbceSChristoph Hellwig 		rphy->scsi_target_id = sas_host->next_target_id++;
633c7ebbbceSChristoph Hellwig 	else
634c7ebbbceSChristoph Hellwig 		rphy->scsi_target_id = -1;
635c7ebbbceSChristoph Hellwig 	spin_unlock(&sas_host->lock);
636c7ebbbceSChristoph Hellwig 
637c7ebbbceSChristoph Hellwig 	if (rphy->scsi_target_id != -1) {
638c7ebbbceSChristoph Hellwig 		scsi_scan_target(&rphy->dev, parent->number,
639c7ebbbceSChristoph Hellwig 				rphy->scsi_target_id, ~0, 0);
640c7ebbbceSChristoph Hellwig 	}
641c7ebbbceSChristoph Hellwig 
642c7ebbbceSChristoph Hellwig 	return 0;
643c7ebbbceSChristoph Hellwig }
644c7ebbbceSChristoph Hellwig EXPORT_SYMBOL(sas_rphy_add);
645c7ebbbceSChristoph Hellwig 
646c7ebbbceSChristoph Hellwig /**
647c7ebbbceSChristoph Hellwig  * sas_rphy_free  --  free a SAS remote PHY
648c7ebbbceSChristoph Hellwig  * @rphy	SAS remote PHY to free
649c7ebbbceSChristoph Hellwig  *
650c7ebbbceSChristoph Hellwig  * Frees the specified SAS remote PHY.
651c7ebbbceSChristoph Hellwig  *
652c7ebbbceSChristoph Hellwig  * Note:
653c7ebbbceSChristoph Hellwig  *   This function must only be called on a remote
654c7ebbbceSChristoph Hellwig  *   PHY that has not sucessfully been added using
655c7ebbbceSChristoph Hellwig  *   sas_rphy_add().
656c7ebbbceSChristoph Hellwig  */
657c7ebbbceSChristoph Hellwig void sas_rphy_free(struct sas_rphy *rphy)
658c7ebbbceSChristoph Hellwig {
659c7ebbbceSChristoph Hellwig 	struct Scsi_Host *shost = dev_to_shost(rphy->dev.parent->parent);
660c7ebbbceSChristoph Hellwig 	struct sas_host_attrs *sas_host = to_sas_host_attrs(shost);
661c7ebbbceSChristoph Hellwig 
662c7ebbbceSChristoph Hellwig 	spin_lock(&sas_host->lock);
663c7ebbbceSChristoph Hellwig 	list_del(&rphy->list);
664c7ebbbceSChristoph Hellwig 	spin_unlock(&sas_host->lock);
665c7ebbbceSChristoph Hellwig 
666c7ebbbceSChristoph Hellwig 	transport_destroy_device(&rphy->dev);
667c7ebbbceSChristoph Hellwig 	put_device(rphy->dev.parent);
668c7ebbbceSChristoph Hellwig 	put_device(rphy->dev.parent);
669c7ebbbceSChristoph Hellwig 	put_device(rphy->dev.parent);
670c7ebbbceSChristoph Hellwig 	kfree(rphy);
671c7ebbbceSChristoph Hellwig }
672c7ebbbceSChristoph Hellwig EXPORT_SYMBOL(sas_rphy_free);
673c7ebbbceSChristoph Hellwig 
674c7ebbbceSChristoph Hellwig /**
675c7ebbbceSChristoph Hellwig  * sas_rphy_delete  --  remove SAS remote PHY
676c7ebbbceSChristoph Hellwig  * @rphy:	SAS remote PHY to remove
677c7ebbbceSChristoph Hellwig  *
678c7ebbbceSChristoph Hellwig  * Removes the specified SAS remote PHY.
679c7ebbbceSChristoph Hellwig  */
680c7ebbbceSChristoph Hellwig void
681c7ebbbceSChristoph Hellwig sas_rphy_delete(struct sas_rphy *rphy)
682c7ebbbceSChristoph Hellwig {
683c7ebbbceSChristoph Hellwig 	struct device *dev = &rphy->dev;
684c7ebbbceSChristoph Hellwig 	struct sas_phy *parent = dev_to_phy(dev->parent);
685c7ebbbceSChristoph Hellwig 	struct Scsi_Host *shost = dev_to_shost(parent->dev.parent);
686c7ebbbceSChristoph Hellwig 	struct sas_host_attrs *sas_host = to_sas_host_attrs(shost);
687c7ebbbceSChristoph Hellwig 
688fe8b2304SChristoph Hellwig 	scsi_remove_target(dev);
689c7ebbbceSChristoph Hellwig 
690fe8b2304SChristoph Hellwig 	transport_remove_device(dev);
691fe8b2304SChristoph Hellwig 	device_del(dev);
692fe8b2304SChristoph Hellwig 	transport_destroy_device(dev);
693c7ebbbceSChristoph Hellwig 
694c7ebbbceSChristoph Hellwig 	spin_lock(&sas_host->lock);
695c7ebbbceSChristoph Hellwig 	list_del(&rphy->list);
696c7ebbbceSChristoph Hellwig 	spin_unlock(&sas_host->lock);
697c7ebbbceSChristoph Hellwig 
698c7ebbbceSChristoph Hellwig 	put_device(&parent->dev);
699c7ebbbceSChristoph Hellwig }
700c7ebbbceSChristoph Hellwig EXPORT_SYMBOL(sas_rphy_delete);
701c7ebbbceSChristoph Hellwig 
702c7ebbbceSChristoph Hellwig /**
703c7ebbbceSChristoph Hellwig  * scsi_is_sas_rphy  --  check if a struct device represents a SAS remote PHY
704c7ebbbceSChristoph Hellwig  * @dev:	device to check
705c7ebbbceSChristoph Hellwig  *
706c7ebbbceSChristoph Hellwig  * Returns:
707c7ebbbceSChristoph Hellwig  *	%1 if the device represents a SAS remote PHY, %0 else
708c7ebbbceSChristoph Hellwig  */
709c7ebbbceSChristoph Hellwig int scsi_is_sas_rphy(const struct device *dev)
710c7ebbbceSChristoph Hellwig {
711c7ebbbceSChristoph Hellwig 	return dev->release == sas_rphy_release;
712c7ebbbceSChristoph Hellwig }
713c7ebbbceSChristoph Hellwig EXPORT_SYMBOL(scsi_is_sas_rphy);
714c7ebbbceSChristoph Hellwig 
715c7ebbbceSChristoph Hellwig 
716c7ebbbceSChristoph Hellwig /*
717c7ebbbceSChristoph Hellwig  * SCSI scan helper
718c7ebbbceSChristoph Hellwig  */
719c7ebbbceSChristoph Hellwig 
720c7ebbbceSChristoph Hellwig static struct device *sas_target_parent(struct Scsi_Host *shost,
721c7ebbbceSChristoph Hellwig 					int channel, uint id)
722c7ebbbceSChristoph Hellwig {
723c7ebbbceSChristoph Hellwig 	struct sas_host_attrs *sas_host = to_sas_host_attrs(shost);
724c7ebbbceSChristoph Hellwig 	struct sas_rphy *rphy;
725c7ebbbceSChristoph Hellwig 	struct device *dev = NULL;
726c7ebbbceSChristoph Hellwig 
727c7ebbbceSChristoph Hellwig 	spin_lock(&sas_host->lock);
728c7ebbbceSChristoph Hellwig 	list_for_each_entry(rphy, &sas_host->rphy_list, list) {
729c7ebbbceSChristoph Hellwig 		struct sas_phy *parent = dev_to_phy(rphy->dev.parent);
730c7ebbbceSChristoph Hellwig 		if (parent->number == channel &&
731c7ebbbceSChristoph Hellwig 		    rphy->scsi_target_id == id)
732c7ebbbceSChristoph Hellwig 			dev = &rphy->dev;
733c7ebbbceSChristoph Hellwig 	}
734c7ebbbceSChristoph Hellwig 	spin_unlock(&sas_host->lock);
735c7ebbbceSChristoph Hellwig 
736c7ebbbceSChristoph Hellwig 	return dev;
737c7ebbbceSChristoph Hellwig }
738c7ebbbceSChristoph Hellwig 
739c7ebbbceSChristoph Hellwig 
740c7ebbbceSChristoph Hellwig /*
741c7ebbbceSChristoph Hellwig  * Setup / Teardown code
742c7ebbbceSChristoph Hellwig  */
743c7ebbbceSChristoph Hellwig 
744c7ebbbceSChristoph Hellwig #define SETUP_RPORT_ATTRIBUTE(field)					\
745c7ebbbceSChristoph Hellwig 	i->private_rphy_attrs[count] = class_device_attr_##field;	\
746c7ebbbceSChristoph Hellwig 	i->private_rphy_attrs[count].attr.mode = S_IRUGO;		\
747c7ebbbceSChristoph Hellwig 	i->private_rphy_attrs[count].store = NULL;			\
748c7ebbbceSChristoph Hellwig 	i->rphy_attrs[count] = &i->private_rphy_attrs[count];	\
749c7ebbbceSChristoph Hellwig 	count++
750c7ebbbceSChristoph Hellwig 
751c7ebbbceSChristoph Hellwig #define SETUP_PORT_ATTRIBUTE(field)					\
752c7ebbbceSChristoph Hellwig 	i->private_phy_attrs[count] = class_device_attr_##field;	\
753c7ebbbceSChristoph Hellwig         i->private_phy_attrs[count].attr.mode = S_IRUGO;		\
754c7ebbbceSChristoph Hellwig         i->private_phy_attrs[count].store = NULL;			\
755c7ebbbceSChristoph Hellwig         i->phy_attrs[count] = &i->private_phy_attrs[count];		\
756c7ebbbceSChristoph Hellwig 	count++
757c7ebbbceSChristoph Hellwig 
75807ba3a95SChristoph Hellwig #define SETUP_PORT_ATTRIBUTE_WRONLY(field)				\
75907ba3a95SChristoph Hellwig 	i->private_phy_attrs[count] = class_device_attr_##field;	\
76007ba3a95SChristoph Hellwig 	i->private_phy_attrs[count].attr.mode = S_IWUGO;		\
76107ba3a95SChristoph Hellwig 	i->private_phy_attrs[count].show = NULL;			\
76207ba3a95SChristoph Hellwig 	i->phy_attrs[count] = &i->private_phy_attrs[count];		\
76307ba3a95SChristoph Hellwig 	count++
76407ba3a95SChristoph Hellwig 
765c7ebbbceSChristoph Hellwig 
766c7ebbbceSChristoph Hellwig /**
767c7ebbbceSChristoph Hellwig  * sas_attach_transport  --  instantiate SAS transport template
768c7ebbbceSChristoph Hellwig  * @ft:		SAS transport class function template
769c7ebbbceSChristoph Hellwig  */
770c7ebbbceSChristoph Hellwig struct scsi_transport_template *
771c7ebbbceSChristoph Hellwig sas_attach_transport(struct sas_function_template *ft)
772c7ebbbceSChristoph Hellwig {
773c7ebbbceSChristoph Hellwig 	struct sas_internal *i;
774c7ebbbceSChristoph Hellwig 	int count;
775c7ebbbceSChristoph Hellwig 
776c7ebbbceSChristoph Hellwig 	i = kmalloc(sizeof(struct sas_internal), GFP_KERNEL);
777c7ebbbceSChristoph Hellwig 	if (!i)
778c7ebbbceSChristoph Hellwig 		return NULL;
779c7ebbbceSChristoph Hellwig 	memset(i, 0, sizeof(struct sas_internal));
780c7ebbbceSChristoph Hellwig 
781c7ebbbceSChristoph Hellwig 	i->t.target_parent = sas_target_parent;
782c7ebbbceSChristoph Hellwig 
783c7ebbbceSChristoph Hellwig 	i->t.host_attrs.ac.attrs = &i->host_attrs[0];
784c7ebbbceSChristoph Hellwig 	i->t.host_attrs.ac.class = &sas_host_class.class;
785c7ebbbceSChristoph Hellwig 	i->t.host_attrs.ac.match = sas_host_match;
786c7ebbbceSChristoph Hellwig 	transport_container_register(&i->t.host_attrs);
787c7ebbbceSChristoph Hellwig 	i->t.host_size = sizeof(struct sas_host_attrs);
788c7ebbbceSChristoph Hellwig 
789c7ebbbceSChristoph Hellwig 	i->phy_attr_cont.ac.class = &sas_phy_class.class;
790c7ebbbceSChristoph Hellwig 	i->phy_attr_cont.ac.attrs = &i->phy_attrs[0];
791c7ebbbceSChristoph Hellwig 	i->phy_attr_cont.ac.match = sas_phy_match;
792c7ebbbceSChristoph Hellwig 	transport_container_register(&i->phy_attr_cont);
793c7ebbbceSChristoph Hellwig 
794c7ebbbceSChristoph Hellwig 	i->rphy_attr_cont.ac.class = &sas_rphy_class.class;
795c7ebbbceSChristoph Hellwig 	i->rphy_attr_cont.ac.attrs = &i->rphy_attrs[0];
796c7ebbbceSChristoph Hellwig 	i->rphy_attr_cont.ac.match = sas_rphy_match;
797c7ebbbceSChristoph Hellwig 	transport_container_register(&i->rphy_attr_cont);
798c7ebbbceSChristoph Hellwig 
799c7ebbbceSChristoph Hellwig 	i->f = ft;
800c7ebbbceSChristoph Hellwig 
801c7ebbbceSChristoph Hellwig 	count = 0;
802c7ebbbceSChristoph Hellwig 	i->host_attrs[count] = NULL;
803c7ebbbceSChristoph Hellwig 
804c7ebbbceSChristoph Hellwig 	count = 0;
805c7ebbbceSChristoph Hellwig 	SETUP_PORT_ATTRIBUTE(initiator_port_protocols);
806c7ebbbceSChristoph Hellwig 	SETUP_PORT_ATTRIBUTE(target_port_protocols);
807c7ebbbceSChristoph Hellwig 	SETUP_PORT_ATTRIBUTE(device_type);
808c7ebbbceSChristoph Hellwig 	SETUP_PORT_ATTRIBUTE(sas_address);
809c7ebbbceSChristoph Hellwig 	SETUP_PORT_ATTRIBUTE(phy_identifier);
810c7ebbbceSChristoph Hellwig 	SETUP_PORT_ATTRIBUTE(port_identifier);
811c7ebbbceSChristoph Hellwig 	SETUP_PORT_ATTRIBUTE(negotiated_linkrate);
812c7ebbbceSChristoph Hellwig 	SETUP_PORT_ATTRIBUTE(minimum_linkrate_hw);
813c7ebbbceSChristoph Hellwig 	SETUP_PORT_ATTRIBUTE(minimum_linkrate);
814c7ebbbceSChristoph Hellwig 	SETUP_PORT_ATTRIBUTE(maximum_linkrate_hw);
815c7ebbbceSChristoph Hellwig 	SETUP_PORT_ATTRIBUTE(maximum_linkrate);
816c3ee74c4SChristoph Hellwig 
817c3ee74c4SChristoph Hellwig 	SETUP_PORT_ATTRIBUTE(invalid_dword_count);
818c3ee74c4SChristoph Hellwig 	SETUP_PORT_ATTRIBUTE(running_disparity_error_count);
819c3ee74c4SChristoph Hellwig 	SETUP_PORT_ATTRIBUTE(loss_of_dword_sync_count);
820c3ee74c4SChristoph Hellwig 	SETUP_PORT_ATTRIBUTE(phy_reset_problem_count);
82107ba3a95SChristoph Hellwig 	SETUP_PORT_ATTRIBUTE_WRONLY(link_reset);
82207ba3a95SChristoph Hellwig 	SETUP_PORT_ATTRIBUTE_WRONLY(hard_reset);
823c7ebbbceSChristoph Hellwig 	i->phy_attrs[count] = NULL;
824c7ebbbceSChristoph Hellwig 
825c7ebbbceSChristoph Hellwig 	count = 0;
826c7ebbbceSChristoph Hellwig 	SETUP_RPORT_ATTRIBUTE(rphy_initiator_port_protocols);
827c7ebbbceSChristoph Hellwig 	SETUP_RPORT_ATTRIBUTE(rphy_target_port_protocols);
828c7ebbbceSChristoph Hellwig 	SETUP_RPORT_ATTRIBUTE(rphy_device_type);
829c7ebbbceSChristoph Hellwig 	SETUP_RPORT_ATTRIBUTE(rphy_sas_address);
830c7ebbbceSChristoph Hellwig 	SETUP_RPORT_ATTRIBUTE(rphy_phy_identifier);
831c7ebbbceSChristoph Hellwig 	i->rphy_attrs[count] = NULL;
832c7ebbbceSChristoph Hellwig 
833c7ebbbceSChristoph Hellwig 	return &i->t;
834c7ebbbceSChristoph Hellwig }
835c7ebbbceSChristoph Hellwig EXPORT_SYMBOL(sas_attach_transport);
836c7ebbbceSChristoph Hellwig 
837c7ebbbceSChristoph Hellwig /**
838c7ebbbceSChristoph Hellwig  * sas_release_transport  --  release SAS transport template instance
839c7ebbbceSChristoph Hellwig  * @t:		transport template instance
840c7ebbbceSChristoph Hellwig  */
841c7ebbbceSChristoph Hellwig void sas_release_transport(struct scsi_transport_template *t)
842c7ebbbceSChristoph Hellwig {
843c7ebbbceSChristoph Hellwig 	struct sas_internal *i = to_sas_internal(t);
844c7ebbbceSChristoph Hellwig 
845c7ebbbceSChristoph Hellwig 	transport_container_unregister(&i->t.host_attrs);
846c7ebbbceSChristoph Hellwig 	transport_container_unregister(&i->phy_attr_cont);
847c7ebbbceSChristoph Hellwig 	transport_container_unregister(&i->rphy_attr_cont);
848c7ebbbceSChristoph Hellwig 
849c7ebbbceSChristoph Hellwig 	kfree(i);
850c7ebbbceSChristoph Hellwig }
851c7ebbbceSChristoph Hellwig EXPORT_SYMBOL(sas_release_transport);
852c7ebbbceSChristoph Hellwig 
853c7ebbbceSChristoph Hellwig static __init int sas_transport_init(void)
854c7ebbbceSChristoph Hellwig {
855c7ebbbceSChristoph Hellwig 	int error;
856c7ebbbceSChristoph Hellwig 
857c7ebbbceSChristoph Hellwig 	error = transport_class_register(&sas_host_class);
858c7ebbbceSChristoph Hellwig 	if (error)
859c7ebbbceSChristoph Hellwig 		goto out;
860c7ebbbceSChristoph Hellwig 	error = transport_class_register(&sas_phy_class);
861c7ebbbceSChristoph Hellwig 	if (error)
862c7ebbbceSChristoph Hellwig 		goto out_unregister_transport;
863c7ebbbceSChristoph Hellwig 	error = transport_class_register(&sas_rphy_class);
864c7ebbbceSChristoph Hellwig 	if (error)
865c7ebbbceSChristoph Hellwig 		goto out_unregister_phy;
866c7ebbbceSChristoph Hellwig 
867c7ebbbceSChristoph Hellwig 	return 0;
868c7ebbbceSChristoph Hellwig 
869c7ebbbceSChristoph Hellwig  out_unregister_phy:
870c7ebbbceSChristoph Hellwig 	transport_class_unregister(&sas_phy_class);
871c7ebbbceSChristoph Hellwig  out_unregister_transport:
872c7ebbbceSChristoph Hellwig 	transport_class_unregister(&sas_host_class);
873c7ebbbceSChristoph Hellwig  out:
874c7ebbbceSChristoph Hellwig 	return error;
875c7ebbbceSChristoph Hellwig 
876c7ebbbceSChristoph Hellwig }
877c7ebbbceSChristoph Hellwig 
878c7ebbbceSChristoph Hellwig static void __exit sas_transport_exit(void)
879c7ebbbceSChristoph Hellwig {
880c7ebbbceSChristoph Hellwig 	transport_class_unregister(&sas_host_class);
881c7ebbbceSChristoph Hellwig 	transport_class_unregister(&sas_phy_class);
882c7ebbbceSChristoph Hellwig 	transport_class_unregister(&sas_rphy_class);
883c7ebbbceSChristoph Hellwig }
884c7ebbbceSChristoph Hellwig 
885c7ebbbceSChristoph Hellwig MODULE_AUTHOR("Christoph Hellwig");
886c7ebbbceSChristoph Hellwig MODULE_DESCRIPTION("SAS Transphy Attributes");
887c7ebbbceSChristoph Hellwig MODULE_LICENSE("GPL");
888c7ebbbceSChristoph Hellwig 
889c7ebbbceSChristoph Hellwig module_init(sas_transport_init);
890c7ebbbceSChristoph Hellwig module_exit(sas_transport_exit);
891