Commit d9027470 authored by Gwendal Grignou's avatar Gwendal Grignou Committed by Jeff Garzik

[libata] Add ATA transport class

This is a scheleton for libata transport class.
All information is read only, exporting information from libata:
- ata_port class: one per ATA port
- ata_link class: one per ATA port or 15 for SATA Port Multiplier
- ata_device class: up to 2 for PATA link, usually one for SATA.
Signed-off-by: default avatarGwendal Grignou <gwendal@google.com>
Reviewed-by: default avatarGrant Grundler <grundler@google.com>
Signed-off-by: default avatarJeff Garzik <jgarzik@redhat.com>
parent f6f94e2a
What: /sys/class/ata_...
Date: August 2008
Contact: Gwendal Grignou<gwendal@google.com>
Description:
Provide a place in sysfs for storing the ATA topology of the system. This allows
retrieving various information about ATA objects.
Files under /sys/class/ata_port
-------------------------------
For each port, a directory ataX is created where X is the ata_port_id of
the port. The device parent is the ata host device.
idle_irq (read)
Number of IRQ received by the port while idle [some ata HBA only].
nr_pmp_links (read)
If a SATA Port Multiplier (PM) is connected, number of link behind it.
Files under /sys/class/ata_link
-------------------------------
Behind each port, there is a ata_link. If there is a SATA PM in the
topology, 15 ata_link objects are created.
If a link is behind a port, the directory name is linkX, where X is
ata_port_id of the port.
If a link is behind a PM, its name is linkX.Y where X is ata_port_id
of the parent port and Y the PM port.
hw_sata_spd_limit
Maximum speed supported by the connected SATA device.
sata_spd_limit
Maximum speed imposed by libata.
sata_spd
Current speed of the link [1.5, 3Gps,...].
Files under /sys/class/ata_device
---------------------------------
Behind each link, up to two ata device are created.
The name of the directory is devX[.Y].Z where:
- X is ata_port_id of the port where the device is connected,
- Y the port of the PM if any, and
- Z the device id: for PATA, there is usually 2 devices [0,1],
only 1 for SATA.
class
Device class. Can be "ata" for disk, "atapi" for packet device,
"pmp" for PM, or "none" if no device was found behind the link.
dma_mode
Transfer modes supported by the device when in DMA mode.
Mostly used by PATA device.
pio_mode
Transfer modes supported by the device when in PIO mode.
Mostly used by PATA device.
xfer_mode
Current transfer mode.
id
Cached result of IDENTIFY command, as described in ATA8 7.16 and 7.17.
Only valid if the device is not a PM.
gscr
Cached result of the dump of PM GSCR register.
Valid registers are:
0: SATA_PMP_GSCR_PROD_ID,
1: SATA_PMP_GSCR_REV,
2: SATA_PMP_GSCR_PORT_INFO,
32: SATA_PMP_GSCR_ERROR,
33: SATA_PMP_GSCR_ERROR_EN,
64: SATA_PMP_GSCR_FEAT,
96: SATA_PMP_GSCR_FEAT_EN,
130: SATA_PMP_GSCR_SII_GPIO
Only valid if the device is a PM.
spdn_cnt
Number of time libata decided to lower the speed of link due to errors.
ering
Formatted output of the error ring of the device.
...@@ -99,7 +99,7 @@ obj-$(CONFIG_ATA_GENERIC) += ata_generic.o ...@@ -99,7 +99,7 @@ obj-$(CONFIG_ATA_GENERIC) += ata_generic.o
# Should be last libata driver # Should be last libata driver
obj-$(CONFIG_PATA_LEGACY) += pata_legacy.o obj-$(CONFIG_PATA_LEGACY) += pata_legacy.o
libata-objs := libata-core.o libata-scsi.o libata-eh.o libata-objs := libata-core.o libata-scsi.o libata-eh.o libata-transport.o
libata-$(CONFIG_ATA_SFF) += libata-sff.o libata-$(CONFIG_ATA_SFF) += libata-sff.o
libata-$(CONFIG_SATA_PMP) += libata-pmp.o libata-$(CONFIG_SATA_PMP) += libata-pmp.o
libata-$(CONFIG_ATA_ACPI) += libata-acpi.o libata-$(CONFIG_ATA_ACPI) += libata-acpi.o
...@@ -68,7 +68,7 @@ ...@@ -68,7 +68,7 @@
#include <linux/ratelimit.h> #include <linux/ratelimit.h>
#include "libata.h" #include "libata.h"
#include "libata-transport.h"
/* debounce timing parameters in msecs { interval, duration, timeout } */ /* debounce timing parameters in msecs { interval, duration, timeout } */
const unsigned long sata_deb_timing_normal[] = { 5, 100, 2000 }; const unsigned long sata_deb_timing_normal[] = { 5, 100, 2000 };
...@@ -1017,7 +1017,7 @@ const char *ata_mode_string(unsigned long xfer_mask) ...@@ -1017,7 +1017,7 @@ const char *ata_mode_string(unsigned long xfer_mask)
return "<n/a>"; return "<n/a>";
} }
static const char *sata_spd_string(unsigned int spd) const char *sata_spd_string(unsigned int spd)
{ {
static const char * const spd_str[] = { static const char * const spd_str[] = {
"1.5 Gbps", "1.5 Gbps",
...@@ -5517,7 +5517,8 @@ void ata_link_init(struct ata_port *ap, struct ata_link *link, int pmp) ...@@ -5517,7 +5517,8 @@ void ata_link_init(struct ata_port *ap, struct ata_link *link, int pmp)
int i; int i;
/* clear everything except for devices */ /* clear everything except for devices */
memset(link, 0, offsetof(struct ata_link, device[0])); memset((void *)link + ATA_LINK_CLEAR_BEGIN, 0,
ATA_LINK_CLEAR_END - ATA_LINK_CLEAR_BEGIN);
link->ap = ap; link->ap = ap;
link->pmp = pmp; link->pmp = pmp;
...@@ -5591,7 +5592,7 @@ struct ata_port *ata_port_alloc(struct ata_host *host) ...@@ -5591,7 +5592,7 @@ struct ata_port *ata_port_alloc(struct ata_host *host)
ap = kzalloc(sizeof(*ap), GFP_KERNEL); ap = kzalloc(sizeof(*ap), GFP_KERNEL);
if (!ap) if (!ap)
return NULL; return NULL;
ap->pflags |= ATA_PFLAG_INITIALIZING; ap->pflags |= ATA_PFLAG_INITIALIZING;
ap->lock = &host->lock; ap->lock = &host->lock;
ap->print_id = -1; ap->print_id = -1;
...@@ -6093,9 +6094,18 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht) ...@@ -6093,9 +6094,18 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht)
for (i = 0; i < host->n_ports; i++) for (i = 0; i < host->n_ports; i++)
host->ports[i]->print_id = ata_print_id++; host->ports[i]->print_id = ata_print_id++;
/* Create associated sysfs transport objects */
for (i = 0; i < host->n_ports; i++) {
rc = ata_tport_add(host->dev,host->ports[i]);
if (rc) {
goto err_tadd;
}
}
rc = ata_scsi_add_hosts(host, sht); rc = ata_scsi_add_hosts(host, sht);
if (rc) if (rc)
return rc; goto err_tadd;
/* associate with ACPI nodes */ /* associate with ACPI nodes */
ata_acpi_associate(host); ata_acpi_associate(host);
...@@ -6136,6 +6146,13 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht) ...@@ -6136,6 +6146,13 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht)
} }
return 0; return 0;
err_tadd:
while (--i >= 0) {
ata_tport_delete(host->ports[i]);
}
return rc;
} }
/** /**
...@@ -6226,6 +6243,13 @@ static void ata_port_detach(struct ata_port *ap) ...@@ -6226,6 +6243,13 @@ static void ata_port_detach(struct ata_port *ap)
cancel_rearming_delayed_work(&ap->hotplug_task); cancel_rearming_delayed_work(&ap->hotplug_task);
skip_eh: skip_eh:
if (ap->pmp_link) {
int i;
for (i = 0; i < SATA_PMP_MAX_PORTS; i++)
ata_tlink_delete(&ap->pmp_link[i]);
}
ata_tport_delete(ap);
/* remove the associated SCSI host */ /* remove the associated SCSI host */
scsi_remove_host(ap->scsi_host); scsi_remove_host(ap->scsi_host);
} }
...@@ -6542,7 +6566,7 @@ static void __init ata_parse_force_param(void) ...@@ -6542,7 +6566,7 @@ static void __init ata_parse_force_param(void)
static int __init ata_init(void) static int __init ata_init(void)
{ {
int rc = -ENOMEM; int rc;
ata_parse_force_param(); ata_parse_force_param();
...@@ -6552,12 +6576,25 @@ static int __init ata_init(void) ...@@ -6552,12 +6576,25 @@ static int __init ata_init(void)
return rc; return rc;
} }
libata_transport_init();
ata_scsi_transport_template = ata_attach_transport();
if (!ata_scsi_transport_template) {
ata_sff_exit();
rc = -ENOMEM;
goto err_out;
}
printk(KERN_DEBUG "libata version " DRV_VERSION " loaded.\n"); printk(KERN_DEBUG "libata version " DRV_VERSION " loaded.\n");
return 0; return 0;
err_out:
return rc;
} }
static void __exit ata_exit(void) static void __exit ata_exit(void)
{ {
ata_release_transport(ata_scsi_transport_template);
libata_transport_exit();
ata_sff_exit(); ata_sff_exit();
kfree(ata_force_tbl); kfree(ata_force_tbl);
} }
......
...@@ -57,6 +57,7 @@ enum { ...@@ -57,6 +57,7 @@ enum {
/* error flags */ /* error flags */
ATA_EFLAG_IS_IO = (1 << 0), ATA_EFLAG_IS_IO = (1 << 0),
ATA_EFLAG_DUBIOUS_XFER = (1 << 1), ATA_EFLAG_DUBIOUS_XFER = (1 << 1),
ATA_EFLAG_OLD_ER = (1 << 31),
/* error categories */ /* error categories */
ATA_ECAT_NONE = 0, ATA_ECAT_NONE = 0,
...@@ -396,14 +397,9 @@ static struct ata_ering_entry *ata_ering_top(struct ata_ering *ering) ...@@ -396,14 +397,9 @@ static struct ata_ering_entry *ata_ering_top(struct ata_ering *ering)
return NULL; return NULL;
} }
static void ata_ering_clear(struct ata_ering *ering) int ata_ering_map(struct ata_ering *ering,
{ int (*map_fn)(struct ata_ering_entry *, void *),
memset(ering, 0, sizeof(*ering)); void *arg)
}
static int ata_ering_map(struct ata_ering *ering,
int (*map_fn)(struct ata_ering_entry *, void *),
void *arg)
{ {
int idx, rc = 0; int idx, rc = 0;
struct ata_ering_entry *ent; struct ata_ering_entry *ent;
...@@ -422,6 +418,17 @@ static int ata_ering_map(struct ata_ering *ering, ...@@ -422,6 +418,17 @@ static int ata_ering_map(struct ata_ering *ering,
return rc; return rc;
} }
int ata_ering_clear_cb(struct ata_ering_entry *ent, void *void_arg)
{
ent->eflags |= ATA_EFLAG_OLD_ER;
return 0;
}
static void ata_ering_clear(struct ata_ering *ering)
{
ata_ering_map(ering, ata_ering_clear_cb, NULL);
}
static unsigned int ata_eh_dev_action(struct ata_device *dev) static unsigned int ata_eh_dev_action(struct ata_device *dev)
{ {
struct ata_eh_context *ehc = &dev->link->eh_context; struct ata_eh_context *ehc = &dev->link->eh_context;
...@@ -572,19 +579,19 @@ void ata_scsi_error(struct Scsi_Host *host) ...@@ -572,19 +579,19 @@ void ata_scsi_error(struct Scsi_Host *host)
int nr_timedout = 0; int nr_timedout = 0;
spin_lock_irqsave(ap->lock, flags); spin_lock_irqsave(ap->lock, flags);
/* This must occur under the ap->lock as we don't want /* This must occur under the ap->lock as we don't want
a polled recovery to race the real interrupt handler a polled recovery to race the real interrupt handler
The lost_interrupt handler checks for any completed but The lost_interrupt handler checks for any completed but
non-notified command and completes much like an IRQ handler. non-notified command and completes much like an IRQ handler.
We then fall into the error recovery code which will treat We then fall into the error recovery code which will treat
this as if normal completion won the race */ this as if normal completion won the race */
if (ap->ops->lost_interrupt) if (ap->ops->lost_interrupt)
ap->ops->lost_interrupt(ap); ap->ops->lost_interrupt(ap);
list_for_each_entry_safe(scmd, tmp, &host->eh_cmd_q, eh_entry) { list_for_each_entry_safe(scmd, tmp, &host->eh_cmd_q, eh_entry) {
struct ata_queued_cmd *qc; struct ata_queued_cmd *qc;
...@@ -628,7 +635,7 @@ void ata_scsi_error(struct Scsi_Host *host) ...@@ -628,7 +635,7 @@ void ata_scsi_error(struct Scsi_Host *host)
ap->eh_tries = ATA_EH_MAX_TRIES; ap->eh_tries = ATA_EH_MAX_TRIES;
} else } else
spin_unlock_wait(ap->lock); spin_unlock_wait(ap->lock);
/* If we timed raced normal completion and there is nothing to /* If we timed raced normal completion and there is nothing to
recover nr_timedout == 0 why exactly are we doing error recovery ? */ recover nr_timedout == 0 why exactly are we doing error recovery ? */
...@@ -1755,7 +1762,7 @@ static int speed_down_verdict_cb(struct ata_ering_entry *ent, void *void_arg) ...@@ -1755,7 +1762,7 @@ static int speed_down_verdict_cb(struct ata_ering_entry *ent, void *void_arg)
struct speed_down_verdict_arg *arg = void_arg; struct speed_down_verdict_arg *arg = void_arg;
int cat; int cat;
if (ent->timestamp < arg->since) if ((ent->eflags & ATA_EFLAG_OLD_ER) || (ent->timestamp < arg->since))
return -1; return -1;
cat = ata_eh_categorize_error(ent->eflags, ent->err_mask, cat = ata_eh_categorize_error(ent->eflags, ent->err_mask,
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <linux/libata.h> #include <linux/libata.h>
#include <linux/slab.h> #include <linux/slab.h>
#include "libata.h" #include "libata.h"
#include "libata-transport.h"
const struct ata_port_operations sata_pmp_port_ops = { const struct ata_port_operations sata_pmp_port_ops = {
.inherits = &sata_port_ops, .inherits = &sata_port_ops,
...@@ -312,10 +313,10 @@ static int sata_pmp_configure(struct ata_device *dev, int print_info) ...@@ -312,10 +313,10 @@ static int sata_pmp_configure(struct ata_device *dev, int print_info)
return rc; return rc;
} }
static int sata_pmp_init_links(struct ata_port *ap, int nr_ports) static int sata_pmp_init_links (struct ata_port *ap, int nr_ports)
{ {
struct ata_link *pmp_link = ap->pmp_link; struct ata_link *pmp_link = ap->pmp_link;
int i; int i, err;
if (!pmp_link) { if (!pmp_link) {
pmp_link = kzalloc(sizeof(pmp_link[0]) * SATA_PMP_MAX_PORTS, pmp_link = kzalloc(sizeof(pmp_link[0]) * SATA_PMP_MAX_PORTS,
...@@ -327,6 +328,13 @@ static int sata_pmp_init_links(struct ata_port *ap, int nr_ports) ...@@ -327,6 +328,13 @@ static int sata_pmp_init_links(struct ata_port *ap, int nr_ports)
ata_link_init(ap, &pmp_link[i], i); ata_link_init(ap, &pmp_link[i], i);
ap->pmp_link = pmp_link; ap->pmp_link = pmp_link;
for (i = 0; i < SATA_PMP_MAX_PORTS; i++) {
err = ata_tlink_add(&pmp_link[i]);
if (err) {
goto err_tlink;
}
}
} }
for (i = 0; i < nr_ports; i++) { for (i = 0; i < nr_ports; i++) {
...@@ -339,6 +347,12 @@ static int sata_pmp_init_links(struct ata_port *ap, int nr_ports) ...@@ -339,6 +347,12 @@ static int sata_pmp_init_links(struct ata_port *ap, int nr_ports)
} }
return 0; return 0;
err_tlink:
while (--i >= 0)
ata_tlink_delete(&pmp_link[i]);
kfree(pmp_link);
ap->pmp_link = NULL;
return err;
} }
static void sata_pmp_quirks(struct ata_port *ap) static void sata_pmp_quirks(struct ata_port *ap)
......
...@@ -51,6 +51,7 @@ ...@@ -51,6 +51,7 @@
#include <asm/unaligned.h> #include <asm/unaligned.h>
#include "libata.h" #include "libata.h"
#include "libata-transport.h"
#define SECTOR_SIZE 512 #define SECTOR_SIZE 512
#define ATA_SCSI_RBUF_SIZE 4096 #define ATA_SCSI_RBUF_SIZE 4096
...@@ -64,9 +65,6 @@ static struct ata_device *__ata_scsi_find_dev(struct ata_port *ap, ...@@ -64,9 +65,6 @@ static struct ata_device *__ata_scsi_find_dev(struct ata_port *ap,
const struct scsi_device *scsidev); const struct scsi_device *scsidev);
static struct ata_device *ata_scsi_find_dev(struct ata_port *ap, static struct ata_device *ata_scsi_find_dev(struct ata_port *ap,
const struct scsi_device *scsidev); const struct scsi_device *scsidev);
static int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel,
unsigned int id, unsigned int lun);
#define RW_RECOVERY_MPAGE 0x1 #define RW_RECOVERY_MPAGE 0x1
#define RW_RECOVERY_MPAGE_LEN 12 #define RW_RECOVERY_MPAGE_LEN 12
...@@ -106,17 +104,6 @@ static const u8 def_control_mpage[CONTROL_MPAGE_LEN] = { ...@@ -106,17 +104,6 @@ static const u8 def_control_mpage[CONTROL_MPAGE_LEN] = {
0, 30 /* extended self test time, see 05-359r1 */ 0, 30 /* extended self test time, see 05-359r1 */
}; };
/*
* libata transport template. libata doesn't do real transport stuff.
* It just needs the eh_timed_out hook.
*/
static struct scsi_transport_template ata_scsi_transport_template = {
.eh_strategy_handler = ata_scsi_error,
.eh_timed_out = ata_scsi_timed_out,
.user_scan = ata_scsi_user_scan,
};
static const struct { static const struct {
enum link_pm value; enum link_pm value;
const char *name; const char *name;
...@@ -3334,7 +3321,7 @@ int ata_scsi_add_hosts(struct ata_host *host, struct scsi_host_template *sht) ...@@ -3334,7 +3321,7 @@ int ata_scsi_add_hosts(struct ata_host *host, struct scsi_host_template *sht)
*(struct ata_port **)&shost->hostdata[0] = ap; *(struct ata_port **)&shost->hostdata[0] = ap;
ap->scsi_host = shost; ap->scsi_host = shost;
shost->transportt = &ata_scsi_transport_template; shost->transportt = ata_scsi_transport_template;
shost->unique_id = ap->print_id; shost->unique_id = ap->print_id;
shost->max_id = 16; shost->max_id = 16;
shost->max_lun = 1; shost->max_lun = 1;
...@@ -3616,8 +3603,8 @@ void ata_scsi_hotplug(struct work_struct *work) ...@@ -3616,8 +3603,8 @@ void ata_scsi_hotplug(struct work_struct *work)
* RETURNS: * RETURNS:
* Zero. * Zero.
*/ */
static int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel, int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel,
unsigned int id, unsigned int lun) unsigned int id, unsigned int lun)
{ {
struct ata_port *ap = ata_shost_to_port(shost); struct ata_port *ap = ata_shost_to_port(shost);
unsigned long flags; unsigned long flags;
......
/*
* Copyright 2008 ioogle, Inc. All rights reserved.
* Released under GPL v2.
*
* Libata transport class.
*
* The ATA transport class contains common code to deal with ATA HBAs,
* an approximated representation of ATA topologies in the driver model,
* and various sysfs attributes to expose these topologies and management
* interfaces to user-space.
*
* There are 3 objects defined in in this class:
* - ata_port
* - ata_link
* - ata_device
* Each port has a link object. Each link can have up to two devices for PATA
* and generally one for SATA.
* If there is SATA port multiplier [PMP], 15 additional ata_link object are
* created.
*
* These objects are created when the ata host is initialized and when a PMP is
* found. They are removed only when the HBA is removed, cleaned before the
* error handler runs.
*/
#include <linux/kernel.h>
#include <linux/blkdev.h>
#include <linux/spinlock.h>
#include <scsi/scsi_transport.h>
#include <linux/libata.h>
#include <linux/hdreg.h>
#include <linux/uaccess.h>
#include "libata.h"
#include "libata-transport.h"
#define ATA_PORT_ATTRS 2
#define ATA_LINK_ATTRS 3
#define ATA_DEV_ATTRS 9
struct scsi_transport_template;
struct scsi_transport_template *ata_scsi_transport_template;
struct ata_internal {
struct scsi_transport_template t;
struct device_attribute private_port_attrs[ATA_PORT_ATTRS];
struct device_attribute private_link_attrs[ATA_LINK_ATTRS];
struct device_attribute private_dev_attrs[ATA_DEV_ATTRS];
struct transport_container link_attr_cont;
struct transport_container dev_attr_cont;
/*
* The array of null terminated pointers to attributes
* needed by scsi_sysfs.c
*/
struct device_attribute *link_attrs[ATA_LINK_ATTRS + 1];
struct device_attribute *port_attrs[ATA_PORT_ATTRS + 1];
struct device_attribute *dev_attrs[ATA_DEV_ATTRS + 1];
};
#define to_ata_internal(tmpl) container_of(tmpl, struct ata_internal, t)
#define tdev_to_device(d) \
container_of((d), struct ata_device, tdev)
#define transport_class_to_dev(dev) \
tdev_to_device((dev)->parent)
#define tdev_to_link(d) \
container_of((d), struct ata_link, tdev)
#define transport_class_to_link(dev) \
tdev_to_link((dev)->parent)
#define tdev_to_port(d) \
container_of((d), struct ata_port, tdev)
#define transport_class_to_port(dev) \
tdev_to_port((dev)->parent)
/* Device objects are always created whit link objects */
static int ata_tdev_add(struct ata_device *dev);
static void ata_tdev_delete(struct ata_device *dev);
/*
* Hack to allow attributes of the same name in different objects.
*/
#define ATA_DEVICE_ATTR(_prefix,_name,_mode,_show,_store) \
struct device_attribute device_attr_##_prefix##_##_name = \
__ATTR(_name,_mode,_show,_store)
#define ata_bitfield_name_match(title, table) \
static ssize_t \
get_ata_##title##_names(u32 table_key, char *buf) \
{ \
char *prefix = ""; \
ssize_t len = 0; \
int i; \
\
for (i = 0; i < ARRAY_SIZE(table); i++) { \
if (table[i].value & table_key) { \
len += sprintf(buf + len, "%s%s", \
prefix, table[i].name); \
prefix = ", "; \
} \
} \
len += sprintf(buf + len, "\n"); \
return len; \
}
#define ata_bitfield_name_search(title, table) \
static ssize_t \
get_ata_##title##_names(u32 table_key, char *buf) \
{ \
ssize_t len = 0; \
int i; \
\
for (i = 0; i < ARRAY_SIZE(table); i++) { \
if (table[i].value == table_key) { \
len += sprintf(buf + len, "%s", \
table[i].name); \
break; \
} \
} \
len += sprintf(buf + len, "\n"); \
return len; \
}
static struct {
u32 value;
char *name;
} ata_class_names[] = {
{ ATA_DEV_UNKNOWN, "unknown" },
{ ATA_DEV_ATA, "ata" },
{ ATA_DEV_ATA_UNSUP, "ata" },
{ ATA_DEV_ATAPI, "atapi" },
{ ATA_DEV_ATAPI_UNSUP, "atapi" },
{ ATA_DEV_PMP, "pmp" },
{ ATA_DEV_PMP_UNSUP, "pmp" },
{ ATA_DEV_SEMB, "semb" },
{ ATA_DEV_SEMB_UNSUP, "semb" },
{ ATA_DEV_NONE, "none" }
};
ata_bitfield_name_search(class, ata_class_names)
static struct {
u32 value;
char *name;
} ata_err_names[] = {
{ AC_ERR_DEV, "DeviceError" },
{ AC_ERR_HSM, "HostStateMachineError" },
{ AC_ERR_TIMEOUT, "Timeout" },
{ AC_ERR_MEDIA, "MediaError" },
{ AC_ERR_ATA_BUS, "BusError" },
{ AC_ERR_HOST_BUS, "HostBusError" },
{ AC_ERR_SYSTEM, "SystemError" },
{ AC_ERR_INVALID, "InvalidArg" },
{ AC_ERR_OTHER, "Unknown" },
{ AC_ERR_NODEV_HINT, "NoDeviceHint" },
{ AC_ERR_NCQ, "NCQError" }
};
ata_bitfield_name_match(err, ata_err_names)
static struct {
u32 value;
char *name;
} ata_xfer_names[] = {
{ XFER_UDMA_7, "XFER_UDMA_7" },
{ XFER_UDMA_6, "XFER_UDMA_6" },
{ XFER_UDMA_5, "XFER_UDMA_5" },
{ XFER_UDMA_4, "XFER_UDMA_4" },
{ XFER_UDMA_3, "XFER_UDMA_3" },
{ XFER_UDMA_2, "XFER_UDMA_2" },
{ XFER_UDMA_1, "XFER_UDMA_1" },
{ XFER_UDMA_0, "XFER_UDMA_0" },
{ XFER_MW_DMA_4, "XFER_MW_DMA_4" },
{ XFER_MW_DMA_3, "XFER_MW_DMA_3" },
{ XFER_MW_DMA_2, "XFER_MW_DMA_2" },
{ XFER_MW_DMA_1, "XFER_MW_DMA_1" },
{ XFER_MW_DMA_0, "XFER_MW_DMA_0" },
{ XFER_SW_DMA_2, "XFER_SW_DMA_2" },
{ XFER_SW_DMA_1, "XFER_SW_DMA_1" },
{ XFER_SW_DMA_0, "XFER_SW_DMA_0" },
{ XFER_PIO_6, "XFER_PIO_6" },
{ XFER_PIO_5, "XFER_PIO_5" },
{ XFER_PIO_4, "XFER_PIO_4" },
{ XFER_PIO_3, "XFER_PIO_3" },
{ XFER_PIO_2, "XFER_PIO_2" },
{ XFER_PIO_1, "XFER_PIO_1" },
{ XFER_PIO_0, "XFER_PIO_0" },
{ XFER_PIO_SLOW, "XFER_PIO_SLOW" }
};
ata_bitfield_name_match(xfer,ata_xfer_names)
/*
* ATA Port attributes
*/
#define ata_port_show_simple(field, name, format_string, cast) \
static ssize_t \
show_ata_port_##name(struct device *dev, \
struct device_attribute *attr, char *buf) \
{ \
struct ata_port *ap = transport_class_to_port(dev); \
\
return snprintf(buf, 20, format_string, cast ap->field); \
}
#define ata_port_simple_attr(field, name, format_string, type) \
ata_port_show_simple(field, name, format_string, (type)) \
static DEVICE_ATTR(name, S_IRUGO, show_ata_port_##name, NULL)
ata_port_simple_attr(nr_pmp_links, nr_pmp_links, "%d\n", int);
ata_port_simple_attr(stats.idle_irq, idle_irq, "%ld\n", unsigned long);
static DECLARE_TRANSPORT_CLASS(ata_port_class,
"ata_port", NULL, NULL, NULL);
static void ata_tport_release(struct device *dev)
{
put_device(dev->parent);
}
/**
* ata_is_port -- check if a struct device represents a ATA port
* @dev: device to check
*
* Returns:
* %1 if the device represents a ATA Port, %0 else
*/
int ata_is_port(const struct device *dev)
{
return dev->release == ata_tport_release;
}
static int ata_tport_match(struct attribute_container *cont,
struct device *dev)
{
if (!ata_is_port(dev))
return 0;
return &ata_scsi_transport_template->host_attrs.ac == cont;
}
/**
* ata_tport_delete -- remove ATA PORT
* @port: ATA PORT to remove
*
* Removes the specified ATA PORT. Remove the associated link as well.
*/
void ata_tport_delete(struct ata_port *ap)
{
struct device *dev = &ap->tdev;
ata_tlink_delete(&ap->link);
transport_remove_device(dev);
device_del(dev);
transport_destroy_device(dev);
put_device(dev);
}
/** ata_tport_add - initialize a transport ATA port structure
*
* @parent: parent device
* @ap: existing ata_port structure
*
* Initialize a ATA port structure for sysfs. It will be added to the device
* tree below the device specified by @parent which could be a PCI device.
*
* Returns %0 on success
*/
int ata_tport_add(struct device *parent,
struct ata_port *ap)
{
int error;
struct device *dev = &ap->tdev;
device_initialize(dev);
dev->parent = get_device(parent);
dev->release = ata_tport_release;
dev_set_name(dev, "ata%d", ap->print_id);
transport_setup_device(dev);
error = device_add(dev);
if (error) {
goto tport_err;
}
transport_add_device(dev);
transport_configure_device(dev);
error = ata_tlink_add(&ap->link);
if (error) {
goto tport_link_err;
}
return 0;
tport_link_err:
transport_remove_device(dev);
device_del(dev);
tport_err:
transport_destroy_device(dev);
put_device(dev);
return error;
}
/*
* ATA link attributes
*/
#define ata_link_show_linkspeed(field) \
static ssize_t \
show_ata_link_##field(struct device *dev, \
struct device_attribute *attr, char *buf) \
{ \
struct ata_link *link = transport_class_to_link(dev); \
\
return sprintf(buf,"%s\n", sata_spd_string(fls(link->field))); \
}
#define ata_link_linkspeed_attr(field) \
ata_link_show_linkspeed(field) \
static DEVICE_ATTR(field, S_IRUGO, show_ata_link_##field, NULL)
ata_link_linkspeed_attr(hw_sata_spd_limit);
ata_link_linkspeed_attr(sata_spd_limit);
ata_link_linkspeed_attr(sata_spd);
static DECLARE_TRANSPORT_CLASS(ata_link_class,
"ata_link", NULL, NULL, NULL);
static void ata_tlink_release(struct device *dev)
{
put_device(dev->parent);
}
/**
* ata_is_link -- check if a struct device represents a ATA link
* @dev: device to check
*
* Returns:
* %1 if the device represents a ATA link, %0 else
*/
int ata_is_link(const struct device *dev)
{
return dev->release == ata_tlink_release;
}
static int ata_tlink_match(struct attribute_container *cont,
struct device *dev)
{
struct ata_internal* i = to_ata_internal(ata_scsi_transport_template);
if (!ata_is_link(dev))
return 0;
return &i->link_attr_cont.ac == cont;
}
/**
* ata_tlink_delete -- remove ATA LINK
* @port: ATA LINK to remove
*
* Removes the specified ATA LINK. remove associated ATA device(s) as well.
*/
void ata_tlink_delete(struct ata_link *link)
{
struct device *dev = &link->tdev;
struct ata_device *ata_dev;
ata_for_each_dev(ata_dev, link, ALL) {
ata_tdev_delete(ata_dev);
}
transport_remove_device(dev);
device_del(dev);
transport_destroy_device(dev);
put_device(dev);
}
/**
* ata_tlink_add -- initialize a transport ATA link structure
* @link: allocated ata_link structure.
*
* Initialize an ATA LINK structure for sysfs. It will be added in the
* device tree below the ATA PORT it belongs to.
*
* Returns %0 on success
*/
int ata_tlink_add(struct ata_link *link)
{
struct device *dev = &link->tdev;
struct ata_port *ap = link->ap;
struct ata_device *ata_dev;
int error;
device_initialize(dev);
dev->parent = get_device(&ap->tdev);
dev->release = ata_tlink_release;
if (ata_is_host_link(link))
dev_set_name(dev, "link%d", ap->print_id);
else
dev_set_name(dev, "link%d.%d", ap->print_id, link->pmp);
transport_setup_device(dev);
error = device_add(dev);
if (error) {
goto tlink_err;
}
transport_add_device(dev);
transport_configure_device(dev);
ata_for_each_dev(ata_dev, link, ALL) {
error = ata_tdev_add(ata_dev);
if (error) {
goto tlink_dev_err;
}
}
return 0;
tlink_dev_err:
while (--ata_dev >= link->device) {
ata_tdev_delete(ata_dev);
}
transport_remove_device(dev);
device_del(dev);
tlink_err:
transport_destroy_device(dev);
put_device(dev);
return error;
}
/*
* ATA device attributes
*/
#define ata_dev_show_class(title, field) \
static ssize_t \
show_ata_dev_##field(struct device *dev, \
struct device_attribute *attr, char *buf) \
{ \
struct ata_device *ata_dev = transport_class_to_dev(dev); \
\
return get_ata_##title##_names(ata_dev->field, buf); \
}
#define ata_dev_attr(title, field) \
ata_dev_show_class(title, field) \
static DEVICE_ATTR(field, S_IRUGO, show_ata_dev_##field, NULL)
ata_dev_attr(class, class);
ata_dev_attr(xfer, pio_mode);
ata_dev_attr(xfer, dma_mode);
ata_dev_attr(xfer, xfer_mode);
#define ata_dev_show_simple(field, format_string, cast) \
static ssize_t \
show_ata_dev_##field(struct device *dev, \
struct device_attribute *attr, char *buf) \
{ \
struct ata_device *ata_dev = transport_class_to_dev(dev); \
\
return snprintf(buf, 20, format_string, cast ata_dev->field); \
}
#define ata_dev_simple_attr(field, format_string, type) \
ata_dev_show_simple(field, format_string, (type)) \
static DEVICE_ATTR(field, S_IRUGO, \
show_ata_dev_##field, NULL)
ata_dev_simple_attr(spdn_cnt, "%d\n", int);
struct ata_show_ering_arg {
char* buf;
int written;
};
static int ata_show_ering(struct ata_ering_entry *ent, void *void_arg)
{
struct ata_show_ering_arg* arg = void_arg;
struct timespec time;
jiffies_to_timespec(ent->timestamp,&time);
arg->written += sprintf(arg->buf + arg->written,
"[%5lu.%06lu]",
time.tv_sec, time.tv_nsec);
arg->written += get_ata_err_names(ent->err_mask,
arg->buf + arg->written);
return 0;
}
static ssize_t
show_ata_dev_ering(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ata_device *ata_dev = transport_class_to_dev(dev);
struct ata_show_ering_arg arg = { buf, 0 };
ata_ering_map(&ata_dev->ering, ata_show_ering, &arg);
return arg.written;
}
static DEVICE_ATTR(ering, S_IRUGO, show_ata_dev_ering, NULL);
static ssize_t
show_ata_dev_id(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ata_device *ata_dev = transport_class_to_dev(dev);
int written = 0, i = 0;
if (ata_dev->class == ATA_DEV_PMP)
return 0;
for(i=0;i<ATA_ID_WORDS;i++) {
written += snprintf(buf+written, 20, "%04x%c",
ata_dev->id[i],
((i+1) & 7) ? ' ' : '\n');
}
return written;
}
static DEVICE_ATTR(id, S_IRUGO, show_ata_dev_id, NULL);
static ssize_t
show_ata_dev_gscr(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ata_device *ata_dev = transport_class_to_dev(dev);
int written = 0, i = 0;
if (ata_dev->class != ATA_DEV_PMP)
return 0;
for(i=0;i<SATA_PMP_GSCR_DWORDS;i++) {
written += snprintf(buf+written, 20, "%08x%c",
ata_dev->gscr[i],
((i+1) & 3) ? ' ' : '\n');
}
if (SATA_PMP_GSCR_DWORDS & 3)
buf[written-1] = '\n';
return written;
}
static DEVICE_ATTR(gscr, S_IRUGO, show_ata_dev_gscr, NULL);
static DECLARE_TRANSPORT_CLASS(ata_dev_class,
"ata_device", NULL, NULL, NULL);
static void ata_tdev_release(struct device *dev)
{
put_device(dev->parent);
}
/**
* ata_is_ata_dev -- check if a struct device represents a ATA device
* @dev: device to check
*
* Returns:
* %1 if the device represents a ATA device, %0 else
*/
int ata_is_ata_dev(const struct device *dev)
{
return dev->release == ata_tdev_release;
}
static int ata_tdev_match(struct attribute_container *cont,
struct device *dev)
{
struct ata_internal* i = to_ata_internal(ata_scsi_transport_template);
if (!ata_is_ata_dev(dev))
return 0;
return &i->dev_attr_cont.ac == cont;
}
/**
* ata_tdev_free -- free a ATA LINK
* @dev: ATA PHY to free
*
* Frees the specified ATA PHY.
*
* Note:
* This function must only be called on a PHY that has not
* successfully been added using ata_tdev_add().
*/
static void ata_tdev_free(struct ata_device *dev)
{
transport_destroy_device(&dev->tdev);
put_device(&dev->tdev);
}
/**
* ata_tdev_delete -- remove ATA device
* @port: ATA PORT to remove
*
* Removes the specified ATA device.
*/
static void ata_tdev_delete(struct ata_device *ata_dev)
{
struct device *dev = &ata_dev->tdev;
transport_remove_device(dev);
device_del(dev);
ata_tdev_free(ata_dev);
}
/**
* ata_tdev_add -- initialize a transport ATA device structure.
* @ata_dev: ata_dev structure.
*
* Initialize an ATA device structure for sysfs. It will be added in the
* device tree below the ATA LINK device it belongs to.
*
* Returns %0 on success
*/
static int ata_tdev_add(struct ata_device *ata_dev)
{
struct device *dev = &ata_dev->tdev;
struct ata_link *link = ata_dev->link;
struct ata_port *ap = link->ap;
int error;
device_initialize(dev);
dev->parent = get_device(&link->tdev);
dev->release = ata_tdev_release;
if (ata_is_host_link(link))
dev_set_name(dev, "dev%d.%d", ap->print_id,ata_dev->devno);
else
dev_set_name(dev, "dev%d.%d.0", ap->print_id, link->pmp);
transport_setup_device(dev);
error = device_add(dev);
if (error) {
ata_tdev_free(ata_dev);
return error;
}
transport_add_device(dev);
transport_configure_device(dev);
return 0;
}
/*
* Setup / Teardown code
*/
#define SETUP_TEMPLATE(attrb, field, perm, test) \
i->private_##attrb[count] = dev_attr_##field; \
i->private_##attrb[count].attr.mode = perm; \
i->attrb[count] = &i->private_##attrb[count]; \
if (test) \
count++
#define SETUP_LINK_ATTRIBUTE(field) \
SETUP_TEMPLATE(link_attrs, field, S_IRUGO, 1)
#define SETUP_PORT_ATTRIBUTE(field) \
SETUP_TEMPLATE(port_attrs, field, S_IRUGO, 1)
#define SETUP_DEV_ATTRIBUTE(field) \
SETUP_TEMPLATE(dev_attrs, field, S_IRUGO, 1)
/**
* ata_attach_transport -- instantiate ATA transport template
*/
struct scsi_transport_template *ata_attach_transport(void)
{
struct ata_internal *i;
int count;
i = kzalloc(sizeof(struct ata_internal), GFP_KERNEL);
if (!i)
return NULL;
i->t.eh_strategy_handler = ata_scsi_error;
i->t.eh_timed_out = ata_scsi_timed_out;
i->t.user_scan = ata_scsi_user_scan;
i->t.host_attrs.ac.attrs = &i->port_attrs[0];
i->t.host_attrs.ac.class = &ata_port_class.class;
i->t.host_attrs.ac.match = ata_tport_match;
transport_container_register(&i->t.host_attrs);
i->link_attr_cont.ac.class = &ata_link_class.class;
i->link_attr_cont.ac.attrs = &i->link_attrs[0];
i->link_attr_cont.ac.match = ata_tlink_match;
transport_container_register(&i->link_attr_cont);
i->dev_attr_cont.ac.class = &ata_dev_class.class;
i->dev_attr_cont.ac.attrs = &i->dev_attrs[0];
i->dev_attr_cont.ac.match = ata_tdev_match;
transport_container_register(&i->dev_attr_cont);
count = 0;
SETUP_PORT_ATTRIBUTE(nr_pmp_links);
SETUP_PORT_ATTRIBUTE(idle_irq);
BUG_ON(count > ATA_PORT_ATTRS);
i->port_attrs[count] = NULL;
count = 0;
SETUP_LINK_ATTRIBUTE(hw_sata_spd_limit);
SETUP_LINK_ATTRIBUTE(sata_spd_limit);
SETUP_LINK_ATTRIBUTE(sata_spd);
BUG_ON(count > ATA_LINK_ATTRS);
i->link_attrs[count] = NULL;
count = 0;
SETUP_DEV_ATTRIBUTE(class);
SETUP_DEV_ATTRIBUTE(pio_mode);
SETUP_DEV_ATTRIBUTE(dma_mode);
SETUP_DEV_ATTRIBUTE(xfer_mode);
SETUP_DEV_ATTRIBUTE(spdn_cnt);
SETUP_DEV_ATTRIBUTE(ering);
SETUP_DEV_ATTRIBUTE(id);
SETUP_DEV_ATTRIBUTE(gscr);
BUG_ON(count > ATA_DEV_ATTRS);
i->dev_attrs[count] = NULL;
return &i->t;
}
/**
* ata_release_transport -- release ATA transport template instance
* @t: transport template instance
*/
void ata_release_transport(struct scsi_transport_template *t)
{
struct ata_internal *i = to_ata_internal(t);
transport_container_unregister(&i->t.host_attrs);
transport_container_unregister(&i->link_attr_cont);
transport_container_unregister(&i->dev_attr_cont);
kfree(i);
}
__init int libata_transport_init(void)
{
int error;
error = transport_class_register(&ata_link_class);
if (error)
goto out_unregister_transport;
error = transport_class_register(&ata_port_class);
if (error)
goto out_unregister_link;
error = transport_class_register(&ata_dev_class);
if (error)
goto out_unregister_port;
return 0;
out_unregister_port:
transport_class_unregister(&ata_port_class);
out_unregister_link:
transport_class_unregister(&ata_link_class);
out_unregister_transport:
return error;
}
void __exit libata_transport_exit(void)
{
transport_class_unregister(&ata_link_class);
transport_class_unregister(&ata_port_class);
transport_class_unregister(&ata_dev_class);
}
#ifndef _LIBATA_TRANSPORT_H
#define _LIBATA_TRANSPORT_H
extern struct scsi_transport_template *ata_scsi_transport_template;
int ata_tlink_add(struct ata_link *link);
void ata_tlink_delete(struct ata_link *link);
int ata_tport_add(struct device *parent, struct ata_port *ap);
void ata_tport_delete(struct ata_port *ap);
struct scsi_transport_template *ata_attach_transport(void);
void ata_release_transport(struct scsi_transport_template *t);
__init int libata_transport_init(void);
void __exit libata_transport_exit(void);
#endif
...@@ -102,6 +102,7 @@ extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg); ...@@ -102,6 +102,7 @@ extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg);
extern struct ata_port *ata_port_alloc(struct ata_host *host); extern struct ata_port *ata_port_alloc(struct ata_host *host);
extern void ata_dev_enable_pm(struct ata_device *dev, enum link_pm policy); extern void ata_dev_enable_pm(struct ata_device *dev, enum link_pm policy);
extern void ata_lpm_schedule(struct ata_port *ap, enum link_pm); extern void ata_lpm_schedule(struct ata_port *ap, enum link_pm);
extern const char *sata_spd_string(unsigned int spd);
/* libata-acpi.c */ /* libata-acpi.c */
#ifdef CONFIG_ATA_ACPI #ifdef CONFIG_ATA_ACPI
...@@ -137,6 +138,9 @@ extern void ata_scsi_hotplug(struct work_struct *work); ...@@ -137,6 +138,9 @@ extern void ata_scsi_hotplug(struct work_struct *work);
extern void ata_schedule_scsi_eh(struct Scsi_Host *shost); extern void ata_schedule_scsi_eh(struct Scsi_Host *shost);
extern void ata_scsi_dev_rescan(struct work_struct *work); extern void ata_scsi_dev_rescan(struct work_struct *work);
extern int ata_bus_probe(struct ata_port *ap); extern int ata_bus_probe(struct ata_port *ap);
extern int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel,
unsigned int id, unsigned int lun);
/* libata-eh.c */ /* libata-eh.c */
extern unsigned long ata_internal_cmd_timeout(struct ata_device *dev, u8 cmd); extern unsigned long ata_internal_cmd_timeout(struct ata_device *dev, u8 cmd);
...@@ -164,6 +168,9 @@ extern int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset, ...@@ -164,6 +168,9 @@ extern int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
ata_postreset_fn_t postreset, ata_postreset_fn_t postreset,
struct ata_link **r_failed_disk); struct ata_link **r_failed_disk);
extern void ata_eh_finish(struct ata_port *ap); extern void ata_eh_finish(struct ata_port *ap);
extern int ata_ering_map(struct ata_ering *ering,
int (*map_fn)(struct ata_ering_entry *, void *),
void *arg);
/* libata-pmp.c */ /* libata-pmp.c */
#ifdef CONFIG_SATA_PMP #ifdef CONFIG_SATA_PMP
......
...@@ -604,6 +604,7 @@ struct ata_device { ...@@ -604,6 +604,7 @@ struct ata_device {
union acpi_object *gtf_cache; union acpi_object *gtf_cache;
unsigned int gtf_filter; unsigned int gtf_filter;
#endif #endif
struct device tdev;
/* n_sector is CLEAR_BEGIN, read comment above CLEAR_BEGIN */ /* n_sector is CLEAR_BEGIN, read comment above CLEAR_BEGIN */
u64 n_sectors; /* size of device, if ATA */ u64 n_sectors; /* size of device, if ATA */
u64 n_native_sectors; /* native size, if ATA */ u64 n_native_sectors; /* native size, if ATA */
...@@ -690,6 +691,7 @@ struct ata_link { ...@@ -690,6 +691,7 @@ struct ata_link {
struct ata_port *ap; struct ata_port *ap;
int pmp; /* port multiplier port # */ int pmp; /* port multiplier port # */
struct device tdev;
unsigned int active_tag; /* active tag on this link */ unsigned int active_tag; /* active tag on this link */
u32 sactive; /* active NCQ commands */ u32 sactive; /* active NCQ commands */
...@@ -707,6 +709,8 @@ struct ata_link { ...@@ -707,6 +709,8 @@ struct ata_link {
struct ata_device device[ATA_MAX_DEVICES]; struct ata_device device[ATA_MAX_DEVICES];
}; };
#define ATA_LINK_CLEAR_BEGIN offsetof(struct ata_link, active_tag)
#define ATA_LINK_CLEAR_END offsetof(struct ata_link, device[0])
struct ata_port { struct ata_port {
struct Scsi_Host *scsi_host; /* our co-allocated scsi host */ struct Scsi_Host *scsi_host; /* our co-allocated scsi host */
...@@ -752,6 +756,7 @@ struct ata_port { ...@@ -752,6 +756,7 @@ struct ata_port {
struct ata_port_stats stats; struct ata_port_stats stats;
struct ata_host *host; struct ata_host *host;
struct device *dev; struct device *dev;
struct device tdev;
struct mutex scsi_scan_mutex; struct mutex scsi_scan_mutex;
struct delayed_work hotplug_task; struct delayed_work hotplug_task;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment