User Tools

Site Tools


sata_port_multipliers

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.

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
                        ...   

libata-pmp.c

sata_pmp_configure()

sata_port_multipliers.txt · Last modified: 2019/04/24 12:30 by rpjday