| 1 | // SPDX-License-Identifier: GPL-2.0-only | 
|---|
| 2 | /* | 
|---|
| 3 | *  Copyright 2008 ioogle, Inc.  All rights reserved. | 
|---|
| 4 | * | 
|---|
| 5 | * Libata transport class. | 
|---|
| 6 | * | 
|---|
| 7 | * The ATA transport class contains common code to deal with ATA HBAs, | 
|---|
| 8 | * an approximated representation of ATA topologies in the driver model, | 
|---|
| 9 | * and various sysfs attributes to expose these topologies and management | 
|---|
| 10 | * interfaces to user-space. | 
|---|
| 11 | * | 
|---|
| 12 | * There are 3 objects defined in this class: | 
|---|
| 13 | * - ata_port | 
|---|
| 14 | * - ata_link | 
|---|
| 15 | * - ata_device | 
|---|
| 16 | * Each port has a link object. Each link can have up to two devices for PATA | 
|---|
| 17 | * and generally one for SATA. | 
|---|
| 18 | * If there is SATA port multiplier [PMP], 15 additional ata_link object are | 
|---|
| 19 | * created. | 
|---|
| 20 | * | 
|---|
| 21 | * These objects are created when the ata host is initialized and when a PMP is | 
|---|
| 22 | * found. They are removed only when the HBA is removed, cleaned before the | 
|---|
| 23 | * error handler runs. | 
|---|
| 24 | */ | 
|---|
| 25 |  | 
|---|
| 26 |  | 
|---|
| 27 | #include <linux/kernel.h> | 
|---|
| 28 | #include <linux/blkdev.h> | 
|---|
| 29 | #include <linux/spinlock.h> | 
|---|
| 30 | #include <linux/slab.h> | 
|---|
| 31 | #include <scsi/scsi_transport.h> | 
|---|
| 32 | #include <linux/libata.h> | 
|---|
| 33 | #include <linux/hdreg.h> | 
|---|
| 34 | #include <linux/uaccess.h> | 
|---|
| 35 | #include <linux/pm_runtime.h> | 
|---|
| 36 |  | 
|---|
| 37 | #include "libata.h" | 
|---|
| 38 | #include "libata-transport.h" | 
|---|
| 39 |  | 
|---|
| 40 | #define ATA_PORT_ATTRS		3 | 
|---|
| 41 | #define ATA_LINK_ATTRS		3 | 
|---|
| 42 | #define ATA_DEV_ATTRS		9 | 
|---|
| 43 |  | 
|---|
| 44 | struct scsi_transport_template; | 
|---|
| 45 | struct scsi_transport_template *ata_scsi_transport_template; | 
|---|
| 46 |  | 
|---|
| 47 | struct ata_internal { | 
|---|
| 48 | struct scsi_transport_template t; | 
|---|
| 49 |  | 
|---|
| 50 | struct device_attribute private_port_attrs[ATA_PORT_ATTRS]; | 
|---|
| 51 | struct device_attribute private_link_attrs[ATA_LINK_ATTRS]; | 
|---|
| 52 | struct device_attribute private_dev_attrs[ATA_DEV_ATTRS]; | 
|---|
| 53 |  | 
|---|
| 54 | struct transport_container link_attr_cont; | 
|---|
| 55 | struct transport_container dev_attr_cont; | 
|---|
| 56 |  | 
|---|
| 57 | /* | 
|---|
| 58 | * The array of null terminated pointers to attributes | 
|---|
| 59 | * needed by scsi_sysfs.c | 
|---|
| 60 | */ | 
|---|
| 61 | struct device_attribute *link_attrs[ATA_LINK_ATTRS + 1]; | 
|---|
| 62 | struct device_attribute *port_attrs[ATA_PORT_ATTRS + 1]; | 
|---|
| 63 | struct device_attribute *dev_attrs[ATA_DEV_ATTRS + 1]; | 
|---|
| 64 | }; | 
|---|
| 65 | #define to_ata_internal(tmpl)	container_of(tmpl, struct ata_internal, t) | 
|---|
| 66 |  | 
|---|
| 67 |  | 
|---|
| 68 | #define tdev_to_device(d)					\ | 
|---|
| 69 | container_of((d), struct ata_device, tdev) | 
|---|
| 70 | #define transport_class_to_dev(dev)				\ | 
|---|
| 71 | tdev_to_device((dev)->parent) | 
|---|
| 72 |  | 
|---|
| 73 | #define tdev_to_link(d)						\ | 
|---|
| 74 | container_of((d), struct ata_link, tdev) | 
|---|
| 75 | #define transport_class_to_link(dev)				\ | 
|---|
| 76 | tdev_to_link((dev)->parent) | 
|---|
| 77 |  | 
|---|
| 78 | #define tdev_to_port(d)						\ | 
|---|
| 79 | container_of((d), struct ata_port, tdev) | 
|---|
| 80 | #define transport_class_to_port(dev)				\ | 
|---|
| 81 | tdev_to_port((dev)->parent) | 
|---|
| 82 |  | 
|---|
| 83 | /* | 
|---|
| 84 | * Hack to allow attributes of the same name in different objects. | 
|---|
| 85 | */ | 
|---|
| 86 | #define ATA_DEVICE_ATTR(_prefix,_name,_mode,_show,_store) \ | 
|---|
| 87 | struct device_attribute device_attr_##_prefix##_##_name = \ | 
|---|
| 88 | __ATTR(_name,_mode,_show,_store) | 
|---|
| 89 |  | 
|---|
| 90 | #define ata_bitfield_name_match(title, table)			\ | 
|---|
| 91 | static ssize_t							\ | 
|---|
| 92 | get_ata_##title##_names(u32 table_key, char *buf)		\ | 
|---|
| 93 | {								\ | 
|---|
| 94 | char *prefix = "";					\ | 
|---|
| 95 | ssize_t len = 0;					\ | 
|---|
| 96 | int i;							\ | 
|---|
| 97 | \ | 
|---|
| 98 | for (i = 0; i < ARRAY_SIZE(table); i++) {		\ | 
|---|
| 99 | if (table[i].value & table_key) {		\ | 
|---|
| 100 | len += sprintf(buf + len, "%s%s",	\ | 
|---|
| 101 | prefix, table[i].name);		\ | 
|---|
| 102 | prefix = ", ";				\ | 
|---|
| 103 | }						\ | 
|---|
| 104 | }							\ | 
|---|
| 105 | len += sprintf(buf + len, "\n");			\ | 
|---|
| 106 | return len;						\ | 
|---|
| 107 | } | 
|---|
| 108 |  | 
|---|
| 109 | #define ata_bitfield_name_search(title, table)			\ | 
|---|
| 110 | static ssize_t							\ | 
|---|
| 111 | get_ata_##title##_names(u32 table_key, char *buf)		\ | 
|---|
| 112 | {								\ | 
|---|
| 113 | ssize_t len = 0;					\ | 
|---|
| 114 | int i;							\ | 
|---|
| 115 | \ | 
|---|
| 116 | for (i = 0; i < ARRAY_SIZE(table); i++) {		\ | 
|---|
| 117 | if (table[i].value == table_key) {		\ | 
|---|
| 118 | len += sprintf(buf + len, "%s",		\ | 
|---|
| 119 | table[i].name);			\ | 
|---|
| 120 | break;					\ | 
|---|
| 121 | }						\ | 
|---|
| 122 | }							\ | 
|---|
| 123 | len += sprintf(buf + len, "\n");			\ | 
|---|
| 124 | return len;						\ | 
|---|
| 125 | } | 
|---|
| 126 |  | 
|---|
| 127 | static struct { | 
|---|
| 128 | u32		value; | 
|---|
| 129 | char		*name; | 
|---|
| 130 | } ata_class_names[] = { | 
|---|
| 131 | { ATA_DEV_UNKNOWN, "unknown"}, | 
|---|
| 132 | { ATA_DEV_ATA, "ata"}, | 
|---|
| 133 | { ATA_DEV_ATA_UNSUP, "ata"}, | 
|---|
| 134 | { ATA_DEV_ATAPI, "atapi"}, | 
|---|
| 135 | { ATA_DEV_ATAPI_UNSUP, "atapi"}, | 
|---|
| 136 | { ATA_DEV_PMP, "pmp"}, | 
|---|
| 137 | { ATA_DEV_PMP_UNSUP, "pmp"}, | 
|---|
| 138 | { ATA_DEV_SEMB, "semb"}, | 
|---|
| 139 | { ATA_DEV_SEMB_UNSUP, "semb"}, | 
|---|
| 140 | { ATA_DEV_ZAC, "zac"}, | 
|---|
| 141 | { ATA_DEV_NONE, "none"} | 
|---|
| 142 | }; | 
|---|
| 143 | ata_bitfield_name_search(class, ata_class_names) | 
|---|
| 144 |  | 
|---|
| 145 |  | 
|---|
| 146 | static struct { | 
|---|
| 147 | u32		value; | 
|---|
| 148 | char		*name; | 
|---|
| 149 | } ata_err_names[] = { | 
|---|
| 150 | { AC_ERR_DEV, "DeviceError"}, | 
|---|
| 151 | { AC_ERR_HSM, "HostStateMachineError"}, | 
|---|
| 152 | { AC_ERR_TIMEOUT, "Timeout"}, | 
|---|
| 153 | { AC_ERR_MEDIA, "MediaError"}, | 
|---|
| 154 | { AC_ERR_ATA_BUS, "BusError"}, | 
|---|
| 155 | { AC_ERR_HOST_BUS, "HostBusError"}, | 
|---|
| 156 | { AC_ERR_SYSTEM, "SystemError"}, | 
|---|
| 157 | { AC_ERR_INVALID, "InvalidArg"}, | 
|---|
| 158 | { AC_ERR_OTHER, "Unknown"}, | 
|---|
| 159 | { AC_ERR_NODEV_HINT, "NoDeviceHint"}, | 
|---|
| 160 | { AC_ERR_NCQ, "NCQError"} | 
|---|
| 161 | }; | 
|---|
| 162 | ata_bitfield_name_match(err, ata_err_names) | 
|---|
| 163 |  | 
|---|
| 164 | static struct { | 
|---|
| 165 | u32		value; | 
|---|
| 166 | char		*name; | 
|---|
| 167 | } ata_xfer_names[] = { | 
|---|
| 168 | { XFER_UDMA_7, "XFER_UDMA_7"}, | 
|---|
| 169 | { XFER_UDMA_6, "XFER_UDMA_6"}, | 
|---|
| 170 | { XFER_UDMA_5, "XFER_UDMA_5"}, | 
|---|
| 171 | { XFER_UDMA_4, "XFER_UDMA_4"}, | 
|---|
| 172 | { XFER_UDMA_3, "XFER_UDMA_3"}, | 
|---|
| 173 | { XFER_UDMA_2, "XFER_UDMA_2"}, | 
|---|
| 174 | { XFER_UDMA_1, "XFER_UDMA_1"}, | 
|---|
| 175 | { XFER_UDMA_0, "XFER_UDMA_0"}, | 
|---|
| 176 | { XFER_MW_DMA_4, "XFER_MW_DMA_4"}, | 
|---|
| 177 | { XFER_MW_DMA_3, "XFER_MW_DMA_3"}, | 
|---|
| 178 | { XFER_MW_DMA_2, "XFER_MW_DMA_2"}, | 
|---|
| 179 | { XFER_MW_DMA_1, "XFER_MW_DMA_1"}, | 
|---|
| 180 | { XFER_MW_DMA_0, "XFER_MW_DMA_0"}, | 
|---|
| 181 | { XFER_SW_DMA_2, "XFER_SW_DMA_2"}, | 
|---|
| 182 | { XFER_SW_DMA_1, "XFER_SW_DMA_1"}, | 
|---|
| 183 | { XFER_SW_DMA_0, "XFER_SW_DMA_0"}, | 
|---|
| 184 | { XFER_PIO_6, "XFER_PIO_6"}, | 
|---|
| 185 | { XFER_PIO_5, "XFER_PIO_5"}, | 
|---|
| 186 | { XFER_PIO_4, "XFER_PIO_4"}, | 
|---|
| 187 | { XFER_PIO_3, "XFER_PIO_3"}, | 
|---|
| 188 | { XFER_PIO_2, "XFER_PIO_2"}, | 
|---|
| 189 | { XFER_PIO_1, "XFER_PIO_1"}, | 
|---|
| 190 | { XFER_PIO_0, "XFER_PIO_0"}, | 
|---|
| 191 | { XFER_PIO_SLOW, "XFER_PIO_SLOW"} | 
|---|
| 192 | }; | 
|---|
| 193 | ata_bitfield_name_search(xfer, ata_xfer_names) | 
|---|
| 194 |  | 
|---|
| 195 | /* | 
|---|
| 196 | * ATA Port attributes | 
|---|
| 197 | */ | 
|---|
| 198 | #define ata_port_show_simple(field, name, format_string, cast)		\ | 
|---|
| 199 | static ssize_t								\ | 
|---|
| 200 | show_ata_port_##name(struct device *dev,				\ | 
|---|
| 201 | struct device_attribute *attr, char *buf)		\ | 
|---|
| 202 | {									\ | 
|---|
| 203 | struct ata_port *ap = transport_class_to_port(dev);		\ | 
|---|
| 204 | \ | 
|---|
| 205 | return sysfs_emit(buf, format_string, cast ap->field);	        \ | 
|---|
| 206 | } | 
|---|
| 207 |  | 
|---|
| 208 | #define ata_port_simple_attr(field, name, format_string, type)		\ | 
|---|
| 209 | ata_port_show_simple(field, name, format_string, (type))	\ | 
|---|
| 210 | static DEVICE_ATTR(name, S_IRUGO, show_ata_port_##name, NULL) | 
|---|
| 211 |  | 
|---|
| 212 | ata_port_simple_attr(nr_pmp_links, nr_pmp_links, "%d\n", int); | 
|---|
| 213 | ata_port_simple_attr(stats.idle_irq, idle_irq, "%ld\n", unsigned long); | 
|---|
| 214 | /* We want the port_no sysfs attibute to start at 1 (ap->port_no starts at 0) */ | 
|---|
| 215 | ata_port_simple_attr(port_no + 1, port_no, "%u\n", unsigned int); | 
|---|
| 216 |  | 
|---|
| 217 | static DECLARE_TRANSPORT_CLASS(ata_port_class, | 
|---|
| 218 | "ata_port", NULL, NULL, NULL); | 
|---|
| 219 |  | 
|---|
| 220 | static void ata_tport_release(struct device *dev) | 
|---|
| 221 | { | 
|---|
| 222 | struct ata_port *ap = tdev_to_port(dev); | 
|---|
| 223 | ata_host_put(host: ap->host); | 
|---|
| 224 | } | 
|---|
| 225 |  | 
|---|
| 226 | /** | 
|---|
| 227 | * ata_is_port --  check if a struct device represents a ATA port | 
|---|
| 228 | * @dev:	device to check | 
|---|
| 229 | * | 
|---|
| 230 | * Returns: | 
|---|
| 231 | *	%1 if the device represents a ATA Port, %0 else | 
|---|
| 232 | */ | 
|---|
| 233 | static int ata_is_port(const struct device *dev) | 
|---|
| 234 | { | 
|---|
| 235 | return dev->release == ata_tport_release; | 
|---|
| 236 | } | 
|---|
| 237 |  | 
|---|
| 238 | static int ata_tport_match(struct attribute_container *cont, | 
|---|
| 239 | struct device *dev) | 
|---|
| 240 | { | 
|---|
| 241 | if (!ata_is_port(dev)) | 
|---|
| 242 | return 0; | 
|---|
| 243 | return &ata_scsi_transport_template->host_attrs.ac == cont; | 
|---|
| 244 | } | 
|---|
| 245 |  | 
|---|
| 246 | /** | 
|---|
| 247 | * ata_tport_delete  --  remove ATA PORT | 
|---|
| 248 | * @ap:	ATA PORT to remove | 
|---|
| 249 | * | 
|---|
| 250 | * Removes the specified ATA PORT.  Remove the associated link as well. | 
|---|
| 251 | */ | 
|---|
| 252 | void ata_tport_delete(struct ata_port *ap) | 
|---|
| 253 | { | 
|---|
| 254 | struct device *dev = &ap->tdev; | 
|---|
| 255 |  | 
|---|
| 256 | ata_tlink_delete(link: &ap->link); | 
|---|
| 257 |  | 
|---|
| 258 | transport_remove_device(dev); | 
|---|
| 259 | device_del(dev); | 
|---|
| 260 | transport_destroy_device(dev); | 
|---|
| 261 | put_device(dev); | 
|---|
| 262 | } | 
|---|
| 263 | EXPORT_SYMBOL_GPL(ata_tport_delete); | 
|---|
| 264 |  | 
|---|
| 265 | static const struct device_type ata_port_sas_type = { | 
|---|
| 266 | .name = ATA_PORT_TYPE_NAME, | 
|---|
| 267 | }; | 
|---|
| 268 |  | 
|---|
| 269 | /** ata_tport_add - initialize a transport ATA port structure | 
|---|
| 270 | * | 
|---|
| 271 | * @parent:	parent device | 
|---|
| 272 | * @ap:		existing ata_port structure | 
|---|
| 273 | * | 
|---|
| 274 | * Initialize a ATA port structure for sysfs.  It will be added to the device | 
|---|
| 275 | * tree below the device specified by @parent which could be a PCI device. | 
|---|
| 276 | * | 
|---|
| 277 | * Returns %0 on success | 
|---|
| 278 | */ | 
|---|
| 279 | int ata_tport_add(struct device *parent, | 
|---|
| 280 | struct ata_port *ap) | 
|---|
| 281 | { | 
|---|
| 282 | int error; | 
|---|
| 283 | struct device *dev = &ap->tdev; | 
|---|
| 284 |  | 
|---|
| 285 | device_initialize(dev); | 
|---|
| 286 | if (ap->flags & ATA_FLAG_SAS_HOST) | 
|---|
| 287 | dev->type = &ata_port_sas_type; | 
|---|
| 288 | else | 
|---|
| 289 | dev->type = &ata_port_type; | 
|---|
| 290 |  | 
|---|
| 291 | dev->parent = parent; | 
|---|
| 292 | ata_host_get(host: ap->host); | 
|---|
| 293 | dev->release = ata_tport_release; | 
|---|
| 294 | dev_set_name(dev, name: "ata%d", ap->print_id); | 
|---|
| 295 | transport_setup_device(dev); | 
|---|
| 296 | ata_acpi_bind_port(ap); | 
|---|
| 297 | error = device_add(dev); | 
|---|
| 298 | if (error) { | 
|---|
| 299 | goto tport_err; | 
|---|
| 300 | } | 
|---|
| 301 |  | 
|---|
| 302 | device_enable_async_suspend(dev); | 
|---|
| 303 | pm_runtime_set_active(dev); | 
|---|
| 304 | pm_runtime_enable(dev); | 
|---|
| 305 | pm_runtime_forbid(dev); | 
|---|
| 306 |  | 
|---|
| 307 | error = transport_add_device(dev); | 
|---|
| 308 | if (error) | 
|---|
| 309 | goto tport_transport_add_err; | 
|---|
| 310 | transport_configure_device(dev); | 
|---|
| 311 |  | 
|---|
| 312 | error = ata_tlink_add(link: &ap->link); | 
|---|
| 313 | if (error) { | 
|---|
| 314 | goto tport_link_err; | 
|---|
| 315 | } | 
|---|
| 316 | return 0; | 
|---|
| 317 |  | 
|---|
| 318 | tport_link_err: | 
|---|
| 319 | transport_remove_device(dev); | 
|---|
| 320 | tport_transport_add_err: | 
|---|
| 321 | device_del(dev); | 
|---|
| 322 |  | 
|---|
| 323 | tport_err: | 
|---|
| 324 | transport_destroy_device(dev); | 
|---|
| 325 | put_device(dev); | 
|---|
| 326 | return error; | 
|---|
| 327 | } | 
|---|
| 328 | EXPORT_SYMBOL_GPL(ata_tport_add); | 
|---|
| 329 |  | 
|---|
| 330 | /** | 
|---|
| 331 | *     ata_port_classify - determine device type based on ATA-spec signature | 
|---|
| 332 | *     @ap: ATA port device on which the classification should be run | 
|---|
| 333 | *     @tf: ATA taskfile register set for device to be identified | 
|---|
| 334 | * | 
|---|
| 335 | *     A wrapper around ata_dev_classify() to provide additional logging | 
|---|
| 336 | * | 
|---|
| 337 | *     RETURNS: | 
|---|
| 338 | *     Device type, %ATA_DEV_ATA, %ATA_DEV_ATAPI, %ATA_DEV_PMP, | 
|---|
| 339 | *     %ATA_DEV_ZAC, or %ATA_DEV_UNKNOWN the event of failure. | 
|---|
| 340 | */ | 
|---|
| 341 | unsigned int ata_port_classify(struct ata_port *ap, | 
|---|
| 342 | const struct ata_taskfile *tf) | 
|---|
| 343 | { | 
|---|
| 344 | int i; | 
|---|
| 345 | unsigned int class = ata_dev_classify(tf); | 
|---|
| 346 |  | 
|---|
| 347 | /* Start with index '1' to skip the 'unknown' entry */ | 
|---|
| 348 | for (i = 1; i < ARRAY_SIZE(ata_class_names); i++) { | 
|---|
| 349 | if (ata_class_names[i].value == class) { | 
|---|
| 350 | ata_port_dbg(ap, "found %s device by sig\n", | 
|---|
| 351 | ata_class_names[i].name); | 
|---|
| 352 | return class; | 
|---|
| 353 | } | 
|---|
| 354 | } | 
|---|
| 355 |  | 
|---|
| 356 | ata_port_info(ap, "found unknown device (class %u)\n", class); | 
|---|
| 357 | return class; | 
|---|
| 358 | } | 
|---|
| 359 | EXPORT_SYMBOL_GPL(ata_port_classify); | 
|---|
| 360 |  | 
|---|
| 361 | /* | 
|---|
| 362 | * ATA device attributes | 
|---|
| 363 | */ | 
|---|
| 364 |  | 
|---|
| 365 | #define ata_dev_show_class(title, field)				\ | 
|---|
| 366 | static ssize_t								\ | 
|---|
| 367 | show_ata_dev_##field(struct device *dev,				\ | 
|---|
| 368 | struct device_attribute *attr, char *buf)		\ | 
|---|
| 369 | {									\ | 
|---|
| 370 | struct ata_device *ata_dev = transport_class_to_dev(dev);	\ | 
|---|
| 371 | \ | 
|---|
| 372 | return get_ata_##title##_names(ata_dev->field, buf);		\ | 
|---|
| 373 | } | 
|---|
| 374 |  | 
|---|
| 375 | #define ata_dev_attr(title, field)					\ | 
|---|
| 376 | ata_dev_show_class(title, field)				\ | 
|---|
| 377 | static DEVICE_ATTR(field, S_IRUGO, show_ata_dev_##field, NULL) | 
|---|
| 378 |  | 
|---|
| 379 | ata_dev_attr(class, class); | 
|---|
| 380 | ata_dev_attr(xfer, pio_mode); | 
|---|
| 381 | ata_dev_attr(xfer, dma_mode); | 
|---|
| 382 | ata_dev_attr(xfer, xfer_mode); | 
|---|
| 383 |  | 
|---|
| 384 |  | 
|---|
| 385 | #define ata_dev_show_simple(field, format_string, cast)			\ | 
|---|
| 386 | static ssize_t								\ | 
|---|
| 387 | show_ata_dev_##field(struct device *dev,				\ | 
|---|
| 388 | struct device_attribute *attr, char *buf)		\ | 
|---|
| 389 | {									\ | 
|---|
| 390 | struct ata_device *ata_dev = transport_class_to_dev(dev);	\ | 
|---|
| 391 | \ | 
|---|
| 392 | return sysfs_emit(buf, format_string, cast ata_dev->field);	\ | 
|---|
| 393 | } | 
|---|
| 394 |  | 
|---|
| 395 | #define ata_dev_simple_attr(field, format_string, type)		\ | 
|---|
| 396 | ata_dev_show_simple(field, format_string, (type))	\ | 
|---|
| 397 | static DEVICE_ATTR(field, S_IRUGO,			\ | 
|---|
| 398 | show_ata_dev_##field, NULL) | 
|---|
| 399 |  | 
|---|
| 400 | ata_dev_simple_attr(spdn_cnt, "%d\n", int); | 
|---|
| 401 |  | 
|---|
| 402 | struct ata_show_ering_arg { | 
|---|
| 403 | char* buf; | 
|---|
| 404 | int written; | 
|---|
| 405 | }; | 
|---|
| 406 |  | 
|---|
| 407 | static int ata_show_ering(struct ata_ering_entry *ent, void *void_arg) | 
|---|
| 408 | { | 
|---|
| 409 | struct ata_show_ering_arg* arg = void_arg; | 
|---|
| 410 | u64 seconds; | 
|---|
| 411 | u32 rem; | 
|---|
| 412 |  | 
|---|
| 413 | seconds = div_u64_rem(dividend: ent->timestamp, HZ, remainder: &rem); | 
|---|
| 414 | arg->written += sprintf(buf: arg->buf + arg->written, | 
|---|
| 415 | fmt: "[%5llu.%09lu]", seconds, | 
|---|
| 416 | rem * NSEC_PER_SEC / HZ); | 
|---|
| 417 | arg->written += get_ata_err_names(table_key: ent->err_mask, | 
|---|
| 418 | buf: arg->buf + arg->written); | 
|---|
| 419 | return 0; | 
|---|
| 420 | } | 
|---|
| 421 |  | 
|---|
| 422 | static ssize_t | 
|---|
| 423 | show_ata_dev_ering(struct device *dev, | 
|---|
| 424 | struct device_attribute *attr, char *buf) | 
|---|
| 425 | { | 
|---|
| 426 | struct ata_device *ata_dev = transport_class_to_dev(dev); | 
|---|
| 427 | struct ata_show_ering_arg arg = { buf, 0 }; | 
|---|
| 428 |  | 
|---|
| 429 | ata_ering_map(ering: &ata_dev->ering, map_fn: ata_show_ering, arg: &arg); | 
|---|
| 430 | return arg.written; | 
|---|
| 431 | } | 
|---|
| 432 |  | 
|---|
| 433 |  | 
|---|
| 434 | static DEVICE_ATTR(ering, S_IRUGO, show_ata_dev_ering, NULL); | 
|---|
| 435 |  | 
|---|
| 436 | static ssize_t | 
|---|
| 437 | show_ata_dev_id(struct device *dev, | 
|---|
| 438 | struct device_attribute *attr, char *buf) | 
|---|
| 439 | { | 
|---|
| 440 | struct ata_device *ata_dev = transport_class_to_dev(dev); | 
|---|
| 441 | int written = 0, i = 0; | 
|---|
| 442 |  | 
|---|
| 443 | if (ata_dev->class == ATA_DEV_PMP) | 
|---|
| 444 | return 0; | 
|---|
| 445 | for(i=0;i<ATA_ID_WORDS;i++)  { | 
|---|
| 446 | written += scnprintf(buf: buf+written, size: 20, fmt: "%04x%c", | 
|---|
| 447 | ata_dev->id[i], | 
|---|
| 448 | ((i+1) & 7) ? ' ' : '\n'); | 
|---|
| 449 | } | 
|---|
| 450 | return written; | 
|---|
| 451 | } | 
|---|
| 452 |  | 
|---|
| 453 | static DEVICE_ATTR(id, S_IRUGO, show_ata_dev_id, NULL); | 
|---|
| 454 |  | 
|---|
| 455 | static ssize_t | 
|---|
| 456 | show_ata_dev_gscr(struct device *dev, | 
|---|
| 457 | struct device_attribute *attr, char *buf) | 
|---|
| 458 | { | 
|---|
| 459 | struct ata_device *ata_dev = transport_class_to_dev(dev); | 
|---|
| 460 | int written = 0, i = 0; | 
|---|
| 461 |  | 
|---|
| 462 | if (ata_dev->class != ATA_DEV_PMP) | 
|---|
| 463 | return 0; | 
|---|
| 464 | for(i=0;i<SATA_PMP_GSCR_DWORDS;i++)  { | 
|---|
| 465 | written += scnprintf(buf: buf+written, size: 20, fmt: "%08x%c", | 
|---|
| 466 | ata_dev->gscr[i], | 
|---|
| 467 | ((i+1) & 3) ? ' ' : '\n'); | 
|---|
| 468 | } | 
|---|
| 469 | if (SATA_PMP_GSCR_DWORDS & 3) | 
|---|
| 470 | buf[written-1] = '\n'; | 
|---|
| 471 | return written; | 
|---|
| 472 | } | 
|---|
| 473 |  | 
|---|
| 474 | static DEVICE_ATTR(gscr, S_IRUGO, show_ata_dev_gscr, NULL); | 
|---|
| 475 |  | 
|---|
| 476 | static ssize_t | 
|---|
| 477 | show_ata_dev_trim(struct device *dev, | 
|---|
| 478 | struct device_attribute *attr, char *buf) | 
|---|
| 479 | { | 
|---|
| 480 | struct ata_device *ata_dev = transport_class_to_dev(dev); | 
|---|
| 481 | unsigned char *mode; | 
|---|
| 482 |  | 
|---|
| 483 | if (!ata_id_has_trim(id: ata_dev->id)) | 
|---|
| 484 | mode = "unsupported"; | 
|---|
| 485 | else if (ata_dev->quirks & ATA_QUIRK_NOTRIM) | 
|---|
| 486 | mode = "forced_unsupported"; | 
|---|
| 487 | else if (ata_dev->quirks & ATA_QUIRK_NO_NCQ_TRIM) | 
|---|
| 488 | mode = "forced_unqueued"; | 
|---|
| 489 | else if (ata_fpdma_dsm_supported(dev: ata_dev)) | 
|---|
| 490 | mode = "queued"; | 
|---|
| 491 | else | 
|---|
| 492 | mode = "unqueued"; | 
|---|
| 493 |  | 
|---|
| 494 | return scnprintf(buf, size: 20, fmt: "%s\n", mode); | 
|---|
| 495 | } | 
|---|
| 496 |  | 
|---|
| 497 | static DEVICE_ATTR(trim, S_IRUGO, show_ata_dev_trim, NULL); | 
|---|
| 498 |  | 
|---|
| 499 | static DECLARE_TRANSPORT_CLASS(ata_dev_class, | 
|---|
| 500 | "ata_device", NULL, NULL, NULL); | 
|---|
| 501 |  | 
|---|
| 502 | static void ata_tdev_release(struct device *dev) | 
|---|
| 503 | { | 
|---|
| 504 | } | 
|---|
| 505 |  | 
|---|
| 506 | /** | 
|---|
| 507 | * ata_is_ata_dev  --  check if a struct device represents a ATA device | 
|---|
| 508 | * @dev:	device to check | 
|---|
| 509 | * | 
|---|
| 510 | * Returns: | 
|---|
| 511 | *	true if the device represents a ATA device, false otherwise | 
|---|
| 512 | */ | 
|---|
| 513 | static bool ata_is_ata_dev(const struct device *dev) | 
|---|
| 514 | { | 
|---|
| 515 | return dev->release == ata_tdev_release; | 
|---|
| 516 | } | 
|---|
| 517 |  | 
|---|
| 518 | static int ata_tdev_match(struct attribute_container *cont, | 
|---|
| 519 | struct device *dev) | 
|---|
| 520 | { | 
|---|
| 521 | struct ata_internal *i = to_ata_internal(ata_scsi_transport_template); | 
|---|
| 522 |  | 
|---|
| 523 | if (!ata_is_ata_dev(dev)) | 
|---|
| 524 | return 0; | 
|---|
| 525 | return &i->dev_attr_cont.ac == cont; | 
|---|
| 526 | } | 
|---|
| 527 |  | 
|---|
| 528 | /** | 
|---|
| 529 | * ata_tdev_free  --  free an ATA transport device | 
|---|
| 530 | * @dev:	struct ata_device owning the transport device to free | 
|---|
| 531 | * | 
|---|
| 532 | * Free the ATA transport device for the specified ATA device. | 
|---|
| 533 | * | 
|---|
| 534 | * Note: | 
|---|
| 535 | *   This function must only be called for a ATA transport device that has not | 
|---|
| 536 | *   yet successfully been added using ata_tdev_add(). | 
|---|
| 537 | */ | 
|---|
| 538 | static void ata_tdev_free(struct ata_device *dev) | 
|---|
| 539 | { | 
|---|
| 540 | transport_destroy_device(&dev->tdev); | 
|---|
| 541 | put_device(dev: &dev->tdev); | 
|---|
| 542 | } | 
|---|
| 543 |  | 
|---|
| 544 | /** | 
|---|
| 545 | * ata_tdev_delete  --  remove an ATA transport device | 
|---|
| 546 | * @ata_dev:	struct ata_device owning the transport device to delete | 
|---|
| 547 | * | 
|---|
| 548 | * Removes the ATA transport device for the specified ATA device. | 
|---|
| 549 | */ | 
|---|
| 550 | static void ata_tdev_delete(struct ata_device *ata_dev) | 
|---|
| 551 | { | 
|---|
| 552 | struct device *dev = &ata_dev->tdev; | 
|---|
| 553 |  | 
|---|
| 554 | transport_remove_device(dev); | 
|---|
| 555 | device_del(dev); | 
|---|
| 556 | ata_tdev_free(dev: ata_dev); | 
|---|
| 557 | } | 
|---|
| 558 |  | 
|---|
| 559 | /** | 
|---|
| 560 | * ata_tdev_add  --  initialize an ATA transport device | 
|---|
| 561 | * @ata_dev:	struct ata_device owning the transport device to add | 
|---|
| 562 | * | 
|---|
| 563 | * Initialize an ATA transport device for sysfs.  It will be added in the | 
|---|
| 564 | * device tree below the ATA link device it belongs to. | 
|---|
| 565 | * | 
|---|
| 566 | * Returns %0 on success and a negative error code on error. | 
|---|
| 567 | */ | 
|---|
| 568 | static int ata_tdev_add(struct ata_device *ata_dev) | 
|---|
| 569 | { | 
|---|
| 570 | struct device *dev = &ata_dev->tdev; | 
|---|
| 571 | struct ata_link *link = ata_dev->link; | 
|---|
| 572 | struct ata_port *ap = link->ap; | 
|---|
| 573 | int error; | 
|---|
| 574 |  | 
|---|
| 575 | device_initialize(dev); | 
|---|
| 576 | dev->parent = &link->tdev; | 
|---|
| 577 | dev->release = ata_tdev_release; | 
|---|
| 578 | if (ata_is_host_link(link)) | 
|---|
| 579 | dev_set_name(dev, name: "dev%d.%d", ap->print_id,ata_dev->devno); | 
|---|
| 580 | else | 
|---|
| 581 | dev_set_name(dev, name: "dev%d.%d.0", ap->print_id, link->pmp); | 
|---|
| 582 |  | 
|---|
| 583 | transport_setup_device(dev); | 
|---|
| 584 | ata_acpi_bind_dev(dev: ata_dev); | 
|---|
| 585 | error = device_add(dev); | 
|---|
| 586 | if (error) { | 
|---|
| 587 | ata_tdev_free(dev: ata_dev); | 
|---|
| 588 | return error; | 
|---|
| 589 | } | 
|---|
| 590 |  | 
|---|
| 591 | error = transport_add_device(dev); | 
|---|
| 592 | if (error) { | 
|---|
| 593 | device_del(dev); | 
|---|
| 594 | ata_tdev_free(dev: ata_dev); | 
|---|
| 595 | return error; | 
|---|
| 596 | } | 
|---|
| 597 |  | 
|---|
| 598 | transport_configure_device(dev); | 
|---|
| 599 | return 0; | 
|---|
| 600 | } | 
|---|
| 601 |  | 
|---|
| 602 | /* | 
|---|
| 603 | * ATA link attributes | 
|---|
| 604 | */ | 
|---|
| 605 | static int noop(int x) | 
|---|
| 606 | { | 
|---|
| 607 | return x; | 
|---|
| 608 | } | 
|---|
| 609 |  | 
|---|
| 610 | #define ata_link_show_linkspeed(field, format)			\ | 
|---|
| 611 | static ssize_t							\ | 
|---|
| 612 | show_ata_link_##field(struct device *dev,			\ | 
|---|
| 613 | struct device_attribute *attr, char *buf)	\ | 
|---|
| 614 | {								\ | 
|---|
| 615 | struct ata_link *link = transport_class_to_link(dev);	\ | 
|---|
| 616 | \ | 
|---|
| 617 | return sprintf(buf, "%s\n",				\ | 
|---|
| 618 | sata_spd_string(format(link->field)));	\ | 
|---|
| 619 | } | 
|---|
| 620 |  | 
|---|
| 621 | #define ata_link_linkspeed_attr(field, format)			\ | 
|---|
| 622 | ata_link_show_linkspeed(field, format)			\ | 
|---|
| 623 | static DEVICE_ATTR(field, 0444, show_ata_link_##field, NULL) | 
|---|
| 624 |  | 
|---|
| 625 | ata_link_linkspeed_attr(hw_sata_spd_limit, fls); | 
|---|
| 626 | ata_link_linkspeed_attr(sata_spd_limit, fls); | 
|---|
| 627 | ata_link_linkspeed_attr(sata_spd, noop); | 
|---|
| 628 |  | 
|---|
| 629 | static DECLARE_TRANSPORT_CLASS(ata_link_class, | 
|---|
| 630 | "ata_link", NULL, NULL, NULL); | 
|---|
| 631 |  | 
|---|
| 632 | static void ata_tlink_release(struct device *dev) | 
|---|
| 633 | { | 
|---|
| 634 | } | 
|---|
| 635 |  | 
|---|
| 636 | /** | 
|---|
| 637 | * ata_is_link --  check if a struct device represents a ATA link | 
|---|
| 638 | * @dev:	device to check | 
|---|
| 639 | * | 
|---|
| 640 | * Returns: | 
|---|
| 641 | *	true if the device represents a ATA link, false otherwise | 
|---|
| 642 | */ | 
|---|
| 643 | static bool ata_is_link(const struct device *dev) | 
|---|
| 644 | { | 
|---|
| 645 | return dev->release == ata_tlink_release; | 
|---|
| 646 | } | 
|---|
| 647 |  | 
|---|
| 648 | static int ata_tlink_match(struct attribute_container *cont, | 
|---|
| 649 | struct device *dev) | 
|---|
| 650 | { | 
|---|
| 651 | struct ata_internal *i = to_ata_internal(ata_scsi_transport_template); | 
|---|
| 652 |  | 
|---|
| 653 | if (!ata_is_link(dev)) | 
|---|
| 654 | return 0; | 
|---|
| 655 | return &i->link_attr_cont.ac == cont; | 
|---|
| 656 | } | 
|---|
| 657 |  | 
|---|
| 658 | /** | 
|---|
| 659 | * ata_tlink_delete  --  remove an ATA link transport device | 
|---|
| 660 | * @link:	struct ata_link owning the link transport device to remove | 
|---|
| 661 | * | 
|---|
| 662 | * Removes the link transport device of the specified ATA link. This also | 
|---|
| 663 | * removes the ATA device(s) associated with the link as well. | 
|---|
| 664 | */ | 
|---|
| 665 | void ata_tlink_delete(struct ata_link *link) | 
|---|
| 666 | { | 
|---|
| 667 | struct device *dev = &link->tdev; | 
|---|
| 668 | struct ata_device *ata_dev; | 
|---|
| 669 |  | 
|---|
| 670 | ata_for_each_dev(ata_dev, link, ALL) { | 
|---|
| 671 | ata_tdev_delete(ata_dev); | 
|---|
| 672 | } | 
|---|
| 673 |  | 
|---|
| 674 | transport_remove_device(dev); | 
|---|
| 675 | device_del(dev); | 
|---|
| 676 | transport_destroy_device(dev); | 
|---|
| 677 | put_device(dev); | 
|---|
| 678 | } | 
|---|
| 679 |  | 
|---|
| 680 | /** | 
|---|
| 681 | * ata_tlink_add  --  initialize an ATA link transport device | 
|---|
| 682 | * @link:	struct ata_link owning the link transport device to initialize | 
|---|
| 683 | * | 
|---|
| 684 | * Initialize an ATA link transport device for sysfs. It will be added in the | 
|---|
| 685 | * device tree below the ATA port it belongs to. | 
|---|
| 686 | * | 
|---|
| 687 | * Returns %0 on success and a negative error code on error. | 
|---|
| 688 | */ | 
|---|
| 689 | int ata_tlink_add(struct ata_link *link) | 
|---|
| 690 | { | 
|---|
| 691 | struct device *dev = &link->tdev; | 
|---|
| 692 | struct ata_port *ap = link->ap; | 
|---|
| 693 | struct ata_device *ata_dev; | 
|---|
| 694 | int error; | 
|---|
| 695 |  | 
|---|
| 696 | device_initialize(dev); | 
|---|
| 697 | dev->parent = &ap->tdev; | 
|---|
| 698 | dev->release = ata_tlink_release; | 
|---|
| 699 | if (ata_is_host_link(link)) | 
|---|
| 700 | dev_set_name(dev, name: "link%d", ap->print_id); | 
|---|
| 701 | else | 
|---|
| 702 | dev_set_name(dev, name: "link%d.%d", ap->print_id, link->pmp); | 
|---|
| 703 |  | 
|---|
| 704 | transport_setup_device(dev); | 
|---|
| 705 |  | 
|---|
| 706 | error = device_add(dev); | 
|---|
| 707 | if (error) | 
|---|
| 708 | goto tlink_err; | 
|---|
| 709 |  | 
|---|
| 710 | error = transport_add_device(dev); | 
|---|
| 711 | if (error) | 
|---|
| 712 | goto tlink_transport_err; | 
|---|
| 713 | transport_configure_device(dev); | 
|---|
| 714 |  | 
|---|
| 715 | ata_for_each_dev(ata_dev, link, ALL) { | 
|---|
| 716 | error = ata_tdev_add(ata_dev); | 
|---|
| 717 | if (error) | 
|---|
| 718 | goto tlink_dev_err; | 
|---|
| 719 | } | 
|---|
| 720 | return 0; | 
|---|
| 721 | tlink_dev_err: | 
|---|
| 722 | while (--ata_dev >= link->device) | 
|---|
| 723 | ata_tdev_delete(ata_dev); | 
|---|
| 724 | transport_remove_device(dev); | 
|---|
| 725 | tlink_transport_err: | 
|---|
| 726 | device_del(dev); | 
|---|
| 727 | tlink_err: | 
|---|
| 728 | transport_destroy_device(dev); | 
|---|
| 729 | put_device(dev); | 
|---|
| 730 | return error; | 
|---|
| 731 | } | 
|---|
| 732 |  | 
|---|
| 733 | /* | 
|---|
| 734 | * Setup / Teardown code | 
|---|
| 735 | */ | 
|---|
| 736 |  | 
|---|
| 737 | #define SETUP_TEMPLATE(attrb, field, perm, test)			\ | 
|---|
| 738 | i->private_##attrb[count] = dev_attr_##field;			\ | 
|---|
| 739 | i->private_##attrb[count].attr.mode = perm;			\ | 
|---|
| 740 | i->attrb[count] = &i->private_##attrb[count];			\ | 
|---|
| 741 | if (test)							\ | 
|---|
| 742 | count++ | 
|---|
| 743 |  | 
|---|
| 744 | #define SETUP_LINK_ATTRIBUTE(field)					\ | 
|---|
| 745 | SETUP_TEMPLATE(link_attrs, field, S_IRUGO, 1) | 
|---|
| 746 |  | 
|---|
| 747 | #define SETUP_PORT_ATTRIBUTE(field)					\ | 
|---|
| 748 | SETUP_TEMPLATE(port_attrs, field, S_IRUGO, 1) | 
|---|
| 749 |  | 
|---|
| 750 | #define SETUP_DEV_ATTRIBUTE(field)					\ | 
|---|
| 751 | SETUP_TEMPLATE(dev_attrs, field, S_IRUGO, 1) | 
|---|
| 752 |  | 
|---|
| 753 | /** | 
|---|
| 754 | * ata_attach_transport  --  instantiate ATA transport template | 
|---|
| 755 | */ | 
|---|
| 756 | struct scsi_transport_template *ata_attach_transport(void) | 
|---|
| 757 | { | 
|---|
| 758 | struct ata_internal *i; | 
|---|
| 759 | int count; | 
|---|
| 760 |  | 
|---|
| 761 | i = kzalloc(sizeof(struct ata_internal), GFP_KERNEL); | 
|---|
| 762 | if (!i) | 
|---|
| 763 | return NULL; | 
|---|
| 764 |  | 
|---|
| 765 | i->t.eh_strategy_handler	= ata_scsi_error; | 
|---|
| 766 | i->t.user_scan			= ata_scsi_user_scan; | 
|---|
| 767 |  | 
|---|
| 768 | i->t.host_attrs.ac.attrs = &i->port_attrs[0]; | 
|---|
| 769 | i->t.host_attrs.ac.class = &ata_port_class.class; | 
|---|
| 770 | i->t.host_attrs.ac.match = ata_tport_match; | 
|---|
| 771 | transport_container_register(tc: &i->t.host_attrs); | 
|---|
| 772 |  | 
|---|
| 773 | i->link_attr_cont.ac.class = &ata_link_class.class; | 
|---|
| 774 | i->link_attr_cont.ac.attrs = &i->link_attrs[0]; | 
|---|
| 775 | i->link_attr_cont.ac.match = ata_tlink_match; | 
|---|
| 776 | transport_container_register(tc: &i->link_attr_cont); | 
|---|
| 777 |  | 
|---|
| 778 | i->dev_attr_cont.ac.class = &ata_dev_class.class; | 
|---|
| 779 | i->dev_attr_cont.ac.attrs = &i->dev_attrs[0]; | 
|---|
| 780 | i->dev_attr_cont.ac.match = ata_tdev_match; | 
|---|
| 781 | transport_container_register(tc: &i->dev_attr_cont); | 
|---|
| 782 |  | 
|---|
| 783 | count = 0; | 
|---|
| 784 | SETUP_PORT_ATTRIBUTE(nr_pmp_links); | 
|---|
| 785 | SETUP_PORT_ATTRIBUTE(idle_irq); | 
|---|
| 786 | SETUP_PORT_ATTRIBUTE(port_no); | 
|---|
| 787 | BUG_ON(count > ATA_PORT_ATTRS); | 
|---|
| 788 | i->port_attrs[count] = NULL; | 
|---|
| 789 |  | 
|---|
| 790 | count = 0; | 
|---|
| 791 | SETUP_LINK_ATTRIBUTE(hw_sata_spd_limit); | 
|---|
| 792 | SETUP_LINK_ATTRIBUTE(sata_spd_limit); | 
|---|
| 793 | SETUP_LINK_ATTRIBUTE(sata_spd); | 
|---|
| 794 | BUG_ON(count > ATA_LINK_ATTRS); | 
|---|
| 795 | i->link_attrs[count] = NULL; | 
|---|
| 796 |  | 
|---|
| 797 | count = 0; | 
|---|
| 798 | SETUP_DEV_ATTRIBUTE(class); | 
|---|
| 799 | SETUP_DEV_ATTRIBUTE(pio_mode); | 
|---|
| 800 | SETUP_DEV_ATTRIBUTE(dma_mode); | 
|---|
| 801 | SETUP_DEV_ATTRIBUTE(xfer_mode); | 
|---|
| 802 | SETUP_DEV_ATTRIBUTE(spdn_cnt); | 
|---|
| 803 | SETUP_DEV_ATTRIBUTE(ering); | 
|---|
| 804 | SETUP_DEV_ATTRIBUTE(id); | 
|---|
| 805 | SETUP_DEV_ATTRIBUTE(gscr); | 
|---|
| 806 | SETUP_DEV_ATTRIBUTE(trim); | 
|---|
| 807 | BUG_ON(count > ATA_DEV_ATTRS); | 
|---|
| 808 | i->dev_attrs[count] = NULL; | 
|---|
| 809 |  | 
|---|
| 810 | return &i->t; | 
|---|
| 811 | } | 
|---|
| 812 |  | 
|---|
| 813 | /** | 
|---|
| 814 | * ata_release_transport  --  release ATA transport template instance | 
|---|
| 815 | * @t:		transport template instance | 
|---|
| 816 | */ | 
|---|
| 817 | void ata_release_transport(struct scsi_transport_template *t) | 
|---|
| 818 | { | 
|---|
| 819 | struct ata_internal *i = to_ata_internal(t); | 
|---|
| 820 |  | 
|---|
| 821 | transport_container_unregister(tc: &i->t.host_attrs); | 
|---|
| 822 | transport_container_unregister(tc: &i->link_attr_cont); | 
|---|
| 823 | transport_container_unregister(tc: &i->dev_attr_cont); | 
|---|
| 824 |  | 
|---|
| 825 | kfree(objp: i); | 
|---|
| 826 | } | 
|---|
| 827 |  | 
|---|
| 828 | __init int libata_transport_init(void) | 
|---|
| 829 | { | 
|---|
| 830 | int error; | 
|---|
| 831 |  | 
|---|
| 832 | error = transport_class_register(&ata_link_class); | 
|---|
| 833 | if (error) | 
|---|
| 834 | goto out_unregister_transport; | 
|---|
| 835 | error = transport_class_register(&ata_port_class); | 
|---|
| 836 | if (error) | 
|---|
| 837 | goto out_unregister_link; | 
|---|
| 838 | error = transport_class_register(&ata_dev_class); | 
|---|
| 839 | if (error) | 
|---|
| 840 | goto out_unregister_port; | 
|---|
| 841 | return 0; | 
|---|
| 842 |  | 
|---|
| 843 | out_unregister_port: | 
|---|
| 844 | transport_class_unregister(&ata_port_class); | 
|---|
| 845 | out_unregister_link: | 
|---|
| 846 | transport_class_unregister(&ata_link_class); | 
|---|
| 847 | out_unregister_transport: | 
|---|
| 848 | return error; | 
|---|
| 849 |  | 
|---|
| 850 | } | 
|---|
| 851 |  | 
|---|
| 852 | void __exit libata_transport_exit(void) | 
|---|
| 853 | { | 
|---|
| 854 | transport_class_unregister(&ata_link_class); | 
|---|
| 855 | transport_class_unregister(&ata_port_class); | 
|---|
| 856 | transport_class_unregister(&ata_dev_class); | 
|---|
| 857 | } | 
|---|
| 858 |  | 
|---|