Overview
Discussion of kernel support for SATA port multipliers.
Kernel content
- include/
- linux/
- ata.h
- libata.h
- drivers/
- ata/
- Kconfig
- Makefile
- libata-pmp.c
- libata-eh.c
Build files [drivers/ata]
Kconfig
config SATA_PMP
bool "SATA Port Multiplier support"
default y
help
This option adds support for SATA Port Multipliers
(the SATA version of an ethernet hub, or SAS expander).
Makefile
libata-$(CONFIG_SATA_PMP) += libata-pmp.o
Header files [include/linux/]
ata.h
enum {
/* various global constants */
...
/* ATA device commands */
...
ATA_CMD_PMP_READ = 0xE4,
ATA_CMD_PMP_READ_DMA = 0xE9,
ATA_CMD_PMP_WRITE = 0xE8,
ATA_CMD_PMP_WRITE_DMA = 0xEB,
...
/* PMP stuff */ SATA_PMP_MAX_PORTS = 15, SATA_PMP_CTRL_PORT = 15, SATA_PMP_GSCR_DWORDS = 128, SATA_PMP_GSCR_PROD_ID = 0, SATA_PMP_GSCR_REV = 1, SATA_PMP_GSCR_PORT_INFO = 2, SATA_PMP_GSCR_ERROR = 32, SATA_PMP_GSCR_ERROR_EN = 33, SATA_PMP_GSCR_FEAT = 64, SATA_PMP_GSCR_FEAT_EN = 96, SATA_PMP_PSCR_STATUS = 0, SATA_PMP_PSCR_ERROR = 1, SATA_PMP_PSCR_CONTROL = 2, SATA_PMP_FEAT_BIST = (1 << 0), SATA_PMP_FEAT_PMREQ = (1 << 1), SATA_PMP_FEAT_DYNSSC = (1 << 2), SATA_PMP_FEAT_NOTIFY = (1 << 3),
#define sata_pmp_gscr_vendor(gscr) ((gscr)[SATA_PMP_GSCR_PROD_ID] & 0xffff) #define sata_pmp_gscr_devid(gscr) ((gscr)[SATA_PMP_GSCR_PROD_ID] >> 16) #define sata_pmp_gscr_rev(gscr) (((gscr)[SATA_PMP_GSCR_REV] >> 8) & 0xff) #define sata_pmp_gscr_ports(gscr) ((gscr)[SATA_PMP_GSCR_PORT_INFO] & 0xf)
libata.h
enum {
/* various global constants */
...
ATA_DEV_UNKNOWN = 0, /* unknown device */
ATA_DEV_ATA = 1, /* ATA device */
ATA_DEV_ATA_UNSUP = 2, /* ATA device (unsupported) */
ATA_DEV_ATAPI = 3, /* ATAPI device */
ATA_DEV_ATAPI_UNSUP = 4, /* ATAPI device (unsupported) */
ATA_DEV_PMP = 5, /* SATA port multiplier */
ATA_DEV_PMP_UNSUP = 6, /* SATA port multiplier (unsupported) */
ATA_DEV_SEMB = 7, /* SEMB */
ATA_DEV_SEMB_UNSUP = 8, /* SEMB (unsupported) */
ATA_DEV_ZAC = 9, /* ZAC device */
ATA_DEV_ZAC_UNSUP = 10, /* ZAC device (unsupported) */
ATA_DEV_NONE = 11, /* no device */
...
/* struct ata_port flags */
...
ATA_FLAG_PMP = (1 << 19), /* controller supports PMP */
...
struct ata_port_operations {
...
void (*pmp_attach)(struct ata_port *ap);
void (*pmp_detach)(struct ata_port *ap);
...
#ifdef CONFIG_SATA_PMP
static inline bool sata_pmp_supported(struct ata_port *ap)
{
return ap->flags & ATA_FLAG_PMP;
}
static inline bool sata_pmp_attached(struct ata_port *ap)
{
return ap->nr_pmp_links != 0;
}
static inline bool ata_is_host_link(const struct ata_link *link)
{
return link == &link->ap->link || link == link->ap->slave_link;
}
#else /* CONFIG_SATA_PMP */
static inline bool sata_pmp_supported(struct ata_port *ap)
{
return false;
}
static inline bool sata_pmp_attached(struct ata_port *ap)
{
return false;
}
static inline bool ata_is_host_link(const struct ata_link *link)
{
return 1;
}
#endif /* CONFIG_SATA_PMP */
Structures [libata.h]
struct ata_device
ata_device -> ata_link:
struct ata_device {
struct ata_link *link;
unsigned int devno; /* 0 or 1 */
unsigned int horkage; /* List of broken features */
unsigned long flags; /* ATA_DFLAG_xxx */
struct scsi_device *sdev; /* attached SCSI device */
void *private_data;
#ifdef CONFIG_ATA_ACPI
union acpi_object *gtf_cache;
unsigned int gtf_filter;
#endif
#ifdef CONFIG_SATA_ZPODD
void *zpodd;
#endif
struct device tdev;
/* n_sector is CLEAR_BEGIN, read comment above CLEAR_BEGIN */
u64 n_sectors; /* size of device, if ATA */
u64 n_native_sectors; /* native size, if ATA */
unsigned int class; /* ATA_DEV_xxx */
...
union {
u16 id[ATA_ID_WORDS]; /* IDENTIFY xxx DEVICE data */
u32 gscr[SATA_PMP_GSCR_DWORDS]; /* PMP GSCR block */
} ____cacheline_aligned;
class should be DEV_ATA_PMP.
struct ata_link
ata_link -> ata_port:
struct ata_link {
struct ata_port *ap;
int pmp; /* port multiplier port # */
...
struct ata_device device[ATA_MAX_DEVICES];
struct ata_port
struct ata_port {
struct Scsi_Host *scsi_host; /* our co-allocated scsi host */
struct ata_port_operations *ops;
spinlock_t *lock;
/* Flags owned by the EH context. Only EH should touch these once the
port is active */
unsigned long flags; /* ATA_FLAG_xxx */
...
struct ata_link link; /* host default link */
struct ata_link *slave_link; /* see ata_slave_link_init() */
int nr_pmp_links; /* nr of available PMP links */
struct ata_link *pmp_link; /* array of PMP links */
...
struct ata_host *host;
struct device *dev;
...
drivers/ata/
libata-eh.c
static int ata_eh_revalidate_and_attach(struct ata_link *link,
struct ata_device **r_failed_dev)
{
struct ata_port *ap = link->ap;
struct ata_eh_context *ehc = &link->eh_context;
struct ata_device *dev;
...
ata_for_each_dev(dev, link, ALL_REVERSE) {
...
dev->class = ehc->classes[dev->devno];
if (dev->class == ATA_DEV_PMP)
rc = sata_pmp_attach(dev);
else
...