[PATCH 2/3] libsas: add support for SATA port multiplier(PM)

From: Xiangliang Yu
Date: Thu Apr 24 2014 - 09:28:49 EST


Add support for SATA PM so that host can find devices that is
attached to PM, and add PM hotplug event support.

Signed-off-by: Xiangliang Yu <yxlraid@xxxxxxxxx>
---
drivers/ata/libata-scsi.c | 48 ++++-
drivers/scsi/libsas/sas_ata.c | 358 ++++++++++++++++++++++++++++++++++-
drivers/scsi/libsas/sas_discover.c | 25 +++-
drivers/scsi/libsas/sas_internal.h | 7 +
drivers/scsi/libsas/sas_phy.c | 1 +
drivers/scsi/libsas/sas_port.c | 12 ++
drivers/scsi/libsas/sas_scsi_host.c | 12 +-
include/scsi/libsas.h | 13 ++-
include/scsi/sas_ata.h | 5 +
9 files changed, 458 insertions(+), 23 deletions(-)

diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index ef8567d..969b3bb 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -4091,7 +4091,8 @@ int ata_sas_port_start(struct ata_port *ap)
*/
if (!ap->ops->error_handler)
ap->pflags &= ~ATA_PFLAG_FROZEN;
- return 0;
+
+ return ata_tport_add(ap->dev, ap);
}
EXPORT_SYMBOL_GPL(ata_sas_port_start);

@@ -4107,6 +4108,15 @@ EXPORT_SYMBOL_GPL(ata_sas_port_start);

void ata_sas_port_stop(struct ata_port *ap)
{
+ int i = 0;
+
+ /* delete pmp link */
+ if (ap->pmp_link) {
+ for (i = 0; i < ap->nr_pmp_links; i++)
+ ata_tlink_delete(&ap->pmp_link[i]);
+ }
+
+ ata_tport_delete(ap);
}
EXPORT_SYMBOL_GPL(ata_sas_port_stop);

@@ -4143,12 +4153,8 @@ EXPORT_SYMBOL_GPL(ata_sas_sync_probe);

int ata_sas_port_init(struct ata_port *ap)
{
- int rc = ap->ops->port_start(ap);
-
- if (rc)
- return rc;
ap->print_id = atomic_inc_return(&ata_print_id);
- return 0;
+ return ap->ops->port_start(ap);
}
EXPORT_SYMBOL_GPL(ata_sas_port_init);

@@ -4166,6 +4172,23 @@ void ata_sas_port_destroy(struct ata_port *ap)
}
EXPORT_SYMBOL_GPL(ata_sas_port_destroy);

+static struct ata_device *ata_sas_find_dev(struct scsi_device *sdev,
+ struct ata_port *ap)
+{
+ int devno = 0;
+
+ if (!sata_pmp_attached(ap)) {
+ if (unlikely(sdev->channel || sdev->lun))
+ return NULL;
+ } else {
+ if (unlikely(sdev->lun))
+ return NULL;
+ devno = sdev->channel;
+ }
+
+ return ata_find_dev(ap, devno);
+}
+
/**
* ata_sas_slave_configure - Default slave_config routine for libata devices
* @sdev: SCSI device to configure
@@ -4177,8 +4200,11 @@ EXPORT_SYMBOL_GPL(ata_sas_port_destroy);

int ata_sas_slave_configure(struct scsi_device *sdev, struct ata_port *ap)
{
+ struct ata_device *dev = ata_sas_find_dev(sdev, ap);
+ BUG_ON(!dev);
+
ata_scsi_sdev_config(sdev);
- ata_scsi_dev_config(sdev, ap->link.device);
+ ata_scsi_dev_config(sdev, dev);
return 0;
}
EXPORT_SYMBOL_GPL(ata_sas_slave_configure);
@@ -4196,11 +4222,15 @@ EXPORT_SYMBOL_GPL(ata_sas_slave_configure);
int ata_sas_queuecmd(struct scsi_cmnd *cmd, struct ata_port *ap)
{
int rc = 0;
+ struct ata_device *dev;
+ struct scsi_device *scsidev = cmd->device;

ata_scsi_dump_cdb(ap, cmd);

- if (likely(ata_dev_enabled(ap->link.device)))
- rc = __ata_scsi_queuecmd(cmd, ap->link.device);
+ dev = ata_sas_find_dev(scsidev, ap);
+
+ if (likely(dev && ata_dev_enabled(dev)))
+ rc = __ata_scsi_queuecmd(cmd, dev);
else {
cmd->result = (DID_BAD_TARGET << 16);
cmd->scsi_done(cmd);
diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c
index 29a19fd..c820042 100644
--- a/drivers/scsi/libsas/sas_ata.c
+++ b/drivers/scsi/libsas/sas_ata.c
@@ -201,6 +201,17 @@ static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc)
task = sas_alloc_task(GFP_ATOMIC);
if (!task)
goto out;
+
+ if (sata_pmp_attached(ap)) {
+ struct ata_device *adev = qc->dev;
+
+ if (!list_empty(&dev->children) &&
+ !ata_is_host_link(adev->link)) {
+ dev = adev->private_data;
+ BUG_ON(!dev);
+ }
+ }
+
task->dev = dev;
task->task_proto = SAS_PROTOCOL_STP;
task->task_done = sas_ata_task_done;
@@ -662,6 +673,31 @@ error:
return ret;
}

+int sas_pm_revalidate_domain(struct domain_device *dev)
+{
+ struct ata_port *ap = NULL;
+ unsigned long flag;
+
+ ap = dev->sata_dev.ap;
+ if (!ap) {
+ SAS_DPRINTK("ap is null.\n");
+ return -1;
+ }
+
+ spin_lock_irqsave(ap->lock, flag);
+
+ /* avoid calling ata_scsi_hotplug function */
+ ap->pflags |= ATA_PFLAG_LOADING;
+
+ ata_port_schedule_eh(ap);
+
+ spin_unlock_irqrestore(ap->lock, flag);
+
+ sas_ata_wait_eh(dev);
+
+ return 0;
+}
+
/*
* notify the lldd to forget the sas_task for this internal ata command
* that bypasses scsi-eh
@@ -801,6 +837,7 @@ int sas_ata_init(struct domain_device *found_dev)
{
struct sas_ha_struct *ha = found_dev->port->ha;
struct Scsi_Host *shost = ha->core.shost;
+ struct sas_internal *i = dev_to_sas_internal(found_dev);
struct ata_port *ap;
int rc;

@@ -816,6 +853,7 @@ int sas_ata_init(struct domain_device *found_dev)
ap->private_data = found_dev;
ap->cbl = ATA_CBL_SATA;
ap->scsi_host = shost;
+
rc = ata_sas_port_init(ap);
if (rc) {
ata_sas_port_destroy(ap);
@@ -823,6 +861,10 @@ int sas_ata_init(struct domain_device *found_dev)
}
found_dev->sata_dev.ap = ap;

+ /* ata port support setting */
+ if (i->dft->lldd_dev_set)
+ i->dft->lldd_dev_set(found_dev);
+
return 0;
}

@@ -868,7 +910,13 @@ static void sas_get_ata_command_set(struct domain_device *dev)
fis->lbal == 0 &&
fis->lbam == 0xCE &&
fis->lbah == 0xAA &&
- (fis->device & ~0x10) == 0))
+ (fis->device & ~0x10) == 0)
+ ||
+ (fis->interrupt_reason == 1 && /* SATA PM */
+ fis->lbal == 1 &&
+ fis->byte_count_low == 0x69 &&
+ fis->byte_count_high == 0x96 &&
+ (fis->device & ~0x10) == 0))

dev->sata_dev.command_set = ATA_COMMAND_SET;

@@ -884,18 +932,293 @@ static void sas_get_ata_command_set(struct domain_device *dev)
fis->lbal == 1 &&
fis->lbam == 0x3C &&
fis->lbah == 0xC3 &&
- fis->device == 0)
- ||
- (fis->interrupt_reason == 1 && /* SATA PM */
- fis->lbal == 1 &&
- fis->byte_count_low == 0x69 &&
- fis->byte_count_high == 0x96 &&
- (fis->device & ~0x10) == 0))
+ fis->device == 0))

/* Treat it as a superset? */
dev->sata_dev.command_set = ATAPI_COMMAND_SET;
}

+/* Support unplug event of PMP attahced device*/
+void sas_ata_detach_dev(struct ata_port *ap, struct ata_device *dev)
+{
+ struct domain_device *parent = ap->private_data;
+ struct domain_device *child = NULL, *n;
+ struct sata_device *sdev = &parent->sata_dev;
+ struct ata_link *link = dev->link;
+ struct ex_phy *ephy = &sdev->ephy[link->pmp];
+
+ list_for_each_entry_safe(child, n, &parent->children, siblings) {
+
+ if (child->sata_dev.port_no == link->pmp) {
+ sas_unregister_dev(parent->port, child);
+
+ memset(ephy->attached_sas_addr, 0, SAS_ADDR_SIZE);
+ if (ephy->port) {
+ sas_port_delete_phy(ephy->port, ephy->phy);
+ sas_device_set_phy(child, ephy->port);
+ if (ephy->port->num_phys == 0)
+ sas_port_delete(ephy->port);
+ ephy->port = NULL;
+ }
+
+ break;
+ }
+ }
+}
+
+static void sas_ata_alloc_sas_phy(struct domain_device *parent,
+ struct ata_device *dev)
+{
+ struct sata_device *sdev = &parent->sata_dev;
+ struct ata_link *link = dev->link;
+ struct ex_phy *ephy = &sdev->ephy[link->pmp];
+ struct sas_rphy *rphy = parent->rphy;
+ struct sas_phy *phy = NULL;
+ unsigned int num_phys = 0, phy_id;
+
+ if (ephy->phy) {
+ SAS_DPRINTK("phy is not null.\n");
+ return;
+ }
+ num_phys = parent->port->ha->num_phys;
+
+ phy_id = num_phys + (parent->phy->number + 1) * link->pmp;
+ ephy->phy = sas_phy_alloc(&rphy->dev, phy_id);
+
+ phy = ephy->phy;
+ BUG_ON(!phy);
+
+ ephy->attached_dev_type = SAS_SATA_DEV;
+ ephy->phy_id = link->pmp;
+ ephy->attached_tproto = parent->tproto;
+ ephy->attached_iproto = parent->iproto;
+
+ phy->identify.device_type = SAS_SATA_DEV;
+ phy->identify.initiator_port_protocols = SAS_PROTOCOL_SATA;
+ phy->identify.target_port_protocols = SAS_PROTOCOL_SATA;
+ phy->identify.phy_identifier = phy_id;
+
+ sas_phy_add(phy);
+}
+
+static void sas_ata_free_sas_phy(struct domain_device *parent,
+ struct ata_device *dev)
+{
+ struct sata_device *sdev = &parent->sata_dev;
+ struct ata_link *link = dev->link;
+ struct ex_phy *ephy = &sdev->ephy[link->pmp];
+ struct sas_phy *phy = ephy->phy;
+
+ sas_phy_delete(phy);
+ sas_phy_free(phy);
+
+ ephy->phy = NULL;
+}
+
+/* alloc domain device for each sata device and insert it into children list */
+static int sas_ata_alloc_ddev(struct domain_device *parent,
+ struct ata_device *dev)
+{
+ struct ata_link *link = dev->link;
+ struct domain_device *child = NULL;
+ struct sata_device *sdev = &parent->sata_dev;
+ struct ex_phy *ephy = &sdev->ephy[link->pmp];
+ struct sas_rphy *rphy = NULL;
+ int ret = -1;
+
+ child = sas_alloc_device();
+ if (!child) {
+ SAS_DPRINTK("can't alloc child device.\n");
+ goto error;
+ }
+
+ kref_get(&parent->kref);
+ child->parent = parent;
+ child->port = parent->port;
+ child->tproto = parent->tproto;
+ child->dev_type = SAS_SATA_DEV;
+ child->sata_dev.port_no = link->pmp;
+ child->sata_dev.command_set = ATA_COMMAND_SET;
+ child->sata_dev.ap = parent->sata_dev.ap;
+
+ if (!ephy->port) {
+ ephy->port = sas_port_alloc(&parent->rphy->dev,
+ ephy->phy->number);
+ if (unlikely(!ephy->port)) {
+ SAS_DPRINTK("fail to alloc sas port.\n");
+ goto free;
+ }
+ if (unlikely(sas_port_add(ephy->port) != 0)) {
+ SAS_DPRINTK("can't add sas port.\n");
+ goto out;
+ }
+ }
+
+ BUG_ON(!ephy->phy);
+
+ sas_port_add_phy(ephy->port, ephy->phy);
+
+ rphy = sas_end_device_alloc(ephy->port);
+ if (!rphy) {
+ SAS_DPRINTK("fail to alloc end device.\n");
+ goto out;
+ }
+
+ rphy->identify.phy_identifier = parent->phy->identify.phy_identifier;
+ memcpy(child->sas_addr, parent->sas_addr, SAS_ADDR_SIZE);
+ sas_fill_in_rphy(child, rphy);
+ sas_hash_addr(child->hashed_sas_addr, child->sas_addr);
+ child->linkrate = parent->linkrate;
+ child->min_linkrate = child->linkrate;
+ child->max_linkrate = child->linkrate;
+ child->pathways = parent->pathways;
+
+ sas_device_set_phy(child, ephy->port);
+
+ child->rphy = rphy;
+ get_device(&child->rphy->dev);
+
+ list_add_tail(&child->siblings, &parent->children);
+ dev->private_data = child;
+
+ return 0;
+out:
+ sas_port_free(ephy->port);
+free:
+ ephy->port = NULL;
+ sas_put_device(child);
+error:
+ SAS_DPRINTK("error exit.\n");
+ return ret;
+}
+
+static void sas_ata_free_ddev(struct domain_device *parent,
+ struct ata_device *dev)
+{
+ struct ata_link *link = dev->link;
+ struct domain_device *child = NULL;
+ struct sata_device *sdev = &parent->sata_dev;
+ struct ex_phy *ephy = &sdev->ephy[link->pmp];
+ struct sas_rphy *rphy = NULL;
+
+ child = dev->private_data;
+ rphy = child->rphy;
+
+ list_del_init(&child->siblings);
+
+ sas_rphy_free(rphy);
+
+ sas_port_delete(ephy->port);
+ ephy->port = NULL;
+
+ sas_put_device(child);
+ dev->private_data = NULL;
+}
+
+static int sas_ata_add_ddev(struct domain_device *parent,
+ struct ata_device *dev)
+{
+ struct ata_link *link = dev->link;
+ struct domain_device *child, *n;
+ struct sas_rphy *rphy = NULL;
+ int ret = 0;
+
+ list_for_each_entry_safe(child, n, &parent->children, siblings)
+ {
+ if (child->sata_dev.port_no != link->pmp)
+ continue;
+
+ ret = sas_notify_lldd_dev_found(child);
+ if (ret) {
+ SAS_DPRINTK(" fail to notify lldd.\n");
+ return ret;
+ }
+ rphy = child->rphy;
+
+ ret = sas_rphy_add(rphy);
+ if (ret) {
+ sas_notify_lldd_dev_gone(child);
+ SAS_DPRINTK("fail to add rphy.\n");
+ return ret;
+ }
+
+ spin_lock_irq(&parent->port->dev_list_lock);
+ list_add_tail(&child->dev_list_node, &parent->port->dev_list);
+ spin_unlock_irq(&parent->port->dev_list_lock);
+ }
+
+ return ret;
+}
+
+void sas_ata_scan_host(struct ata_port *ap)
+{
+ struct ata_link *link;
+ struct ata_device *dev;
+ struct domain_device *parent = ap->private_data;
+ struct sas_port *port = NULL;
+ struct sata_device *sdev = NULL;
+ int ret = 0;
+
+ if (!sata_pmp_attached(ap)) {
+ SAS_DPRINTK("ap is not pmp attached.\n");
+ return;
+ }
+
+ port = parent->port->port;
+ if (unlikely(!port->rphy)) {
+ ret = sas_rphy_add(parent->rphy);
+ if (ret) {
+ SAS_DPRINTK("fail to add rphy .\n");
+ sas_fail_probe(parent, __func__, ret);
+ return;
+ }
+ list_del_init(&parent->disco_list_node);
+ }
+
+ sdev = &parent->sata_dev;
+
+ if (unlikely(!sdev->ephy)) {
+ sdev->ephy = kzalloc(sizeof(*sdev->ephy) * ap->nr_pmp_links,
+ GFP_KERNEL);
+ if (!sdev->ephy) {
+ SAS_DPRINTK("failed to alloc ex_phy.\n");
+ sas_rphy_delete(parent->rphy);
+ sas_fail_probe(parent, __func__, -ENOMEM);
+ return;
+ }
+ }
+
+ ata_for_each_link(link, ap, EDGE) {
+ ata_for_each_dev(dev, link, ALL) {
+
+ if (dev->flags & ATA_DFLAG_DETACHED) {
+ SAS_DPRINTK("unplug device.\n");
+ sas_ata_detach_dev(ap, dev);
+ continue;
+ }
+
+ if (dev->sdev || !ata_dev_enabled(dev))
+ continue;
+
+ sas_ata_alloc_sas_phy(parent, dev);
+
+ ret = sas_ata_alloc_ddev(parent, dev);
+ if (ret) {
+ SAS_DPRINTK("fail to alloc child dev.\n");
+ sas_ata_free_sas_phy(parent, dev);
+ continue;
+ }
+
+ ret = sas_ata_add_ddev(parent, dev);
+ if (ret) {
+ SAS_DPRINTK("fail to add sas dev.\n");
+ sas_ata_free_ddev(parent, dev);
+ sas_ata_free_sas_phy(parent, dev);
+ }
+ }
+ }
+}
+
void sas_probe_sata(struct asd_sas_port *port)
{
struct domain_device *dev, *n;
@@ -996,8 +1319,16 @@ int sas_discover_sata(struct domain_device *dev)
{
int res;

- if (dev->dev_type == SAS_SATA_PM)
- return -ENODEV;
+ if (dev->dev_type == SAS_SATA_PM) {
+ struct ata_port *ap = dev->sata_dev.ap;
+
+ if (unlikely(!ap))
+ BUG();
+
+ /* if lldd do not support pmp, end discover */
+ if (!(ap->flags & ATA_FLAG_PMP))
+ return -ENODEV;
+ }

sas_get_ata_command_set(dev);
sas_fill_in_rphy(dev, dev->rphy);
@@ -1047,6 +1378,10 @@ void sas_ata_strategy_handler(struct Scsi_Host *shost)
if (!dev_is_sata(dev))
continue;

+ if (dev->parent &&
+ (dev->parent->dev_type == SAS_SATA_PM))
+ continue;
+
/* hold a reference over eh since we may be
* racing with final remove once all commands
* are completed
@@ -1136,4 +1471,7 @@ void sas_ata_wait_eh(struct domain_device *dev)

ap = dev->sata_dev.ap;
ata_port_wait_eh(ap);
+
+ /* add sata device that is attached to PM */
+ sas_ata_scan_host(ap);
}
diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c
index 62b58d3..6d4b0bd 100644
--- a/drivers/scsi/libsas/sas_discover.c
+++ b/drivers/scsi/libsas/sas_discover.c
@@ -42,6 +42,9 @@ void sas_init_dev(struct domain_device *dev)
case SAS_END_DEVICE:
INIT_LIST_HEAD(&dev->ssp_dev.eh_list_node);
break;
+ case SAS_SATA_PM:
+ INIT_LIST_HEAD(&dev->children);
+ break;
case SAS_EDGE_EXPANDER_DEVICE:
case SAS_FANOUT_EXPANDER_DEVICE:
INIT_LIST_HEAD(&dev->ex_dev.children);
@@ -110,6 +113,7 @@ static int sas_get_port_device(struct asd_sas_port *port)
dev->port = port;
switch (dev->dev_type) {
case SAS_SATA_DEV:
+ case SAS_SATA_PM:
rc = sas_ata_init(dev);
if (rc) {
rphy = NULL;
@@ -319,6 +323,14 @@ void sas_free_device(struct kref *kref)
kfree(dev->ex_dev.ex_phy);

if (dev_is_sata(dev) && dev->sata_dev.ap) {
+ /* PMP attached device */
+ if (dev->parent) {
+ kfree(dev);
+ return;
+ }
+
+ kfree(dev->sata_dev.ephy);
+
ata_sas_port_destroy(dev->sata_dev.ap);
dev->sata_dev.ap = NULL;
}
@@ -500,6 +512,7 @@ static void sas_revalidate_domain(struct work_struct *work)
struct sas_discovery_event *ev = to_sas_discovery_event(work);
struct asd_sas_port *port = ev->port;
struct sas_ha_struct *ha = port->ha;
+ struct domain_device *dev = NULL;

/* prevent revalidation from finding sata links in recovery */
mutex_lock(&ha->disco_mutex);
@@ -514,8 +527,16 @@ static void sas_revalidate_domain(struct work_struct *work)
SAS_DPRINTK("REVALIDATING DOMAIN on port %d, pid:%d\n", port->id,
task_pid_nr(current));

- if (port->port_dev)
- res = sas_ex_revalidate_domain(port->port_dev);
+ if (port->port_dev) {
+ dev = port->port_dev;
+ /* SATA port multiplier hotplug */
+ if (dev->dev_type == SAS_SATA_PM) {
+ mutex_unlock(&ha->disco_mutex);
+ res = sas_pm_revalidate_domain(dev);
+ mutex_lock(&ha->disco_mutex);
+ } else
+ res = sas_ex_revalidate_domain(dev);
+ }

SAS_DPRINTK("done REVALIDATING DOMAIN on port %d, pid:%d, res 0x%x\n",
port->id, task_pid_nr(current), res);
diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h
index 7e7ba83..ba11001 100644
--- a/drivers/scsi/libsas/sas_internal.h
+++ b/drivers/scsi/libsas/sas_internal.h
@@ -77,6 +77,7 @@ void sas_deform_port(struct asd_sas_phy *phy, int gone);

void sas_porte_bytes_dmaed(struct work_struct *work);
void sas_porte_broadcast_rcvd(struct work_struct *work);
+void sas_porte_sntf_rcvd(struct work_struct *work);
void sas_porte_link_reset_err(struct work_struct *work);
void sas_porte_timer_event(struct work_struct *work);
void sas_porte_hard_reset(struct work_struct *work);
@@ -131,6 +132,12 @@ static inline void sas_fill_in_rphy(struct domain_device *dev,
rphy->identify.initiator_port_protocols = dev->iproto;
rphy->identify.target_port_protocols = dev->tproto;
switch (dev->dev_type) {
+ case SAS_SATA_PM:
+ /* SATA port multiplier don't need to scan target in
+ * sas_rphy_add function and need device type when
+ * removing module
+ */
+ rphy->identify.target_port_protocols = SAS_PROTOCOL_NONE;
case SAS_SATA_DEV:
/* FIXME: need sata device type */
case SAS_END_DEVICE:
diff --git a/drivers/scsi/libsas/sas_phy.c b/drivers/scsi/libsas/sas_phy.c
index cdee446..ade4ff7 100644
--- a/drivers/scsi/libsas/sas_phy.c
+++ b/drivers/scsi/libsas/sas_phy.c
@@ -134,6 +134,7 @@ int sas_register_phys(struct sas_ha_struct *sas_ha)
[PORTE_LINK_RESET_ERR] = sas_porte_link_reset_err,
[PORTE_TIMER_EVENT] = sas_porte_timer_event,
[PORTE_HARD_RESET] = sas_porte_hard_reset,
+ [PORTE_SNTF_RCVD] = sas_porte_sntf_rcvd,
};

/* Now register the phys. */
diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c
index d3c5297..3a4c514 100644
--- a/drivers/scsi/libsas/sas_port.c
+++ b/drivers/scsi/libsas/sas_port.c
@@ -266,6 +266,18 @@ void sas_porte_bytes_dmaed(struct work_struct *work)
sas_form_port(phy);
}

+/* Support SATA port multiplier hotplug event */
+void sas_porte_sntf_rcvd(struct work_struct *work)
+{
+ struct asd_sas_event *ev = to_asd_sas_event(work);
+ struct asd_sas_phy *phy = ev->phy;
+
+ clear_bit(PORTE_SNTF_RCVD, &phy->port_events_pending);
+
+ SAS_DPRINTK("pm sntf received.\n");
+ sas_discover_event(phy->port, DISCE_REVALIDATE_DOMAIN);
+}
+
void sas_porte_broadcast_rcvd(struct work_struct *work)
{
struct asd_sas_event *ev = to_asd_sas_event(work);
diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c
index 25d0f12..0c3b88b 100644
--- a/drivers/scsi/libsas/sas_scsi_host.c
+++ b/drivers/scsi/libsas/sas_scsi_host.c
@@ -916,6 +916,12 @@ int sas_target_alloc(struct scsi_target *starget)

kref_get(&found_dev->kref);
starget->hostdata = found_dev;
+
+ /* find ata device through channel field */
+ if (found_dev->parent &&
+ (found_dev->parent->dev_type == SAS_SATA_PM))
+ starget->channel = found_dev->sata_dev.port_no;
+
return 0;
}

@@ -929,7 +935,11 @@ int sas_slave_configure(struct scsi_device *scsi_dev)
BUG_ON(dev->rphy->identify.device_type != SAS_END_DEVICE);

if (dev_is_sata(dev)) {
- ata_sas_slave_configure(scsi_dev, dev->sata_dev.ap);
+ struct domain_device *parent = dev;
+ if (dev->parent && (dev->parent->dev_type == SAS_SATA_PM))
+ parent = dev->parent;
+
+ ata_sas_slave_configure(scsi_dev, parent->sata_dev.ap);
return 0;
}

diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h
index a26466a..6ba1dd3 100644
--- a/include/scsi/libsas.h
+++ b/include/scsi/libsas.h
@@ -71,7 +71,8 @@ enum port_event {
PORTE_LINK_RESET_ERR = 2,
PORTE_TIMER_EVENT = 3,
PORTE_HARD_RESET = 4,
- PORT_NUM_EVENTS = 5,
+ PORTE_SNTF_RCVD = 5,
+ PORT_NUM_EVENTS = 6,
};

enum phy_event {
@@ -173,6 +174,11 @@ struct sata_device {
struct smp_resp rps_resp; /* report_phy_sata_resp */
u8 port_no; /* port number, if this is a PM (Port) */

+ /* Allocate sas phy and port for each devcie that
+ * is attached to port multiplier.
+ */
+ struct ex_phy *ephy;
+
struct ata_port *ap;
struct ata_host ata_host;
u8 fis[ATA_RESP_FIS_SIZE];
@@ -204,6 +210,7 @@ struct domain_device {

struct domain_device *parent;
struct list_head siblings; /* devices on the same level */
+ struct list_head children;
struct asd_sas_port *port; /* shortcut to root of the tree */
struct sas_phy *phy;

@@ -695,6 +702,9 @@ struct sas_domain_function_template {
void (*lldd_dev_thaw)(struct domain_device *);
int (*lldd_wait_task_done)(struct sas_task *);
int (*lldd_dev_classify)(struct domain_device *);
+
+ /* SATA Port multiplier setting */
+ void (*lldd_dev_set)(struct domain_device *);
};

extern int sas_register_ha(struct sas_ha_struct *);
@@ -725,6 +735,7 @@ int sas_discover_root_expander(struct domain_device *);
void sas_init_ex_attr(void);

int sas_ex_revalidate_domain(struct domain_device *);
+int sas_pm_revalidate_domain(struct domain_device *);

void sas_unregister_domain_devices(struct asd_sas_port *port, int gone);
void sas_init_disc(struct sas_discovery *disc, struct asd_sas_port *);
diff --git a/include/scsi/sas_ata.h b/include/scsi/sas_ata.h
index 00f41ae..9a638c5 100644
--- a/include/scsi/sas_ata.h
+++ b/include/scsi/sas_ata.h
@@ -48,6 +48,7 @@ void sas_probe_sata(struct asd_sas_port *port);
void sas_suspend_sata(struct asd_sas_port *port);
void sas_resume_sata(struct asd_sas_port *port);
void sas_ata_end_eh(struct ata_port *ap);
+void sas_ata_scan_host(struct ata_port *ap);
#else


@@ -100,6 +101,10 @@ static inline int sas_get_ata_info(struct domain_device *dev, struct ex_phy *phy
static inline void sas_ata_end_eh(struct ata_port *ap)
{
}
+
+static inline void sas_ata_scan_host(struct ata_port *ap)
+{
+}
#endif

#endif /* _SAS_ATA_H_ */
--
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/