blob: d518c1207fb43634988c25762a1a2692ad9f2a91 [file] [log] [blame]
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001/*
Christoph Hellwiga0125642006-02-16 13:31:47 +01002 * Copyright (C) 2005-2006 Dell Inc.
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02003 * Released under GPL v2.
4 *
5 * Serial Attached SCSI (SAS) transport class.
6 *
7 * The SAS transport class contains common code to deal with SAS HBAs,
8 * an aproximated representation of SAS topologies in the driver model,
9 * and various sysfs attributes to expose these topologies and managment
10 * interfaces to userspace.
11 *
12 * In addition to the basic SCSI core objects this transport class
13 * introduces two additional intermediate objects: The SAS PHY
14 * as represented by struct sas_phy defines an "outgoing" PHY on
15 * a SAS HBA or Expander, and the SAS remote PHY represented by
16 * struct sas_rphy defines an "incoming" PHY on a SAS Expander or
17 * end device. Note that this is purely a software concept, the
18 * underlying hardware for a PHY and a remote PHY is the exactly
19 * the same.
20 *
21 * There is no concept of a SAS port in this code, users can see
22 * what PHYs form a wide port based on the port_identifier attribute,
23 * which is the same for all PHYs in a port.
24 */
25
26#include <linux/init.h>
27#include <linux/module.h>
28#include <linux/err.h>
Tim Schmielau8c65b4a2005-11-07 00:59:43 -080029#include <linux/slab.h>
30#include <linux/string.h>
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +020031
Christoph Hellwige02f3f52006-01-13 19:04:00 +010032#include <scsi/scsi.h>
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +020033#include <scsi/scsi_device.h>
34#include <scsi/scsi_host.h>
35#include <scsi/scsi_transport.h>
36#include <scsi/scsi_transport_sas.h>
37
James Bottomleyd6159c12006-03-27 16:45:34 -060038#include "scsi_sas_internal.h"
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +020039struct sas_host_attrs {
40 struct list_head rphy_list;
Christoph Hellwige02f3f52006-01-13 19:04:00 +010041 struct mutex lock;
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +020042 u32 next_target_id;
James Bottomley79cb1812006-03-13 13:50:04 -060043 u32 next_expander_id;
James Bottomleyc9fefeb2006-07-02 11:10:18 -050044 int next_port_id;
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +020045};
46#define to_sas_host_attrs(host) ((struct sas_host_attrs *)(host)->shost_data)
47
48
49/*
50 * Hack to allow attributes of the same name in different objects.
51 */
52#define SAS_CLASS_DEVICE_ATTR(_prefix,_name,_mode,_show,_store) \
53 struct class_device_attribute class_device_attr_##_prefix##_##_name = \
54 __ATTR(_name,_mode,_show,_store)
55
56
57/*
58 * Pretty printing helpers
59 */
60
61#define sas_bitfield_name_match(title, table) \
62static ssize_t \
63get_sas_##title##_names(u32 table_key, char *buf) \
64{ \
65 char *prefix = ""; \
66 ssize_t len = 0; \
67 int i; \
68 \
Tobias Klauser6391a112006-06-08 22:23:48 -070069 for (i = 0; i < ARRAY_SIZE(table); i++) { \
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +020070 if (table[i].value & table_key) { \
71 len += sprintf(buf + len, "%s%s", \
72 prefix, table[i].name); \
73 prefix = ", "; \
74 } \
75 } \
76 len += sprintf(buf + len, "\n"); \
77 return len; \
78}
79
80#define sas_bitfield_name_search(title, table) \
81static ssize_t \
82get_sas_##title##_names(u32 table_key, char *buf) \
83{ \
84 ssize_t len = 0; \
85 int i; \
86 \
Tobias Klauser6391a112006-06-08 22:23:48 -070087 for (i = 0; i < ARRAY_SIZE(table); i++) { \
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +020088 if (table[i].value == table_key) { \
89 len += sprintf(buf + len, "%s", \
90 table[i].name); \
91 break; \
92 } \
93 } \
94 len += sprintf(buf + len, "\n"); \
95 return len; \
96}
97
98static struct {
99 u32 value;
100 char *name;
101} sas_device_type_names[] = {
102 { SAS_PHY_UNUSED, "unused" },
103 { SAS_END_DEVICE, "end device" },
104 { SAS_EDGE_EXPANDER_DEVICE, "edge expander" },
105 { SAS_FANOUT_EXPANDER_DEVICE, "fanout expander" },
106};
107sas_bitfield_name_search(device_type, sas_device_type_names)
108
109
110static struct {
111 u32 value;
112 char *name;
113} sas_protocol_names[] = {
114 { SAS_PROTOCOL_SATA, "sata" },
115 { SAS_PROTOCOL_SMP, "smp" },
116 { SAS_PROTOCOL_STP, "stp" },
117 { SAS_PROTOCOL_SSP, "ssp" },
118};
119sas_bitfield_name_match(protocol, sas_protocol_names)
120
121static struct {
122 u32 value;
123 char *name;
124} sas_linkspeed_names[] = {
125 { SAS_LINK_RATE_UNKNOWN, "Unknown" },
126 { SAS_PHY_DISABLED, "Phy disabled" },
127 { SAS_LINK_RATE_FAILED, "Link Rate failed" },
128 { SAS_SATA_SPINUP_HOLD, "Spin-up hold" },
129 { SAS_LINK_RATE_1_5_GBPS, "1.5 Gbit" },
130 { SAS_LINK_RATE_3_0_GBPS, "3.0 Gbit" },
James Bottomley7e6dff62006-03-02 14:12:56 -0600131 { SAS_LINK_RATE_6_0_GBPS, "6.0 Gbit" },
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +0200132};
133sas_bitfield_name_search(linkspeed, sas_linkspeed_names)
134
135
136/*
137 * SAS host attributes
138 */
139
James Bottomley37be6ee2005-09-09 18:38:27 -0500140static int sas_host_setup(struct transport_container *tc, struct device *dev,
141 struct class_device *cdev)
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +0200142{
143 struct Scsi_Host *shost = dev_to_shost(dev);
144 struct sas_host_attrs *sas_host = to_sas_host_attrs(shost);
145
146 INIT_LIST_HEAD(&sas_host->rphy_list);
Christoph Hellwige02f3f52006-01-13 19:04:00 +0100147 mutex_init(&sas_host->lock);
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +0200148 sas_host->next_target_id = 0;
James Bottomley79cb1812006-03-13 13:50:04 -0600149 sas_host->next_expander_id = 0;
James Bottomleyc9fefeb2006-07-02 11:10:18 -0500150 sas_host->next_port_id = 0;
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +0200151 return 0;
152}
153
154static DECLARE_TRANSPORT_CLASS(sas_host_class,
155 "sas_host", sas_host_setup, NULL, NULL);
156
157static int sas_host_match(struct attribute_container *cont,
158 struct device *dev)
159{
160 struct Scsi_Host *shost;
161 struct sas_internal *i;
162
163 if (!scsi_is_host_device(dev))
164 return 0;
165 shost = dev_to_shost(dev);
166
167 if (!shost->transportt)
168 return 0;
169 if (shost->transportt->host_attrs.ac.class !=
170 &sas_host_class.class)
171 return 0;
172
173 i = to_sas_internal(shost->transportt);
174 return &i->t.host_attrs.ac == cont;
175}
176
177static int do_sas_phy_delete(struct device *dev, void *data)
178{
James Bottomley65c92b02006-06-28 12:22:50 -0400179 int pass = (int)(unsigned long)data;
180
181 if (pass == 0 && scsi_is_sas_port(dev))
182 sas_port_delete(dev_to_sas_port(dev));
183 else if (pass == 1 && scsi_is_sas_phy(dev))
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +0200184 sas_phy_delete(dev_to_phy(dev));
185 return 0;
186}
187
188/**
James Bottomley65c92b02006-06-28 12:22:50 -0400189 * sas_remove_children -- tear down a devices SAS data structures
190 * @dev: device belonging to the sas object
191 *
192 * Removes all SAS PHYs and remote PHYs for a given object
193 */
194void sas_remove_children(struct device *dev)
195{
196 device_for_each_child(dev, (void *)0, do_sas_phy_delete);
197 device_for_each_child(dev, (void *)1, do_sas_phy_delete);
198}
199EXPORT_SYMBOL(sas_remove_children);
200
201/**
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +0200202 * sas_remove_host -- tear down a Scsi_Host's SAS data structures
203 * @shost: Scsi Host that is torn down
204 *
205 * Removes all SAS PHYs and remote PHYs for a given Scsi_Host.
206 * Must be called just before scsi_remove_host for SAS HBAs.
207 */
208void sas_remove_host(struct Scsi_Host *shost)
209{
James Bottomley65c92b02006-06-28 12:22:50 -0400210 sas_remove_children(&shost->shost_gendev);
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +0200211}
212EXPORT_SYMBOL(sas_remove_host);
213
214
215/*
James Bottomley65c92b02006-06-28 12:22:50 -0400216 * SAS Phy attributes
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +0200217 */
218
219#define sas_phy_show_simple(field, name, format_string, cast) \
220static ssize_t \
221show_sas_phy_##name(struct class_device *cdev, char *buf) \
222{ \
223 struct sas_phy *phy = transport_class_to_phy(cdev); \
224 \
225 return snprintf(buf, 20, format_string, cast phy->field); \
226}
227
228#define sas_phy_simple_attr(field, name, format_string, type) \
229 sas_phy_show_simple(field, name, format_string, (type)) \
230static CLASS_DEVICE_ATTR(name, S_IRUGO, show_sas_phy_##name, NULL)
231
232#define sas_phy_show_protocol(field, name) \
233static ssize_t \
234show_sas_phy_##name(struct class_device *cdev, char *buf) \
235{ \
236 struct sas_phy *phy = transport_class_to_phy(cdev); \
237 \
238 if (!phy->field) \
239 return snprintf(buf, 20, "none\n"); \
240 return get_sas_protocol_names(phy->field, buf); \
241}
242
243#define sas_phy_protocol_attr(field, name) \
244 sas_phy_show_protocol(field, name) \
245static CLASS_DEVICE_ATTR(name, S_IRUGO, show_sas_phy_##name, NULL)
246
247#define sas_phy_show_linkspeed(field) \
248static ssize_t \
249show_sas_phy_##field(struct class_device *cdev, char *buf) \
250{ \
251 struct sas_phy *phy = transport_class_to_phy(cdev); \
252 \
253 return get_sas_linkspeed_names(phy->field, buf); \
254}
255
256#define sas_phy_linkspeed_attr(field) \
257 sas_phy_show_linkspeed(field) \
258static CLASS_DEVICE_ATTR(field, S_IRUGO, show_sas_phy_##field, NULL)
259
Christoph Hellwigc3ee74c2005-09-19 21:59:42 +0200260#define sas_phy_show_linkerror(field) \
261static ssize_t \
262show_sas_phy_##field(struct class_device *cdev, char *buf) \
263{ \
264 struct sas_phy *phy = transport_class_to_phy(cdev); \
265 struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); \
266 struct sas_internal *i = to_sas_internal(shost->transportt); \
267 int error; \
268 \
James Bottomleydd9fbb522006-03-02 16:01:31 -0600269 error = i->f->get_linkerrors ? i->f->get_linkerrors(phy) : 0; \
Christoph Hellwigc3ee74c2005-09-19 21:59:42 +0200270 if (error) \
271 return error; \
272 return snprintf(buf, 20, "%u\n", phy->field); \
273}
274
275#define sas_phy_linkerror_attr(field) \
276 sas_phy_show_linkerror(field) \
277static CLASS_DEVICE_ATTR(field, S_IRUGO, show_sas_phy_##field, NULL)
278
279
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +0200280static ssize_t
281show_sas_device_type(struct class_device *cdev, char *buf)
282{
283 struct sas_phy *phy = transport_class_to_phy(cdev);
284
285 if (!phy->identify.device_type)
286 return snprintf(buf, 20, "none\n");
287 return get_sas_device_type_names(phy->identify.device_type, buf);
288}
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +0200289static CLASS_DEVICE_ATTR(device_type, S_IRUGO, show_sas_device_type, NULL);
290
Christoph Hellwig07ba3a92005-10-19 20:01:31 +0200291static ssize_t do_sas_phy_reset(struct class_device *cdev,
292 size_t count, int hard_reset)
293{
294 struct sas_phy *phy = transport_class_to_phy(cdev);
295 struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
296 struct sas_internal *i = to_sas_internal(shost->transportt);
297 int error;
298
Christoph Hellwig07ba3a92005-10-19 20:01:31 +0200299 error = i->f->phy_reset(phy, hard_reset);
300 if (error)
301 return error;
302 return count;
303};
304
305static ssize_t store_sas_link_reset(struct class_device *cdev,
306 const char *buf, size_t count)
307{
308 return do_sas_phy_reset(cdev, count, 0);
309}
310static CLASS_DEVICE_ATTR(link_reset, S_IWUSR, NULL, store_sas_link_reset);
311
312static ssize_t store_sas_hard_reset(struct class_device *cdev,
313 const char *buf, size_t count)
314{
315 return do_sas_phy_reset(cdev, count, 1);
316}
317static CLASS_DEVICE_ATTR(hard_reset, S_IWUSR, NULL, store_sas_hard_reset);
318
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +0200319sas_phy_protocol_attr(identify.initiator_port_protocols,
320 initiator_port_protocols);
321sas_phy_protocol_attr(identify.target_port_protocols,
322 target_port_protocols);
323sas_phy_simple_attr(identify.sas_address, sas_address, "0x%016llx\n",
324 unsigned long long);
325sas_phy_simple_attr(identify.phy_identifier, phy_identifier, "%d\n", u8);
James Bottomleyc9fefeb2006-07-02 11:10:18 -0500326//sas_phy_simple_attr(port_identifier, port_identifier, "%d\n", int);
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +0200327sas_phy_linkspeed_attr(negotiated_linkrate);
328sas_phy_linkspeed_attr(minimum_linkrate_hw);
329sas_phy_linkspeed_attr(minimum_linkrate);
330sas_phy_linkspeed_attr(maximum_linkrate_hw);
331sas_phy_linkspeed_attr(maximum_linkrate);
Christoph Hellwigc3ee74c2005-09-19 21:59:42 +0200332sas_phy_linkerror_attr(invalid_dword_count);
333sas_phy_linkerror_attr(running_disparity_error_count);
334sas_phy_linkerror_attr(loss_of_dword_sync_count);
335sas_phy_linkerror_attr(phy_reset_problem_count);
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +0200336
337
338static DECLARE_TRANSPORT_CLASS(sas_phy_class,
339 "sas_phy", NULL, NULL, NULL);
340
341static int sas_phy_match(struct attribute_container *cont, struct device *dev)
342{
343 struct Scsi_Host *shost;
344 struct sas_internal *i;
345
346 if (!scsi_is_sas_phy(dev))
347 return 0;
348 shost = dev_to_shost(dev->parent);
349
350 if (!shost->transportt)
351 return 0;
352 if (shost->transportt->host_attrs.ac.class !=
353 &sas_host_class.class)
354 return 0;
355
356 i = to_sas_internal(shost->transportt);
357 return &i->phy_attr_cont.ac == cont;
358}
359
360static void sas_phy_release(struct device *dev)
361{
362 struct sas_phy *phy = dev_to_phy(dev);
363
364 put_device(dev->parent);
365 kfree(phy);
366}
367
368/**
369 * sas_phy_alloc -- allocates and initialize a SAS PHY structure
370 * @parent: Parent device
Moore, Ericd99ca412006-01-26 16:20:02 -0700371 * @number: Phy index
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +0200372 *
373 * Allocates an SAS PHY structure. It will be added in the device tree
374 * below the device specified by @parent, which has to be either a Scsi_Host
375 * or sas_rphy.
376 *
377 * Returns:
378 * SAS PHY allocated or %NULL if the allocation failed.
379 */
380struct sas_phy *sas_phy_alloc(struct device *parent, int number)
381{
382 struct Scsi_Host *shost = dev_to_shost(parent);
383 struct sas_phy *phy;
384
Jes Sorensen24669f752006-01-16 10:31:18 -0500385 phy = kzalloc(sizeof(*phy), GFP_KERNEL);
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +0200386 if (!phy)
387 return NULL;
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +0200388
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +0200389 phy->number = number;
390
391 device_initialize(&phy->dev);
392 phy->dev.parent = get_device(parent);
393 phy->dev.release = sas_phy_release;
James Bottomley65c92b02006-06-28 12:22:50 -0400394 INIT_LIST_HEAD(&phy->port_siblings);
James Bottomley79cb1812006-03-13 13:50:04 -0600395 if (scsi_is_sas_expander_device(parent)) {
396 struct sas_rphy *rphy = dev_to_rphy(parent);
James Bottomley65c92b02006-06-28 12:22:50 -0400397 sprintf(phy->dev.bus_id, "phy-%d:%d:%d", shost->host_no,
James Bottomley79cb1812006-03-13 13:50:04 -0600398 rphy->scsi_target_id, number);
399 } else
400 sprintf(phy->dev.bus_id, "phy-%d:%d", shost->host_no, number);
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +0200401
402 transport_setup_device(&phy->dev);
403
404 return phy;
405}
406EXPORT_SYMBOL(sas_phy_alloc);
407
408/**
409 * sas_phy_add -- add a SAS PHY to the device hierachy
410 * @phy: The PHY to be added
411 *
412 * Publishes a SAS PHY to the rest of the system.
413 */
414int sas_phy_add(struct sas_phy *phy)
415{
416 int error;
417
418 error = device_add(&phy->dev);
419 if (!error) {
420 transport_add_device(&phy->dev);
421 transport_configure_device(&phy->dev);
422 }
423
424 return error;
425}
426EXPORT_SYMBOL(sas_phy_add);
427
428/**
429 * sas_phy_free -- free a SAS PHY
430 * @phy: SAS PHY to free
431 *
432 * Frees the specified SAS PHY.
433 *
434 * Note:
435 * This function must only be called on a PHY that has not
436 * sucessfully been added using sas_phy_add().
437 */
438void sas_phy_free(struct sas_phy *phy)
439{
440 transport_destroy_device(&phy->dev);
Mike Anderson92aab642006-03-27 09:37:28 -0800441 put_device(&phy->dev);
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +0200442}
443EXPORT_SYMBOL(sas_phy_free);
444
445/**
446 * sas_phy_delete -- remove SAS PHY
447 * @phy: SAS PHY to remove
448 *
449 * Removes the specified SAS PHY. If the SAS PHY has an
450 * associated remote PHY it is removed before.
451 */
452void
453sas_phy_delete(struct sas_phy *phy)
454{
455 struct device *dev = &phy->dev;
456
James Bottomley65c92b02006-06-28 12:22:50 -0400457 /* this happens if the phy is still part of a port when deleted */
458 BUG_ON(!list_empty(&phy->port_siblings));
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +0200459
460 transport_remove_device(dev);
461 device_del(dev);
462 transport_destroy_device(dev);
Mike Anderson92aab642006-03-27 09:37:28 -0800463 put_device(dev);
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +0200464}
465EXPORT_SYMBOL(sas_phy_delete);
466
467/**
468 * scsi_is_sas_phy -- check if a struct device represents a SAS PHY
469 * @dev: device to check
470 *
471 * Returns:
472 * %1 if the device represents a SAS PHY, %0 else
473 */
474int scsi_is_sas_phy(const struct device *dev)
475{
476 return dev->release == sas_phy_release;
477}
478EXPORT_SYMBOL(scsi_is_sas_phy);
479
480/*
James Bottomley65c92b02006-06-28 12:22:50 -0400481 * SAS Port attributes
482 */
483#define sas_port_show_simple(field, name, format_string, cast) \
484static ssize_t \
485show_sas_port_##name(struct class_device *cdev, char *buf) \
486{ \
487 struct sas_port *port = transport_class_to_sas_port(cdev); \
488 \
489 return snprintf(buf, 20, format_string, cast port->field); \
490}
491
492#define sas_port_simple_attr(field, name, format_string, type) \
493 sas_port_show_simple(field, name, format_string, (type)) \
494static CLASS_DEVICE_ATTR(name, S_IRUGO, show_sas_port_##name, NULL)
495
496sas_port_simple_attr(num_phys, num_phys, "%d\n", int);
497
498static DECLARE_TRANSPORT_CLASS(sas_port_class,
499 "sas_port", NULL, NULL, NULL);
500
501static int sas_port_match(struct attribute_container *cont, struct device *dev)
502{
503 struct Scsi_Host *shost;
504 struct sas_internal *i;
505
506 if (!scsi_is_sas_port(dev))
507 return 0;
508 shost = dev_to_shost(dev->parent);
509
510 if (!shost->transportt)
511 return 0;
512 if (shost->transportt->host_attrs.ac.class !=
513 &sas_host_class.class)
514 return 0;
515
516 i = to_sas_internal(shost->transportt);
517 return &i->port_attr_cont.ac == cont;
518}
519
520
521static void sas_port_release(struct device *dev)
522{
523 struct sas_port *port = dev_to_sas_port(dev);
524
525 BUG_ON(!list_empty(&port->phy_list));
526
527 put_device(dev->parent);
528 kfree(port);
529}
530
531static void sas_port_create_link(struct sas_port *port,
532 struct sas_phy *phy)
533{
534 sysfs_create_link(&port->dev.kobj, &phy->dev.kobj, phy->dev.bus_id);
535 sysfs_create_link(&phy->dev.kobj, &port->dev.kobj, "port");
536}
537
538static void sas_port_delete_link(struct sas_port *port,
539 struct sas_phy *phy)
540{
541 sysfs_remove_link(&port->dev.kobj, phy->dev.bus_id);
542 sysfs_remove_link(&phy->dev.kobj, "port");
543}
544
545/** sas_port_alloc - allocate and initialize a SAS port structure
546 *
547 * @parent: parent device
548 * @port_id: port number
549 *
550 * Allocates a SAS port structure. It will be added to the device tree
551 * below the device specified by @parent which must be either a Scsi_Host
552 * or a sas_expander_device.
553 *
554 * Returns %NULL on error
555 */
556struct sas_port *sas_port_alloc(struct device *parent, int port_id)
557{
558 struct Scsi_Host *shost = dev_to_shost(parent);
559 struct sas_port *port;
560
561 port = kzalloc(sizeof(*port), GFP_KERNEL);
562 if (!port)
563 return NULL;
564
565 port->port_identifier = port_id;
566
567 device_initialize(&port->dev);
568
569 port->dev.parent = get_device(parent);
570 port->dev.release = sas_port_release;
571
572 mutex_init(&port->phy_list_mutex);
573 INIT_LIST_HEAD(&port->phy_list);
574
575 if (scsi_is_sas_expander_device(parent)) {
576 struct sas_rphy *rphy = dev_to_rphy(parent);
577 sprintf(port->dev.bus_id, "port-%d:%d:%d", shost->host_no,
578 rphy->scsi_target_id, port->port_identifier);
579 } else
580 sprintf(port->dev.bus_id, "port-%d:%d", shost->host_no,
581 port->port_identifier);
582
583 transport_setup_device(&port->dev);
584
585 return port;
586}
587EXPORT_SYMBOL(sas_port_alloc);
588
James Bottomleyc9fefeb2006-07-02 11:10:18 -0500589/** sas_port_alloc_num - allocate and initialize a SAS port structure
590 *
591 * @parent: parent device
592 *
593 * Allocates a SAS port structure and a number to go with it. This
594 * interface is really for adapters where the port number has no
595 * meansing, so the sas class should manage them. It will be added to
596 * the device tree below the device specified by @parent which must be
597 * either a Scsi_Host or a sas_expander_device.
598 *
599 * Returns %NULL on error
600 */
601struct sas_port *sas_port_alloc_num(struct device *parent)
602{
603 int index;
604 struct Scsi_Host *shost = dev_to_shost(parent);
605 struct sas_host_attrs *sas_host = to_sas_host_attrs(shost);
606
607 /* FIXME: use idr for this eventually */
608 mutex_lock(&sas_host->lock);
609 if (scsi_is_sas_expander_device(parent)) {
610 struct sas_rphy *rphy = dev_to_rphy(parent);
611 struct sas_expander_device *exp = rphy_to_expander_device(rphy);
612
613 index = exp->next_port_id++;
614 } else
615 index = sas_host->next_port_id++;
616 mutex_unlock(&sas_host->lock);
617 return sas_port_alloc(parent, index);
618}
619EXPORT_SYMBOL(sas_port_alloc_num);
620
James Bottomley65c92b02006-06-28 12:22:50 -0400621/**
622 * sas_port_add - add a SAS port to the device hierarchy
623 *
624 * @port: port to be added
625 *
626 * publishes a port to the rest of the system
627 */
628int sas_port_add(struct sas_port *port)
629{
630 int error;
631
632 /* No phys should be added until this is made visible */
633 BUG_ON(!list_empty(&port->phy_list));
634
635 error = device_add(&port->dev);
636
637 if (error)
638 return error;
639
640 transport_add_device(&port->dev);
641 transport_configure_device(&port->dev);
642
643 return 0;
644}
645EXPORT_SYMBOL(sas_port_add);
646
647/**
648 * sas_port_free -- free a SAS PORT
649 * @port: SAS PORT to free
650 *
651 * Frees the specified SAS PORT.
652 *
653 * Note:
654 * This function must only be called on a PORT that has not
655 * sucessfully been added using sas_port_add().
656 */
657void sas_port_free(struct sas_port *port)
658{
659 transport_destroy_device(&port->dev);
660 put_device(&port->dev);
661}
662EXPORT_SYMBOL(sas_port_free);
663
664/**
665 * sas_port_delete -- remove SAS PORT
666 * @port: SAS PORT to remove
667 *
668 * Removes the specified SAS PORT. If the SAS PORT has an
669 * associated phys, unlink them from the port as well.
670 */
671void sas_port_delete(struct sas_port *port)
672{
673 struct device *dev = &port->dev;
674 struct sas_phy *phy, *tmp_phy;
675
676 if (port->rphy) {
677 sas_rphy_delete(port->rphy);
678 port->rphy = NULL;
679 }
680
681 mutex_lock(&port->phy_list_mutex);
682 list_for_each_entry_safe(phy, tmp_phy, &port->phy_list,
683 port_siblings) {
684 sas_port_delete_link(port, phy);
685 list_del_init(&phy->port_siblings);
686 }
687 mutex_unlock(&port->phy_list_mutex);
688
James Bottomleya0e1b6e2006-07-09 12:38:19 -0500689 if (port->is_backlink) {
690 struct device *parent = port->dev.parent;
691
692 sysfs_remove_link(&port->dev.kobj, parent->bus_id);
693 port->is_backlink = 0;
694 }
695
James Bottomley65c92b02006-06-28 12:22:50 -0400696 transport_remove_device(dev);
697 device_del(dev);
698 transport_destroy_device(dev);
699 put_device(dev);
700}
701EXPORT_SYMBOL(sas_port_delete);
702
703/**
704 * scsi_is_sas_port -- check if a struct device represents a SAS port
705 * @dev: device to check
706 *
707 * Returns:
708 * %1 if the device represents a SAS Port, %0 else
709 */
710int scsi_is_sas_port(const struct device *dev)
711{
712 return dev->release == sas_port_release;
713}
714EXPORT_SYMBOL(scsi_is_sas_port);
715
716/**
717 * sas_port_add_phy - add another phy to a port to form a wide port
718 * @port: port to add the phy to
719 * @phy: phy to add
720 *
721 * When a port is initially created, it is empty (has no phys). All
722 * ports must have at least one phy to operated, and all wide ports
723 * must have at least two. The current code makes no difference
724 * between ports and wide ports, but the only object that can be
725 * connected to a remote device is a port, so ports must be formed on
726 * all devices with phys if they're connected to anything.
727 */
728void sas_port_add_phy(struct sas_port *port, struct sas_phy *phy)
729{
730 mutex_lock(&port->phy_list_mutex);
731 if (unlikely(!list_empty(&phy->port_siblings))) {
732 /* make sure we're already on this port */
733 struct sas_phy *tmp;
734
735 list_for_each_entry(tmp, &port->phy_list, port_siblings)
736 if (tmp == phy)
737 break;
738 /* If this trips, you added a phy that was already
739 * part of a different port */
740 if (unlikely(tmp != phy)) {
741 dev_printk(KERN_ERR, &port->dev, "trying to add phy %s fails: it's already part of another port\n", phy->dev.bus_id);
742 BUG();
743 }
744 } else {
745 sas_port_create_link(port, phy);
746 list_add_tail(&phy->port_siblings, &port->phy_list);
747 port->num_phys++;
748 }
749 mutex_unlock(&port->phy_list_mutex);
750}
751EXPORT_SYMBOL(sas_port_add_phy);
752
753/**
754 * sas_port_delete_phy - remove a phy from a port or wide port
755 * @port: port to remove the phy from
756 * @phy: phy to remove
757 *
758 * This operation is used for tearing down ports again. It must be
759 * done to every port or wide port before calling sas_port_delete.
760 */
761void sas_port_delete_phy(struct sas_port *port, struct sas_phy *phy)
762{
763 mutex_lock(&port->phy_list_mutex);
764 sas_port_delete_link(port, phy);
765 list_del_init(&phy->port_siblings);
766 port->num_phys--;
767 mutex_unlock(&port->phy_list_mutex);
768}
769EXPORT_SYMBOL(sas_port_delete_phy);
770
James Bottomleya0e1b6e2006-07-09 12:38:19 -0500771void sas_port_mark_backlink(struct sas_port *port)
772{
773 struct device *parent = port->dev.parent->parent->parent;
774
775 if (port->is_backlink)
776 return;
777 port->is_backlink = 1;
778 sysfs_create_link(&port->dev.kobj, &parent->kobj,
779 parent->bus_id);
780
781}
782EXPORT_SYMBOL(sas_port_mark_backlink);
783
James Bottomley65c92b02006-06-28 12:22:50 -0400784/*
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +0200785 * SAS remote PHY attributes.
786 */
787
788#define sas_rphy_show_simple(field, name, format_string, cast) \
789static ssize_t \
790show_sas_rphy_##name(struct class_device *cdev, char *buf) \
791{ \
792 struct sas_rphy *rphy = transport_class_to_rphy(cdev); \
793 \
794 return snprintf(buf, 20, format_string, cast rphy->field); \
795}
796
797#define sas_rphy_simple_attr(field, name, format_string, type) \
798 sas_rphy_show_simple(field, name, format_string, (type)) \
799static SAS_CLASS_DEVICE_ATTR(rphy, name, S_IRUGO, \
800 show_sas_rphy_##name, NULL)
801
802#define sas_rphy_show_protocol(field, name) \
803static ssize_t \
804show_sas_rphy_##name(struct class_device *cdev, char *buf) \
805{ \
806 struct sas_rphy *rphy = transport_class_to_rphy(cdev); \
807 \
808 if (!rphy->field) \
809 return snprintf(buf, 20, "none\n"); \
810 return get_sas_protocol_names(rphy->field, buf); \
811}
812
813#define sas_rphy_protocol_attr(field, name) \
814 sas_rphy_show_protocol(field, name) \
815static SAS_CLASS_DEVICE_ATTR(rphy, name, S_IRUGO, \
816 show_sas_rphy_##name, NULL)
817
818static ssize_t
819show_sas_rphy_device_type(struct class_device *cdev, char *buf)
820{
821 struct sas_rphy *rphy = transport_class_to_rphy(cdev);
822
823 if (!rphy->identify.device_type)
824 return snprintf(buf, 20, "none\n");
825 return get_sas_device_type_names(
826 rphy->identify.device_type, buf);
827}
828
829static SAS_CLASS_DEVICE_ATTR(rphy, device_type, S_IRUGO,
830 show_sas_rphy_device_type, NULL);
831
Christoph Hellwiga0125642006-02-16 13:31:47 +0100832static ssize_t
833show_sas_rphy_enclosure_identifier(struct class_device *cdev, char *buf)
834{
835 struct sas_rphy *rphy = transport_class_to_rphy(cdev);
836 struct sas_phy *phy = dev_to_phy(rphy->dev.parent);
837 struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
838 struct sas_internal *i = to_sas_internal(shost->transportt);
839 u64 identifier;
840 int error;
841
842 /*
843 * Only devices behind an expander are supported, because the
844 * enclosure identifier is a SMP feature.
845 */
James Bottomleyf4ad7b52006-08-25 13:48:18 -0500846 if (scsi_is_sas_phy_local(phy))
Christoph Hellwiga0125642006-02-16 13:31:47 +0100847 return -EINVAL;
848
849 error = i->f->get_enclosure_identifier(rphy, &identifier);
850 if (error)
851 return error;
852 return sprintf(buf, "0x%llx\n", (unsigned long long)identifier);
853}
854
855static SAS_CLASS_DEVICE_ATTR(rphy, enclosure_identifier, S_IRUGO,
856 show_sas_rphy_enclosure_identifier, NULL);
857
858static ssize_t
859show_sas_rphy_bay_identifier(struct class_device *cdev, char *buf)
860{
861 struct sas_rphy *rphy = transport_class_to_rphy(cdev);
862 struct sas_phy *phy = dev_to_phy(rphy->dev.parent);
863 struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
864 struct sas_internal *i = to_sas_internal(shost->transportt);
865 int val;
866
James Bottomleyf4ad7b52006-08-25 13:48:18 -0500867 if (scsi_is_sas_phy_local(phy))
Christoph Hellwiga0125642006-02-16 13:31:47 +0100868 return -EINVAL;
869
870 val = i->f->get_bay_identifier(rphy);
871 if (val < 0)
872 return val;
873 return sprintf(buf, "%d\n", val);
874}
875
876static SAS_CLASS_DEVICE_ATTR(rphy, bay_identifier, S_IRUGO,
877 show_sas_rphy_bay_identifier, NULL);
878
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +0200879sas_rphy_protocol_attr(identify.initiator_port_protocols,
880 initiator_port_protocols);
881sas_rphy_protocol_attr(identify.target_port_protocols, target_port_protocols);
882sas_rphy_simple_attr(identify.sas_address, sas_address, "0x%016llx\n",
883 unsigned long long);
884sas_rphy_simple_attr(identify.phy_identifier, phy_identifier, "%d\n", u8);
885
James Bottomley42ab0362006-03-04 09:10:18 -0600886/* only need 8 bytes of data plus header (4 or 8) */
887#define BUF_SIZE 64
888
889int sas_read_port_mode_page(struct scsi_device *sdev)
890{
891 char *buffer = kzalloc(BUF_SIZE, GFP_KERNEL), *msdata;
892 struct sas_rphy *rphy = target_to_rphy(sdev->sdev_target);
893 struct sas_end_device *rdev;
894 struct scsi_mode_data mode_data;
895 int res, error;
896
897 BUG_ON(rphy->identify.device_type != SAS_END_DEVICE);
898
899 rdev = rphy_to_end_device(rphy);
900
901 if (!buffer)
902 return -ENOMEM;
903
904 res = scsi_mode_sense(sdev, 1, 0x19, buffer, BUF_SIZE, 30*HZ, 3,
905 &mode_data, NULL);
906
907 error = -EINVAL;
908 if (!scsi_status_is_good(res))
909 goto out;
910
911 msdata = buffer + mode_data.header_length +
912 mode_data.block_descriptor_length;
913
914 if (msdata - buffer > BUF_SIZE - 8)
915 goto out;
916
917 error = 0;
918
919 rdev->ready_led_meaning = msdata[2] & 0x10 ? 1 : 0;
920 rdev->I_T_nexus_loss_timeout = (msdata[4] << 8) + msdata[5];
921 rdev->initiator_response_timeout = (msdata[6] << 8) + msdata[7];
922
923 out:
924 kfree(buffer);
925 return error;
926}
927EXPORT_SYMBOL(sas_read_port_mode_page);
928
James Bottomley79cb1812006-03-13 13:50:04 -0600929static DECLARE_TRANSPORT_CLASS(sas_end_dev_class,
930 "sas_end_device", NULL, NULL, NULL);
931
James Bottomley42ab0362006-03-04 09:10:18 -0600932#define sas_end_dev_show_simple(field, name, format_string, cast) \
933static ssize_t \
934show_sas_end_dev_##name(struct class_device *cdev, char *buf) \
935{ \
936 struct sas_rphy *rphy = transport_class_to_rphy(cdev); \
937 struct sas_end_device *rdev = rphy_to_end_device(rphy); \
938 \
939 return snprintf(buf, 20, format_string, cast rdev->field); \
940}
941
942#define sas_end_dev_simple_attr(field, name, format_string, type) \
943 sas_end_dev_show_simple(field, name, format_string, (type)) \
944static SAS_CLASS_DEVICE_ATTR(end_dev, name, S_IRUGO, \
945 show_sas_end_dev_##name, NULL)
946
947sas_end_dev_simple_attr(ready_led_meaning, ready_led_meaning, "%d\n", int);
948sas_end_dev_simple_attr(I_T_nexus_loss_timeout, I_T_nexus_loss_timeout,
949 "%d\n", int);
950sas_end_dev_simple_attr(initiator_response_timeout, initiator_response_timeout,
951 "%d\n", int);
952
James Bottomley79cb1812006-03-13 13:50:04 -0600953static DECLARE_TRANSPORT_CLASS(sas_expander_class,
954 "sas_expander", NULL, NULL, NULL);
955
956#define sas_expander_show_simple(field, name, format_string, cast) \
957static ssize_t \
958show_sas_expander_##name(struct class_device *cdev, char *buf) \
959{ \
960 struct sas_rphy *rphy = transport_class_to_rphy(cdev); \
961 struct sas_expander_device *edev = rphy_to_expander_device(rphy); \
962 \
963 return snprintf(buf, 20, format_string, cast edev->field); \
964}
965
966#define sas_expander_simple_attr(field, name, format_string, type) \
967 sas_expander_show_simple(field, name, format_string, (type)) \
968static SAS_CLASS_DEVICE_ATTR(expander, name, S_IRUGO, \
969 show_sas_expander_##name, NULL)
970
971sas_expander_simple_attr(vendor_id, vendor_id, "%s\n", char *);
972sas_expander_simple_attr(product_id, product_id, "%s\n", char *);
973sas_expander_simple_attr(product_rev, product_rev, "%s\n", char *);
974sas_expander_simple_attr(component_vendor_id, component_vendor_id,
975 "%s\n", char *);
976sas_expander_simple_attr(component_id, component_id, "%u\n", unsigned int);
977sas_expander_simple_attr(component_revision_id, component_revision_id, "%u\n",
978 unsigned int);
979sas_expander_simple_attr(level, level, "%d\n", int);
James Bottomley42ab0362006-03-04 09:10:18 -0600980
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +0200981static DECLARE_TRANSPORT_CLASS(sas_rphy_class,
James Bottomley2f8600d2006-03-18 15:00:50 -0600982 "sas_device", NULL, NULL, NULL);
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +0200983
984static int sas_rphy_match(struct attribute_container *cont, struct device *dev)
985{
986 struct Scsi_Host *shost;
987 struct sas_internal *i;
988
989 if (!scsi_is_sas_rphy(dev))
990 return 0;
991 shost = dev_to_shost(dev->parent->parent);
992
993 if (!shost->transportt)
994 return 0;
995 if (shost->transportt->host_attrs.ac.class !=
996 &sas_host_class.class)
997 return 0;
998
999 i = to_sas_internal(shost->transportt);
1000 return &i->rphy_attr_cont.ac == cont;
1001}
1002
James Bottomley42ab0362006-03-04 09:10:18 -06001003static int sas_end_dev_match(struct attribute_container *cont,
1004 struct device *dev)
1005{
1006 struct Scsi_Host *shost;
1007 struct sas_internal *i;
1008 struct sas_rphy *rphy;
1009
1010 if (!scsi_is_sas_rphy(dev))
1011 return 0;
1012 shost = dev_to_shost(dev->parent->parent);
1013 rphy = dev_to_rphy(dev);
1014
1015 if (!shost->transportt)
1016 return 0;
1017 if (shost->transportt->host_attrs.ac.class !=
1018 &sas_host_class.class)
1019 return 0;
1020
1021 i = to_sas_internal(shost->transportt);
1022 return &i->end_dev_attr_cont.ac == cont &&
James Bottomley2f8600d2006-03-18 15:00:50 -06001023 rphy->identify.device_type == SAS_END_DEVICE;
James Bottomley42ab0362006-03-04 09:10:18 -06001024}
1025
James Bottomley79cb1812006-03-13 13:50:04 -06001026static int sas_expander_match(struct attribute_container *cont,
1027 struct device *dev)
1028{
1029 struct Scsi_Host *shost;
1030 struct sas_internal *i;
1031 struct sas_rphy *rphy;
1032
1033 if (!scsi_is_sas_rphy(dev))
1034 return 0;
1035 shost = dev_to_shost(dev->parent->parent);
1036 rphy = dev_to_rphy(dev);
1037
1038 if (!shost->transportt)
1039 return 0;
1040 if (shost->transportt->host_attrs.ac.class !=
1041 &sas_host_class.class)
1042 return 0;
1043
1044 i = to_sas_internal(shost->transportt);
1045 return &i->expander_attr_cont.ac == cont &&
1046 (rphy->identify.device_type == SAS_EDGE_EXPANDER_DEVICE ||
James Bottomley2f8600d2006-03-18 15:00:50 -06001047 rphy->identify.device_type == SAS_FANOUT_EXPANDER_DEVICE);
James Bottomley79cb1812006-03-13 13:50:04 -06001048}
1049
James Bottomley2f8600d2006-03-18 15:00:50 -06001050static void sas_expander_release(struct device *dev)
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001051{
1052 struct sas_rphy *rphy = dev_to_rphy(dev);
James Bottomley2f8600d2006-03-18 15:00:50 -06001053 struct sas_expander_device *edev = rphy_to_expander_device(rphy);
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001054
1055 put_device(dev->parent);
James Bottomley2f8600d2006-03-18 15:00:50 -06001056 kfree(edev);
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001057}
1058
James Bottomley2f8600d2006-03-18 15:00:50 -06001059static void sas_end_device_release(struct device *dev)
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001060{
James Bottomley2f8600d2006-03-18 15:00:50 -06001061 struct sas_rphy *rphy = dev_to_rphy(dev);
1062 struct sas_end_device *edev = rphy_to_end_device(rphy);
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001063
James Bottomley2f8600d2006-03-18 15:00:50 -06001064 put_device(dev->parent);
1065 kfree(edev);
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001066}
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001067
1068/**
James Bottomleyc5943d32006-06-12 09:09:18 -05001069 * sas_rphy_initialize - common rphy intialization
1070 * @rphy: rphy to initialise
1071 *
1072 * Used by both sas_end_device_alloc() and sas_expander_alloc() to
1073 * initialise the common rphy component of each.
1074 */
1075static void sas_rphy_initialize(struct sas_rphy *rphy)
1076{
1077 INIT_LIST_HEAD(&rphy->list);
1078}
1079
1080/**
James Bottomley42ab0362006-03-04 09:10:18 -06001081 * sas_end_device_alloc - allocate an rphy for an end device
1082 *
1083 * Allocates an SAS remote PHY structure, connected to @parent.
1084 *
1085 * Returns:
1086 * SAS PHY allocated or %NULL if the allocation failed.
1087 */
James Bottomley65c92b02006-06-28 12:22:50 -04001088struct sas_rphy *sas_end_device_alloc(struct sas_port *parent)
James Bottomley42ab0362006-03-04 09:10:18 -06001089{
1090 struct Scsi_Host *shost = dev_to_shost(&parent->dev);
1091 struct sas_end_device *rdev;
1092
1093 rdev = kzalloc(sizeof(*rdev), GFP_KERNEL);
1094 if (!rdev) {
James Bottomley42ab0362006-03-04 09:10:18 -06001095 return NULL;
1096 }
1097
1098 device_initialize(&rdev->rphy.dev);
1099 rdev->rphy.dev.parent = get_device(&parent->dev);
James Bottomley2f8600d2006-03-18 15:00:50 -06001100 rdev->rphy.dev.release = sas_end_device_release;
James Bottomley65c92b02006-06-28 12:22:50 -04001101 if (scsi_is_sas_expander_device(parent->dev.parent)) {
1102 struct sas_rphy *rphy = dev_to_rphy(parent->dev.parent);
1103 sprintf(rdev->rphy.dev.bus_id, "end_device-%d:%d:%d",
1104 shost->host_no, rphy->scsi_target_id, parent->port_identifier);
1105 } else
1106 sprintf(rdev->rphy.dev.bus_id, "end_device-%d:%d",
1107 shost->host_no, parent->port_identifier);
James Bottomley42ab0362006-03-04 09:10:18 -06001108 rdev->rphy.identify.device_type = SAS_END_DEVICE;
James Bottomleyc5943d32006-06-12 09:09:18 -05001109 sas_rphy_initialize(&rdev->rphy);
James Bottomley42ab0362006-03-04 09:10:18 -06001110 transport_setup_device(&rdev->rphy.dev);
1111
1112 return &rdev->rphy;
1113}
1114EXPORT_SYMBOL(sas_end_device_alloc);
1115
James Bottomley79cb1812006-03-13 13:50:04 -06001116/**
1117 * sas_expander_alloc - allocate an rphy for an end device
1118 *
1119 * Allocates an SAS remote PHY structure, connected to @parent.
1120 *
1121 * Returns:
1122 * SAS PHY allocated or %NULL if the allocation failed.
1123 */
James Bottomley65c92b02006-06-28 12:22:50 -04001124struct sas_rphy *sas_expander_alloc(struct sas_port *parent,
James Bottomley79cb1812006-03-13 13:50:04 -06001125 enum sas_device_type type)
1126{
1127 struct Scsi_Host *shost = dev_to_shost(&parent->dev);
1128 struct sas_expander_device *rdev;
1129 struct sas_host_attrs *sas_host = to_sas_host_attrs(shost);
1130
1131 BUG_ON(type != SAS_EDGE_EXPANDER_DEVICE &&
1132 type != SAS_FANOUT_EXPANDER_DEVICE);
1133
1134 rdev = kzalloc(sizeof(*rdev), GFP_KERNEL);
1135 if (!rdev) {
James Bottomley79cb1812006-03-13 13:50:04 -06001136 return NULL;
1137 }
1138
1139 device_initialize(&rdev->rphy.dev);
1140 rdev->rphy.dev.parent = get_device(&parent->dev);
James Bottomley2f8600d2006-03-18 15:00:50 -06001141 rdev->rphy.dev.release = sas_expander_release;
James Bottomley79cb1812006-03-13 13:50:04 -06001142 mutex_lock(&sas_host->lock);
1143 rdev->rphy.scsi_target_id = sas_host->next_expander_id++;
1144 mutex_unlock(&sas_host->lock);
1145 sprintf(rdev->rphy.dev.bus_id, "expander-%d:%d",
1146 shost->host_no, rdev->rphy.scsi_target_id);
1147 rdev->rphy.identify.device_type = type;
James Bottomleyc5943d32006-06-12 09:09:18 -05001148 sas_rphy_initialize(&rdev->rphy);
James Bottomley79cb1812006-03-13 13:50:04 -06001149 transport_setup_device(&rdev->rphy.dev);
1150
1151 return &rdev->rphy;
1152}
1153EXPORT_SYMBOL(sas_expander_alloc);
James Bottomley42ab0362006-03-04 09:10:18 -06001154
1155/**
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001156 * sas_rphy_add -- add a SAS remote PHY to the device hierachy
1157 * @rphy: The remote PHY to be added
1158 *
1159 * Publishes a SAS remote PHY to the rest of the system.
1160 */
1161int sas_rphy_add(struct sas_rphy *rphy)
1162{
James Bottomley65c92b02006-06-28 12:22:50 -04001163 struct sas_port *parent = dev_to_sas_port(rphy->dev.parent);
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001164 struct Scsi_Host *shost = dev_to_shost(parent->dev.parent);
1165 struct sas_host_attrs *sas_host = to_sas_host_attrs(shost);
1166 struct sas_identify *identify = &rphy->identify;
1167 int error;
1168
1169 if (parent->rphy)
1170 return -ENXIO;
1171 parent->rphy = rphy;
1172
1173 error = device_add(&rphy->dev);
1174 if (error)
1175 return error;
1176 transport_add_device(&rphy->dev);
1177 transport_configure_device(&rphy->dev);
1178
Christoph Hellwige02f3f52006-01-13 19:04:00 +01001179 mutex_lock(&sas_host->lock);
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001180 list_add_tail(&rphy->list, &sas_host->rphy_list);
1181 if (identify->device_type == SAS_END_DEVICE &&
1182 (identify->target_port_protocols &
1183 (SAS_PROTOCOL_SSP|SAS_PROTOCOL_STP|SAS_PROTOCOL_SATA)))
1184 rphy->scsi_target_id = sas_host->next_target_id++;
James Bottomley7676f832006-04-14 09:47:59 -05001185 else if (identify->device_type == SAS_END_DEVICE)
1186 rphy->scsi_target_id = -1;
Christoph Hellwige02f3f52006-01-13 19:04:00 +01001187 mutex_unlock(&sas_host->lock);
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001188
James Bottomley79cb1812006-03-13 13:50:04 -06001189 if (identify->device_type == SAS_END_DEVICE &&
1190 rphy->scsi_target_id != -1) {
James Bottomleye8bf3942006-07-11 17:49:34 -04001191 scsi_scan_target(&rphy->dev, 0,
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001192 rphy->scsi_target_id, ~0, 0);
1193 }
1194
1195 return 0;
1196}
1197EXPORT_SYMBOL(sas_rphy_add);
1198
1199/**
1200 * sas_rphy_free -- free a SAS remote PHY
1201 * @rphy SAS remote PHY to free
1202 *
1203 * Frees the specified SAS remote PHY.
1204 *
1205 * Note:
1206 * This function must only be called on a remote
1207 * PHY that has not sucessfully been added using
1208 * sas_rphy_add().
1209 */
1210void sas_rphy_free(struct sas_rphy *rphy)
1211{
Mike Anderson92aab642006-03-27 09:37:28 -08001212 struct device *dev = &rphy->dev;
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001213 struct Scsi_Host *shost = dev_to_shost(rphy->dev.parent->parent);
1214 struct sas_host_attrs *sas_host = to_sas_host_attrs(shost);
1215
Christoph Hellwige02f3f52006-01-13 19:04:00 +01001216 mutex_lock(&sas_host->lock);
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001217 list_del(&rphy->list);
Christoph Hellwige02f3f52006-01-13 19:04:00 +01001218 mutex_unlock(&sas_host->lock);
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001219
Mike Anderson92aab642006-03-27 09:37:28 -08001220 transport_destroy_device(dev);
James Bottomley2f8600d2006-03-18 15:00:50 -06001221
Mike Anderson92aab642006-03-27 09:37:28 -08001222 put_device(dev);
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001223}
1224EXPORT_SYMBOL(sas_rphy_free);
1225
1226/**
1227 * sas_rphy_delete -- remove SAS remote PHY
1228 * @rphy: SAS remote PHY to remove
1229 *
1230 * Removes the specified SAS remote PHY.
1231 */
1232void
1233sas_rphy_delete(struct sas_rphy *rphy)
1234{
1235 struct device *dev = &rphy->dev;
James Bottomley65c92b02006-06-28 12:22:50 -04001236 struct sas_port *parent = dev_to_sas_port(dev->parent);
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001237 struct Scsi_Host *shost = dev_to_shost(parent->dev.parent);
1238 struct sas_host_attrs *sas_host = to_sas_host_attrs(shost);
1239
Christoph Hellwigd4054232006-01-04 13:45:20 +01001240 switch (rphy->identify.device_type) {
1241 case SAS_END_DEVICE:
1242 scsi_remove_target(dev);
1243 break;
1244 case SAS_EDGE_EXPANDER_DEVICE:
1245 case SAS_FANOUT_EXPANDER_DEVICE:
James Bottomley65c92b02006-06-28 12:22:50 -04001246 sas_remove_children(dev);
Christoph Hellwigd4054232006-01-04 13:45:20 +01001247 break;
1248 default:
1249 break;
1250 }
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001251
Christoph Hellwigfe8b2302005-09-25 23:10:33 +02001252 transport_remove_device(dev);
1253 device_del(dev);
1254 transport_destroy_device(dev);
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001255
Christoph Hellwige02f3f52006-01-13 19:04:00 +01001256 mutex_lock(&sas_host->lock);
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001257 list_del(&rphy->list);
Christoph Hellwige02f3f52006-01-13 19:04:00 +01001258 mutex_unlock(&sas_host->lock);
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001259
Christoph Hellwig33b114e2006-01-11 14:20:43 +01001260 parent->rphy = NULL;
1261
Mike Anderson92aab642006-03-27 09:37:28 -08001262 put_device(dev);
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001263}
1264EXPORT_SYMBOL(sas_rphy_delete);
1265
1266/**
1267 * scsi_is_sas_rphy -- check if a struct device represents a SAS remote PHY
1268 * @dev: device to check
1269 *
1270 * Returns:
1271 * %1 if the device represents a SAS remote PHY, %0 else
1272 */
1273int scsi_is_sas_rphy(const struct device *dev)
1274{
James Bottomley2f8600d2006-03-18 15:00:50 -06001275 return dev->release == sas_end_device_release ||
1276 dev->release == sas_expander_release;
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001277}
1278EXPORT_SYMBOL(scsi_is_sas_rphy);
1279
1280
1281/*
1282 * SCSI scan helper
1283 */
1284
Christoph Hellwige02f3f52006-01-13 19:04:00 +01001285static int sas_user_scan(struct Scsi_Host *shost, uint channel,
1286 uint id, uint lun)
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001287{
1288 struct sas_host_attrs *sas_host = to_sas_host_attrs(shost);
1289 struct sas_rphy *rphy;
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001290
Christoph Hellwige02f3f52006-01-13 19:04:00 +01001291 mutex_lock(&sas_host->lock);
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001292 list_for_each_entry(rphy, &sas_host->rphy_list, list) {
James Bottomley6d99a3f2006-05-19 10:49:37 -05001293 if (rphy->identify.device_type != SAS_END_DEVICE ||
1294 rphy->scsi_target_id == -1)
Christoph Hellwige02f3f52006-01-13 19:04:00 +01001295 continue;
1296
James Bottomleye8bf3942006-07-11 17:49:34 -04001297 if ((channel == SCAN_WILD_CARD || channel == 0) &&
Christoph Hellwige02f3f52006-01-13 19:04:00 +01001298 (id == SCAN_WILD_CARD || id == rphy->scsi_target_id)) {
James Bottomleye8bf3942006-07-11 17:49:34 -04001299 scsi_scan_target(&rphy->dev, 0,
Christoph Hellwige02f3f52006-01-13 19:04:00 +01001300 rphy->scsi_target_id, lun, 1);
1301 }
1302 }
1303 mutex_unlock(&sas_host->lock);
1304
1305 return 0;
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001306}
1307
1308
1309/*
1310 * Setup / Teardown code
1311 */
1312
James Bottomley42ab0362006-03-04 09:10:18 -06001313#define SETUP_TEMPLATE(attrb, field, perm, test) \
1314 i->private_##attrb[count] = class_device_attr_##field; \
1315 i->private_##attrb[count].attr.mode = perm; \
James Bottomley42ab0362006-03-04 09:10:18 -06001316 i->attrb[count] = &i->private_##attrb[count]; \
1317 if (test) \
1318 count++
1319
1320
1321#define SETUP_RPORT_ATTRIBUTE(field) \
1322 SETUP_TEMPLATE(rphy_attrs, field, S_IRUGO, 1)
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001323
James Bottomleydd9fbb522006-03-02 16:01:31 -06001324#define SETUP_OPTIONAL_RPORT_ATTRIBUTE(field, func) \
James Bottomley42ab0362006-03-04 09:10:18 -06001325 SETUP_TEMPLATE(rphy_attrs, field, S_IRUGO, i->f->func)
James Bottomleydd9fbb522006-03-02 16:01:31 -06001326
James Bottomley65c92b02006-06-28 12:22:50 -04001327#define SETUP_PHY_ATTRIBUTE(field) \
James Bottomley42ab0362006-03-04 09:10:18 -06001328 SETUP_TEMPLATE(phy_attrs, field, S_IRUGO, 1)
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001329
James Bottomley65c92b02006-06-28 12:22:50 -04001330#define SETUP_PORT_ATTRIBUTE(field) \
1331 SETUP_TEMPLATE(port_attrs, field, S_IRUGO, 1)
1332
1333#define SETUP_OPTIONAL_PHY_ATTRIBUTE(field, func) \
James Bottomley42ab0362006-03-04 09:10:18 -06001334 SETUP_TEMPLATE(phy_attrs, field, S_IRUGO, i->f->func)
James Bottomleydd9fbb522006-03-02 16:01:31 -06001335
James Bottomley65c92b02006-06-28 12:22:50 -04001336#define SETUP_PHY_ATTRIBUTE_WRONLY(field) \
James Bottomley42ab0362006-03-04 09:10:18 -06001337 SETUP_TEMPLATE(phy_attrs, field, S_IWUGO, 1)
Christoph Hellwig07ba3a92005-10-19 20:01:31 +02001338
James Bottomley65c92b02006-06-28 12:22:50 -04001339#define SETUP_OPTIONAL_PHY_ATTRIBUTE_WRONLY(field, func) \
James Bottomley42ab0362006-03-04 09:10:18 -06001340 SETUP_TEMPLATE(phy_attrs, field, S_IWUGO, i->f->func)
James Bottomleydd9fbb522006-03-02 16:01:31 -06001341
James Bottomley42ab0362006-03-04 09:10:18 -06001342#define SETUP_END_DEV_ATTRIBUTE(field) \
1343 SETUP_TEMPLATE(end_dev_attrs, field, S_IRUGO, 1)
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001344
James Bottomley79cb1812006-03-13 13:50:04 -06001345#define SETUP_EXPANDER_ATTRIBUTE(field) \
1346 SETUP_TEMPLATE(expander_attrs, expander_##field, S_IRUGO, 1)
1347
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001348/**
1349 * sas_attach_transport -- instantiate SAS transport template
1350 * @ft: SAS transport class function template
1351 */
1352struct scsi_transport_template *
1353sas_attach_transport(struct sas_function_template *ft)
1354{
1355 struct sas_internal *i;
1356 int count;
1357
Jes Sorensen24669f752006-01-16 10:31:18 -05001358 i = kzalloc(sizeof(struct sas_internal), GFP_KERNEL);
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001359 if (!i)
1360 return NULL;
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001361
Christoph Hellwige02f3f52006-01-13 19:04:00 +01001362 i->t.user_scan = sas_user_scan;
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001363
1364 i->t.host_attrs.ac.attrs = &i->host_attrs[0];
1365 i->t.host_attrs.ac.class = &sas_host_class.class;
1366 i->t.host_attrs.ac.match = sas_host_match;
1367 transport_container_register(&i->t.host_attrs);
1368 i->t.host_size = sizeof(struct sas_host_attrs);
1369
1370 i->phy_attr_cont.ac.class = &sas_phy_class.class;
1371 i->phy_attr_cont.ac.attrs = &i->phy_attrs[0];
1372 i->phy_attr_cont.ac.match = sas_phy_match;
1373 transport_container_register(&i->phy_attr_cont);
1374
James Bottomley65c92b02006-06-28 12:22:50 -04001375 i->port_attr_cont.ac.class = &sas_port_class.class;
1376 i->port_attr_cont.ac.attrs = &i->port_attrs[0];
1377 i->port_attr_cont.ac.match = sas_port_match;
1378 transport_container_register(&i->port_attr_cont);
1379
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001380 i->rphy_attr_cont.ac.class = &sas_rphy_class.class;
1381 i->rphy_attr_cont.ac.attrs = &i->rphy_attrs[0];
1382 i->rphy_attr_cont.ac.match = sas_rphy_match;
1383 transport_container_register(&i->rphy_attr_cont);
1384
James Bottomley42ab0362006-03-04 09:10:18 -06001385 i->end_dev_attr_cont.ac.class = &sas_end_dev_class.class;
1386 i->end_dev_attr_cont.ac.attrs = &i->end_dev_attrs[0];
1387 i->end_dev_attr_cont.ac.match = sas_end_dev_match;
1388 transport_container_register(&i->end_dev_attr_cont);
1389
James Bottomley79cb1812006-03-13 13:50:04 -06001390 i->expander_attr_cont.ac.class = &sas_expander_class.class;
1391 i->expander_attr_cont.ac.attrs = &i->expander_attrs[0];
1392 i->expander_attr_cont.ac.match = sas_expander_match;
1393 transport_container_register(&i->expander_attr_cont);
1394
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001395 i->f = ft;
1396
1397 count = 0;
James Bottomley65c92b02006-06-28 12:22:50 -04001398 SETUP_PORT_ATTRIBUTE(num_phys);
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001399 i->host_attrs[count] = NULL;
1400
1401 count = 0;
James Bottomley65c92b02006-06-28 12:22:50 -04001402 SETUP_PHY_ATTRIBUTE(initiator_port_protocols);
1403 SETUP_PHY_ATTRIBUTE(target_port_protocols);
1404 SETUP_PHY_ATTRIBUTE(device_type);
1405 SETUP_PHY_ATTRIBUTE(sas_address);
1406 SETUP_PHY_ATTRIBUTE(phy_identifier);
1407 //SETUP_PHY_ATTRIBUTE(port_identifier);
1408 SETUP_PHY_ATTRIBUTE(negotiated_linkrate);
1409 SETUP_PHY_ATTRIBUTE(minimum_linkrate_hw);
1410 SETUP_PHY_ATTRIBUTE(minimum_linkrate);
1411 SETUP_PHY_ATTRIBUTE(maximum_linkrate_hw);
1412 SETUP_PHY_ATTRIBUTE(maximum_linkrate);
Christoph Hellwigc3ee74c2005-09-19 21:59:42 +02001413
James Bottomley65c92b02006-06-28 12:22:50 -04001414 SETUP_PHY_ATTRIBUTE(invalid_dword_count);
1415 SETUP_PHY_ATTRIBUTE(running_disparity_error_count);
1416 SETUP_PHY_ATTRIBUTE(loss_of_dword_sync_count);
1417 SETUP_PHY_ATTRIBUTE(phy_reset_problem_count);
1418 SETUP_OPTIONAL_PHY_ATTRIBUTE_WRONLY(link_reset, phy_reset);
1419 SETUP_OPTIONAL_PHY_ATTRIBUTE_WRONLY(hard_reset, phy_reset);
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001420 i->phy_attrs[count] = NULL;
1421
1422 count = 0;
James Bottomley65c92b02006-06-28 12:22:50 -04001423 SETUP_PORT_ATTRIBUTE(num_phys);
1424 i->port_attrs[count] = NULL;
1425
1426 count = 0;
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001427 SETUP_RPORT_ATTRIBUTE(rphy_initiator_port_protocols);
1428 SETUP_RPORT_ATTRIBUTE(rphy_target_port_protocols);
1429 SETUP_RPORT_ATTRIBUTE(rphy_device_type);
1430 SETUP_RPORT_ATTRIBUTE(rphy_sas_address);
1431 SETUP_RPORT_ATTRIBUTE(rphy_phy_identifier);
James Bottomleydd9fbb522006-03-02 16:01:31 -06001432 SETUP_OPTIONAL_RPORT_ATTRIBUTE(rphy_enclosure_identifier,
1433 get_enclosure_identifier);
1434 SETUP_OPTIONAL_RPORT_ATTRIBUTE(rphy_bay_identifier,
1435 get_bay_identifier);
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001436 i->rphy_attrs[count] = NULL;
1437
James Bottomley42ab0362006-03-04 09:10:18 -06001438 count = 0;
1439 SETUP_END_DEV_ATTRIBUTE(end_dev_ready_led_meaning);
1440 SETUP_END_DEV_ATTRIBUTE(end_dev_I_T_nexus_loss_timeout);
1441 SETUP_END_DEV_ATTRIBUTE(end_dev_initiator_response_timeout);
1442 i->end_dev_attrs[count] = NULL;
1443
James Bottomley79cb1812006-03-13 13:50:04 -06001444 count = 0;
1445 SETUP_EXPANDER_ATTRIBUTE(vendor_id);
1446 SETUP_EXPANDER_ATTRIBUTE(product_id);
1447 SETUP_EXPANDER_ATTRIBUTE(product_rev);
1448 SETUP_EXPANDER_ATTRIBUTE(component_vendor_id);
1449 SETUP_EXPANDER_ATTRIBUTE(component_id);
1450 SETUP_EXPANDER_ATTRIBUTE(component_revision_id);
1451 SETUP_EXPANDER_ATTRIBUTE(level);
1452 i->expander_attrs[count] = NULL;
1453
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001454 return &i->t;
1455}
1456EXPORT_SYMBOL(sas_attach_transport);
1457
1458/**
1459 * sas_release_transport -- release SAS transport template instance
1460 * @t: transport template instance
1461 */
1462void sas_release_transport(struct scsi_transport_template *t)
1463{
1464 struct sas_internal *i = to_sas_internal(t);
1465
1466 transport_container_unregister(&i->t.host_attrs);
1467 transport_container_unregister(&i->phy_attr_cont);
James Bottomley65c92b02006-06-28 12:22:50 -04001468 transport_container_unregister(&i->port_attr_cont);
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001469 transport_container_unregister(&i->rphy_attr_cont);
James Bottomleydb82f842006-03-09 22:06:36 -05001470 transport_container_unregister(&i->end_dev_attr_cont);
James Bottomley79cb1812006-03-13 13:50:04 -06001471 transport_container_unregister(&i->expander_attr_cont);
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001472
1473 kfree(i);
1474}
1475EXPORT_SYMBOL(sas_release_transport);
1476
1477static __init int sas_transport_init(void)
1478{
1479 int error;
1480
1481 error = transport_class_register(&sas_host_class);
1482 if (error)
1483 goto out;
1484 error = transport_class_register(&sas_phy_class);
1485 if (error)
1486 goto out_unregister_transport;
James Bottomley65c92b02006-06-28 12:22:50 -04001487 error = transport_class_register(&sas_port_class);
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001488 if (error)
1489 goto out_unregister_phy;
James Bottomley65c92b02006-06-28 12:22:50 -04001490 error = transport_class_register(&sas_rphy_class);
1491 if (error)
1492 goto out_unregister_port;
James Bottomley42ab0362006-03-04 09:10:18 -06001493 error = transport_class_register(&sas_end_dev_class);
1494 if (error)
1495 goto out_unregister_rphy;
James Bottomley79cb1812006-03-13 13:50:04 -06001496 error = transport_class_register(&sas_expander_class);
1497 if (error)
1498 goto out_unregister_end_dev;
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001499
1500 return 0;
1501
James Bottomley79cb1812006-03-13 13:50:04 -06001502 out_unregister_end_dev:
1503 transport_class_unregister(&sas_end_dev_class);
James Bottomley42ab0362006-03-04 09:10:18 -06001504 out_unregister_rphy:
1505 transport_class_unregister(&sas_rphy_class);
James Bottomley65c92b02006-06-28 12:22:50 -04001506 out_unregister_port:
1507 transport_class_unregister(&sas_port_class);
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001508 out_unregister_phy:
1509 transport_class_unregister(&sas_phy_class);
1510 out_unregister_transport:
1511 transport_class_unregister(&sas_host_class);
1512 out:
1513 return error;
1514
1515}
1516
1517static void __exit sas_transport_exit(void)
1518{
1519 transport_class_unregister(&sas_host_class);
1520 transport_class_unregister(&sas_phy_class);
James Bottomley65c92b02006-06-28 12:22:50 -04001521 transport_class_unregister(&sas_port_class);
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001522 transport_class_unregister(&sas_rphy_class);
James Bottomley42ab0362006-03-04 09:10:18 -06001523 transport_class_unregister(&sas_end_dev_class);
James Bottomley79cb1812006-03-13 13:50:04 -06001524 transport_class_unregister(&sas_expander_class);
Christoph Hellwigc7ebbbc2005-09-09 16:22:50 +02001525}
1526
1527MODULE_AUTHOR("Christoph Hellwig");
1528MODULE_DESCRIPTION("SAS Transphy Attributes");
1529MODULE_LICENSE("GPL");
1530
1531module_init(sas_transport_init);
1532module_exit(sas_transport_exit);