implement PMP support, take 5
PATCH 4/4 implement PMP support, take 5
Hello, all.
his is the fourth take of libata-pmp patchset. This patchset contains 8 patches and implements PMP support.
Changes from the last take[L] are.
* PMP error message fixed.
* build fix when PM and/or ACPI is disabled
* ahci/sata_sil24 prep patches moved to misc-updates patchset as requested by Jeff.
Tested extensively with sil3214/32, ICH9R, JMB360/3 combined with sil3726, 4726 and 5744 port multipliers. Disks from all major vendors and an SATAPI device is used as downstream devices. sil5744 and 3726 behave very nicely on all controllers. Hotplugging and suspend/resume work well too. There are some remaining issues.
* When attached to AHCI controller (ICH9R, JMB360/3), NCQ doesn't work. Things look okay as long as only one command is in flight but as soon as the second command is issued (to the same device), hell breaks loose and all sorts of errors occur including device abort, bad PMP number and interface fatal error. This behavior is common across all PMP chips. I've disabled NCQ if PMP is attached to an ahci controller for now.
* Sil4726 is a bit too quirky. Without the first fan-out port occupied, the thing acts really weirdly. As long as the first port is occupied, it works okay. This is reportedly fixed by new firmware. I tried to update the firmware but installing 3124 driver on Windows was just too difficult for me and I gave up after thirty painful minutes. Anyone up for testing?
* Again, Sil4726 doesn't like ahci driver initialization sequence. During driver initialization, the whole controller is reset and the PHY seems to go offline and back online. Sil4726 doesn't like it and goes out for lunch for several secs after that which makes initial probing think that the port is empty. After probing is complete, Sil4726 comes back and gets detected but at that point detection is asynchronous and if boot device is on one of fan-out ports, it can be a problem. Again, this might have been fixed by new firmware. Anyone up for testing?
* The long boot delay on ASUS boards with on-board PMP chips should be fixed by this patchset but I don't have such a board or Sil4723 which is used on those baords, so I'm not sure. Anyone up for testing?
This patchset is against
libata-dev#upstream (d79eb6de54f180dc83b488b20747f0097fde3dea)
+ [1] misc-updates patchset
+ [2] libata-link patchset
+ [3] libata-pmp-prep patchset
Thanks.
--
tejun
[L] http://thread.gmane.org/gmane.linux.ide/20128
[1] http://thread.gmane.org/gmane.linux.ide/20824
[2] http://thread.gmane.org/gmane.linux.ide/20842
[3] http://thread.gmane.org/gmane.linux.ide/20857
PATCH 1/8 libata-pmp: update ata_eh_reset() for PMP
PMP always requires SRST to be enabled. Also, hardreset reports classification code from the first device when PMP is attached, not from the PMP. Update ata_eh_reset() such that followup softreset is performed if the controller is PMP capable and the host link is being reset.
--- drivers/ata/libata-eh.c | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-)
drivers/ata/libata-eh.c +2 -0
@@ -1906,6 +1906,8 @@ static int ata_eh_followup_srst_needed(struct ata_link *link,return 1 ;if (rc !=0 )return 0 ; +if ((link->ap->flags &ATA_FLAG_PMP ) &&ata_is_host_link (link)) +return 1 ;if (classify && !(link->flags &ATA_LFLAG_ASSUME_CLASS ) && classes[0 ] ==ATA_DEV_UNKNOWN )return 1 ;
Signature · 1 line
1.5.0.3
PATCH 2/8 libata-pmp: implement Port Multiplier support
In reply to Tejun HeoImplement Port Multiplier support. To support PMP, a LLDD has to supply ops->pmp_read() and pmp_write(). If non-null, ->pmp_attach and ->pmp_detach are called on PMP attach and detach, respectively.
->pmp_read/write() can be called while the port is frozen, so they must be implemented by polling. This patch supplies several helpers to ease ->pmp_read/write() implementation.
Also, irq_handler and error_handler must be PMP aware. Most of PMP aware EH can be done by calling ata_pmp_do_eh() with appropriate methods. PMP EH uses separate set of reset methods and this patch implements standard prereset, hardreset and postreset methods.
This patch only implements PMP support. The next patch will integrate PMP into the reset of libata and thus enable PMP support.
--- drivers/ata/Makefile | 3 +- drivers/ata/libata-core.c | 11 + drivers/ata/libata-pmp.c | 1157 +++++++++++++++++++++++++++++++++++++++++++++ drivers/ata/libata.h | 5 + include/linux/libata.h | 19 + 5 files changed, 1194 insertions(+), 1 deletions(-) create mode 100644 drivers/ata/libata-pmp.c
drivers/ata/Makefile +2 -1
@@ -68,5 +68,6 @@ obj-$(CONFIG_ATA_GENERIC) += ata_generic.o # Should be last libata driver obj-$(CONFIG_PATA_LEGACY) += pata_legacy.o -libata-objs := libata-core.o libata-scsi.o libata-sff.o libata-eh.o +libata-objs := libata-core.o libata-scsi.o libata-sff.o libata-eh.o \ + libata-pmp.o libata-$(CONFIG_ATA_ACPI) += libata-acpi.o
drivers/ata/libata-core.c +11 -0
@@ -3770,6 +3770,9 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { { "IOMEGA ZIP 250 ATAPI", NULL, ATA_HORKAGE_NODMA }, /* temporary fix */ { "IOMEGA ZIP 250 ATAPI Floppy", NULL, ATA_HORKAGE_NODMA }, +/* Odd clown on sil3726/4726 PMPs */ + { "Config Disk", NULL, ATA_HORKAGE_NODMA | + ATA_HORKAGE_SKIP_PM },/* Weird ATAPI devices */ { "TORiSAN DVD-ROM DRD-N216", NULL, ATA_HORKAGE_MAX_SEC_128 },
@@ -7091,6 +7094,14 @@ EXPORT_SYMBOL_GPL(ata_pci_default_filter);EXPORT_SYMBOL_GPL (ata_pci_clear_simplex);#endif /* CONFIG_PCI */ +EXPORT_SYMBOL_GPL (sata_pmp_read_init_tf); +EXPORT_SYMBOL_GPL (sata_pmp_read_val); +EXPORT_SYMBOL_GPL (sata_pmp_write_init_tf); +EXPORT_SYMBOL_GPL (sata_pmp_std_prereset); +EXPORT_SYMBOL_GPL (sata_pmp_std_hardreset); +EXPORT_SYMBOL_GPL (sata_pmp_std_postreset); +EXPORT_SYMBOL_GPL (sata_pmp_do_eh); +EXPORT_SYMBOL_GPL (__ata_ehi_push_desc);EXPORT_SYMBOL_GPL (ata_ehi_push_desc);EXPORT_SYMBOL_GPL (ata_ehi_clear_desc);
drivers/ata/libata-pmp.c new file +1157 -0
1,157 lines not on this page · raw message
drivers/ata/libata.h +5 -0
@@ -150,6 +150,11 @@ extern void ata_schedule_scsi_eh(struct Scsi_Host *shost);extern void ata_scsi_dev_rescan (struct work_struct *work);extern int ata_bus_probe (struct ata_port *ap); +/* libata-pmp.c */ +extern int sata_pmp_scr_read (struct ata_link *link,int reg,u32 *val); +extern int sata_pmp_scr_write (struct ata_link *link,int reg,u32 val); +extern int sata_pmp_attach (struct ata_device *dev); +/* libata-eh.c */ extern enum scsi_eh_timer_return ata_scsi_timed_out (struct scsi_cmnd *cmd);extern void ata_scsi_error (struct Scsi_Host *host);
include/linux/libata.h +19 -0
@@ -939,6 +939,25 @@ extern unsigned long ata_pci_default_filter(struct ata_device *, unsigned long);#endif /* CONFIG_PCI */ /* +* PMP +*/ +extern void sata_pmp_read_init_tf (struct ata_taskfile *tf, +struct ata_device *dev,int pmp,int reg); +extern u32 sata_pmp_read_val (const struct ata_taskfile *tf); +extern void sata_pmp_write_init_tf (struct ata_taskfile *tf, +struct ata_device *dev, +int pmp,int reg,u32 val); +extern int sata_pmp_std_prereset (struct ata_link *link,unsigned long deadline); +extern int sata_pmp_std_hardreset (struct ata_link *link,unsigned int *class, +unsigned long deadline); +extern void sata_pmp_std_postreset (struct ata_link *link,unsigned int *class); +extern void sata_pmp_do_eh (struct ata_port *ap, +ata_prereset_fn_t prereset,ata_reset_fn_t softreset, +ata_reset_fn_t hardreset,ata_postreset_fn_t postreset, +ata_prereset_fn_t pmp_prereset,ata_reset_fn_t pmp_softreset, +ata_reset_fn_t pmp_hardreset,ata_postreset_fn_t pmp_postreset); + +/* * EH */ extern void ata_eng_timeout (struct ata_port *ap);
Signature · 1 line
1.5.0.3
PATCH 3/8 libata-pmp: hook PMP support and enable it
In reply to Tejun HeoHook PMP support into libata and enable it. Connect SCR and probing functions, and update ata_dev_classify() to detect PMP.
--- drivers/ata/libata-core.c | 113 ++++++++++++++++++++++++++++++++------------- drivers/ata/libata-eh.c | 18 ++++++- 2 files changed, 96 insertions(+), 35 deletions(-)
drivers/ata/libata-core.c +81 -32
@@ -59,7 +59,7 @@#include "libata.h" -#define DRV_VERSION "2.21"/* must be exactly four chars */ +#define DRV_VERSION "3.00"/* must be exactly four chars */ /* debounce timing parameters in msecs { interval, duration, timeout } */
@@ -667,29 +667,49 @@ static unsigned int ata_devchk(struct ata_port *ap, unsigned int device)* None. * * RETURNS: -* Device type, %ATA_DEV_ATA, %ATA_DEV_ATAPI, or %ATA_DEV_UNKNOWN -* the event of failure. +* Device type, %ATA_DEV_ATA, %ATA_DEV_ATAPI, %ATA_DEV_PMP or +* %ATA_DEV_UNKNOWN the event of failure. */ -unsigned int ata_dev_classify (const struct ata_taskfile *tf) {/* Apple's open source Darwin code hints that some devices only * put a proper signature into the LBA mid/high registers, * So, we only check those. It's sufficient for uniqueness. +* +* ATA/ATAPI-7 (d1532v1r1: Feb. 19, 2003) specified separate +* signatures for ATA and ATAPI devices attached on SerialATA, +* 0x3c/0xc3 and 0x69/0x96 respectively. However, SerialATA +* spec has never mentioned about using different signatures +* for ATA/ATAPI devices. Then, Serial ATA II: Port +* Multiplier specification began to use 0x69/0x96 to identify +* port multpliers and 0x3c/0xc3 to identify SEMB device. +* ATA/ATAPI-7 dropped descriptions about 0x3c/0xc3 and +* 0x69/0x96 shortly and described them as reserved for +* SerialATA. +* +* We follow the current spec and consider that 0x69/0x96 +* identifies a port multiplier and 0x3c/0xc3 a SEMB device. */ - -if (((tf->lbam ==0 ) && (tf->lbah ==0 )) || - ((tf->lbam ==0x3c ) && (tf->lbah ==0xc3 ))) { +if ((tf->lbam ==0 ) && (tf->lbah ==0 )) {DPRINTK ("found ATA device by sig\n" );return ATA_DEV_ATA ; } -if (((tf->lbam ==0x14 ) && (tf->lbah ==0xeb )) || - ((tf->lbam ==0x69 ) && (tf->lbah ==0x96 ))) { +if ((tf->lbam ==0x14 ) && (tf->lbah ==0xeb )) {DPRINTK ("found ATAPI device by sig\n" );return ATA_DEV_ATAPI ; } +if ((tf->lbam ==0x69 ) && (tf->lbah ==0x96 )) { +DPRINTK ("found PMP device by sig\n" ); +return ATA_DEV_PMP ; + } + +if ((tf->lbam ==0x3c ) && (tf->lbah ==0xc3 )) { +printk ("ata: SEMB device ignored\n" ); +return ATA_DEV_SEMB_UNSUP ;/* not yet */ + } +DPRINTK ("unknown device\n" );return ATA_DEV_UNKNOWN ; }
@@ -3338,6 +3358,12 @@ int ata_std_prereset(struct ata_link *link, unsigned long deadline) (link->flags & ATA_LFLAG_HRST_TO_RESUME)) ehc->i.action |= ATA_EH_HARDRESET; +/* Some PMPs don't work with only SRST, force hardreset if PMP +* is supported. +*/ +if (ap->flags &ATA_FLAG_PMP ) + ehc->i.action |=ATA_EH_HARDRESET ; +/* if we're about to do hardreset, nothing more to do */ if (ehc->i.action &ATA_EH_HARDRESET )return 0 ;
@@ -3526,6 +3552,16 @@ int sata_std_hardreset(struct ata_link *link, unsigned int *class,/* wait a while before checking status, see SRST for more info */ msleep (150 ); +/* If PMP is supported, we have to do follow-up SRST. Note +* that some PMPs don't send D2H Reg FIS after hardreset at +* all if the first port is empty. Wait for it just for a +* second and request follow-up SRST. +*/ +if (ap->flags &ATA_FLAG_PMP ) { +ata_wait_ready (ap, jiffies +HZ ); +return -EAGAIN ; + } + rc =ata_wait_ready (ap, deadline);/* link occupied, -ENODEV too is an error */ if (rc) {
@@ -5801,22 +5837,26 @@ int sata_scr_valid(struct ata_link *link)* @val: Place to store read value * * Read SCR register @reg of @link into *@val. This function is -* guaranteed to succeed if the cable type of the port is SATA -* and the port implements ->scr_read. +* guaranteed to succeed if @link is ap->link, the cable type of +* the port is SATA and the port implements ->scr_read. * * LOCKING: -* None. +* None if @link is ap->link. Kernel thread context otherwise. * * RETURNS: * 0 on success, negative errno on failure. */ int sata_scr_read (struct ata_link *link,int reg,u32 *val) { -struct ata_port *ap = link->ap; +if (ata_is_host_link (link)) { +struct ata_port *ap = link->ap; -if (sata_scr_valid (link)) -return ap->ops->scr_read (ap, reg, val); -return -EOPNOTSUPP ; +if (sata_scr_valid (link)) +return ap->ops->scr_read (ap, reg, val); +return -EOPNOTSUPP ; + } + +return sata_pmp_scr_read (link, reg, val); }/**
@@ -5826,22 +5866,26 @@ int sata_scr_read(struct ata_link *link, int reg, u32 *val)* @val: value to write * * Write @val to SCR register @reg of @link. This function is -* guaranteed to succeed if the cable type of the port is SATA -* and the port implements ->scr_read. +* guaranteed to succeed if @link is ap->link, the cable type of +* the port is SATA and the port implements ->scr_read. * * LOCKING: -* None. +* None if @link is ap->link. Kernel thread context otherwise. * * RETURNS: * 0 on success, negative errno on failure. */ int sata_scr_write (struct ata_link *link,int reg,u32 val) { -struct ata_port *ap = link->ap; +if (ata_is_host_link (link)) { +struct ata_port *ap = link->ap; -if (sata_scr_valid (link)) -return ap->ops->scr_write (ap, reg, val); -return -EOPNOTSUPP ; +if (sata_scr_valid (link)) +return ap->ops->scr_write (ap, reg, val); +return -EOPNOTSUPP ; + } + +return sata_pmp_scr_write (link, reg, val); }/**
@@ -5854,23 +5898,27 @@ int sata_scr_write(struct ata_link *link, int reg, u32 val)* function performs flush after writing to the register. * * LOCKING: -* None. +* None if @link is ap->link. Kernel thread context otherwise. * * RETURNS: * 0 on success, negative errno on failure. */ int sata_scr_write_flush (struct ata_link *link,int reg,u32 val) { -struct ata_port *ap = link->ap; -int rc; +if (ata_is_host_link (link)) { +struct ata_port *ap = link->ap; +int rc; -if (sata_scr_valid (link)) { - rc = ap->ops->scr_write (ap, reg, val); -if (rc ==0 ) - rc = ap->ops->scr_read (ap, reg, &val); -return rc; +if (sata_scr_valid (link)) { + rc = ap->ops->scr_write (ap, reg, val); +if (rc ==0 ) + rc = ap->ops->scr_read (ap, reg, &val); +return rc; + } +return -EOPNOTSUPP ; } -return -EOPNOTSUPP ; + +return sata_pmp_scr_write (link, reg, val); }/**
@@ -6254,6 +6302,7 @@ static void ata_host_release(struct device *gendev, void *res)if (ap->scsi_host)scsi_host_put (ap->scsi_host); +kfree (ap->pmp_link);kfree (ap); host->ports[i] =NULL ; }
drivers/ata/libata-eh.c +15 -3
@@ -2112,6 +2112,8 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link, readid_flags |=ATA_READID_POSTRESET ;if ((action &ATA_EH_REVALIDATE ) &&ata_dev_enabled (dev)) { +WARN_ON (dev->class ==ATA_DEV_PMP ); +if (ata_link_offline (link)) { rc = -EIO ; goto err;
@@ -2137,8 +2139,11 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link, ata_class_enabled(ehc->classes[dev->devno])) { dev->class = ehc->classes[dev->devno]; - rc =ata_dev_read_id (dev, &dev->class, readid_flags, - dev->id); +if (dev->class ==ATA_DEV_PMP ) + rc =sata_pmp_attach (dev); +else + rc =ata_dev_read_id (dev, &dev->class, + readid_flags, dev->id);switch (rc) {case 0 : new_mask |=1 << dev->devno;
@@ -2167,7 +2172,8 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link,* device detection messages backwards. */ ata_link_for_each_dev (dev, link) { -if (!(new_mask & (1 << dev->devno))) +if (!(new_mask & (1 << dev->devno)) || + dev->class ==ATA_DEV_PMP )continue ; ehc->i.flags |=ATA_EHI_PRINTINFO ;
@@ -2424,6 +2430,12 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,if (rc) goto dev_fail; +/* if PMP got attached, return, pmp EH will take care of it */ +if (link->device->class ==ATA_DEV_PMP ) { + ehc->i.action =0 ; +return 0 ; + } +/* configure transfer mode if necessary */ if (ehc->i.flags &ATA_EHI_SETMODE ) { rc =ata_set_mode (link, &dev);
Signature · 1 line
1.5.0.3
PATCH 4/8 libata-pmp: extend ACPI support to cover PMP
In reply to Tejun HeoExtend ata_acpi_associate_sata_port() such that it can handle PMP and call it when PMP is attached and detached.
Build breakage when !CONFIG_ATA_ACPI was spotted and fixed by Petr Vandrovec.
--- drivers/ata/libata-acpi.c | 36 ++++++++++++++++++++++++++++++++---- drivers/ata/libata-pmp.c | 4 ++++ drivers/ata/libata.h | 2 ++ 3 files changed, 38 insertions(+), 4 deletions(-)
drivers/ata/libata-acpi.c +32 -4
@@ -40,12 +40,40 @@ static int is_pci_dev(struct device *dev)return (dev->bus == &pci_bus_type); } -static void ata_acpi_associate_sata_port (struct ata_port *ap) +/** +* ata_acpi_associate_sata_port - associate SATA port with ACPI objects +* @ap: target SATA port +* +* Look up ACPI objects associated with @ap and initialize acpi_handle +* fields of @ap, the port and devices accordingly. +* +* LOCKING: +* EH context. +* +* RETURNS: +* 0 on success, -errno on failure. +*/ +void ata_acpi_associate_sata_port (struct ata_port *ap) { -acpi_integer adr =SATA_ADR (ap->port_no,NO_PORT_MULT ); +WARN_ON (!(ap->flags &ATA_FLAG_ACPI_SATA )); + +if (!ap->nr_pmp_links) { +acpi_integer adr =SATA_ADR (ap->port_no,NO_PORT_MULT ); + + ap->link.device->acpi_handle = +acpi_get_child (ap->host->acpi_handle, adr); + }else { +struct ata_link *link; + + ap->link.device->acpi_handle =NULL ; - ap->link.device->acpi_handle = -acpi_get_child (ap->host->acpi_handle, adr); +ata_port_for_each_link (link, ap) { +acpi_integer adr =SATA_ADR (ap->port_no, link->pmp); + + link->device->acpi_handle = +acpi_get_child (ap->host->acpi_handle, adr); + } + } }static void ata_acpi_associate_ide_port (struct ata_port *ap)
drivers/ata/libata-pmp.c +4 -0
@@ -595,6 +595,8 @@ int sata_pmp_attach(struct ata_device *dev)ata_port_for_each_link (tlink, ap)sata_link_init_spd (tlink); +ata_acpi_associate_sata_port (ap); +return 0 ; fail:
@@ -634,6 +636,8 @@ static void sata_pmp_detach(struct ata_device *dev) ap->nr_pmp_links =0 ; link->pmp =0 ;spin_unlock_irqrestore (ap->lock, flags); + +ata_acpi_associate_sata_port (ap); }/**
drivers/ata/libata.h +2 -0
@@ -101,11 +101,13 @@ extern struct ata_port *ata_port_alloc(struct ata_host *host);/* libata-acpi.c */ #ifdef CONFIG_ATA_ACPI +extern void ata_acpi_associate_sata_port (struct ata_port *ap);extern void ata_acpi_associate (struct ata_host *host);extern int ata_acpi_on_suspend (struct ata_port *ap);extern void ata_acpi_on_resume (struct ata_port *ap);extern int ata_acpi_on_devcfg (struct ata_device *adev);#else +static inline void ata_acpi_associate_sata_port (struct ata_port *ap) { }static inline void ata_acpi_associate (struct ata_host *host) { }static inline int ata_acpi_on_suspend (struct ata_port *ap) {return 0 ; }static inline void ata_acpi_on_resume (struct ata_port *ap) { }
Signature · 1 line
1.5.0.3
PATCH 5/8 libata-pmp: implement qc_defer for command switching PMP support
In reply to Tejun HeoImplement sata_pmp_qc_defer_cmd_switch() - standard qc_defer for command switching PMP support.
--- drivers/ata/libata-core.c | 1 + drivers/ata/libata-pmp.c | 30 ++++++++++++++++++++++++++++++ include/linux/libata.h | 1 + 3 files changed, 32 insertions(+), 0 deletions(-)
drivers/ata/libata-core.c +1 -0
@@ -7143,6 +7143,7 @@ EXPORT_SYMBOL_GPL(ata_pci_default_filter);EXPORT_SYMBOL_GPL (ata_pci_clear_simplex);#endif /* CONFIG_PCI */ +EXPORT_SYMBOL_GPL (sata_pmp_qc_defer_cmd_switch);EXPORT_SYMBOL_GPL (sata_pmp_read_init_tf);EXPORT_SYMBOL_GPL (sata_pmp_read_val);EXPORT_SYMBOL_GPL (sata_pmp_write_init_tf);
drivers/ata/libata-pmp.c +30 -0
@@ -70,6 +70,36 @@ static int sata_pmp_write(struct ata_link *link, int reg, u32 val) }/** +* sata_pmp_qc_defer_cmd_switch - qc_defer for command switching PMP +* @qc: ATA command in question +* +* A host which has command switching PMP support cannot issue +* commands to multiple links simultaneously. +* +* LOCKING: +* spin_lock_irqsave(host lock) +* +* RETURNS: +* ATA_DEFER_* if deferring is needed, 0 otherwise. +*/ +int sata_pmp_qc_defer_cmd_switch (struct ata_queued_cmd *qc) +{ +struct ata_link *link = qc->dev->link; +struct ata_port *ap = link->ap; + +if (ap->excl_link ==NULL || ap->excl_link == link) { +if (ap->nr_active_links ==0 ||ata_link_active (link)) { + qc->flags |=ATA_QCFLAG_CLEAR_EXCL ; +return ata_std_qc_defer (qc); + } + + ap->excl_link = link; + } + +return ATA_DEFER_PORT ; +} + +/** * sata_pmp_read_init_tf - initialize TF for PMP read * @tf: taskfile to initialize * @dev: PMP dev
include/linux/libata.h +1 -0
@@ -941,6 +941,7 @@ extern unsigned long ata_pci_default_filter(struct ata_device *, unsigned long);/* * PMP */ +extern int sata_pmp_qc_defer_cmd_switch (struct ata_queued_cmd *qc);extern void sata_pmp_read_init_tf (struct ata_taskfile *tf,struct ata_device *dev,int pmp,int reg);extern u32 sata_pmp_read_val (const struct ata_taskfile *tf);
Signature · 1 line
1.5.0.3
PATCH 6/8 sata_sil24: implement PMP support
In reply to Tejun HeoImplement PMP support. sil24 supports full FIS-switching. However, it has a PMP DMA CS errata which requires port-wide resetting if commands are outstanding to three or more devices when an error occurs on one of them.
ATAPI commands often result in CHECK SENSE and it's crucial to not reset them before fetching sense data. Unfortunately, ATAPI CHECK SENSE causes a lot of problem if command is outstanding to any other device usually resulting in port-wide reset. So, sata_sil24 implements sil24_qc_defer() which guarantees ATAPI command is run by itself.
--- drivers/ata/sata_sil24.c | 235 +++++++++++++++++++++++++++++++++++++++++----- 1 files changed, 213 insertions(+), 22 deletions(-)
drivers/ata/sata_sil24.c +213 -22
@@ -30,7 +30,7 @@#include <linux/libata.h> #define DRV_NAME "sata_sil24" -#define DRV_VERSION "0.9" +#define DRV_VERSION "1.0"/* * Port request block (PRB) 32 bytes
@@ -168,7 +168,7 @@ enum {DEF_PORT_IRQ =PORT_IRQ_COMPLETE |PORT_IRQ_ERROR |PORT_IRQ_PHYRDY_CHG |PORT_IRQ_DEV_XCHG | -PORT_IRQ_UNK_FIS , +PORT_IRQ_UNK_FIS |PORT_IRQ_SDB_NOTIFY ,/* bits[27:16] are unmasked (raw) */ PORT_IRQ_RAW_SHIFT = 16,
@@ -237,7 +237,8 @@ enum {/* host flags */ SIL24_COMMON_FLAGS =ATA_FLAG_SATA |ATA_FLAG_NO_LEGACY |ATA_FLAG_MMIO |ATA_FLAG_PIO_DMA | -ATA_FLAG_NCQ |ATA_FLAG_ACPI_SATA , +ATA_FLAG_NCQ |ATA_FLAG_ACPI_SATA | +ATA_FLAG_PMP |ATA_FLAG_SDB_NOTIFY ,SIL24_COMMON_LFLAGS =ATA_LFLAG_SKIP_D2H_BSY , SIL24_FLAG_PCIX_IRQ_WOC = (1 << 24), /* IRQ loss errata on PCI-X */
@@ -329,9 +330,14 @@ static u8 sil24_check_status(struct ata_port *ap);static int sil24_scr_read (struct ata_port *ap,unsigned sc_reg,u32 *val);static int sil24_scr_write (struct ata_port *ap,unsigned sc_reg,u32 val);static void sil24_tf_read (struct ata_port *ap,struct ata_taskfile *tf); +static int sil24_qc_defer (struct ata_queued_cmd *qc);static void sil24_qc_prep (struct ata_queued_cmd *qc);static unsigned int sil24_qc_issue (struct ata_queued_cmd *qc);static void sil24_irq_clear (struct ata_port *ap); +static void sil24_pmp_attach (struct ata_port *ap); +static void sil24_pmp_detach (struct ata_port *ap); +static int sil24_pmp_read (struct ata_device *dev,int pmp,int reg,u32 *r_val); +static int sil24_pmp_write (struct ata_device *dev,int pmp,int reg,u32 val);static void sil24_freeze (struct ata_port *ap);static void sil24_thaw (struct ata_port *ap);static void sil24_error_handler (struct ata_port *ap);
@@ -340,6 +346,7 @@ static int sil24_port_start(struct ata_port *ap);static int sil24_init_one (struct pci_dev *pdev,const struct pci_device_id *ent);#ifdef CONFIG_PM static int sil24_pci_device_resume (struct pci_dev *pdev); +static int sil24_port_resume (struct ata_port *ap);#endif static const struct pci_device_id sil24_pci_tbl[] = {
@@ -394,7 +401,7 @@ static const struct ata_port_operations sil24_ops = { .tf_read = sil24_tf_read, - .qc_defer = ata_std_qc_defer, + .qc_defer = sil24_qc_defer, .qc_prep = sil24_qc_prep, .qc_issue = sil24_qc_issue,
@@ -405,12 +412,21 @@ static const struct ata_port_operations sil24_ops = { .scr_read = sil24_scr_read, .scr_write = sil24_scr_write, + .pmp_attach = sil24_pmp_attach, + .pmp_detach = sil24_pmp_detach, + .pmp_read = sil24_pmp_read, + .pmp_write = sil24_pmp_write, + .freeze = sil24_freeze, .thaw = sil24_thaw, .error_handler = sil24_error_handler, .post_internal_cmd = sil24_post_internal_cmd, .port_start = sil24_port_start, + +#ifdef CONFIG_PM + .port_resume = sil24_port_resume, +#endif };/*
@@ -524,11 +540,40 @@ static void sil24_tf_read(struct ata_port *ap, struct ata_taskfile *tf) *tf = pp->tf; } +static void sil24_config_pmp (struct ata_port *ap,int attached) +{ +void __iomem *port = ap->ioaddr.cmd_addr; + +if (attached) +writel (PORT_CS_PMP_EN , port +PORT_CTRL_STAT ); +else +writel (PORT_CS_PMP_EN , port +PORT_CTRL_CLR ); +} + +static void sil24_clear_pmp (struct ata_port *ap) +{ +void __iomem *port = ap->ioaddr.cmd_addr; +int i; + +writel (PORT_CS_PMP_RESUME , port +PORT_CTRL_CLR ); + +for (i =0 ; i <SATA_PMP_MAX_PORTS ; i++) { +void __iomem *pmp_base = port +PORT_PMP + i *PORT_PMP_SIZE ; + +writel (0 , pmp_base +PORT_PMP_STATUS ); +writel (0 , pmp_base +PORT_PMP_QACTIVE ); + } +} +static int sil24_init_port (struct ata_port *ap) {void __iomem *port = ap->ioaddr.cmd_addr;u32 tmp; +/* clear PMP error status */ +if (ap->nr_pmp_links) +sil24_clear_pmp (ap); +writel (PORT_CS_INIT , port +PORT_CTRL_STAT );ata_wait_register (port +PORT_CTRL_STAT ,PORT_CS_INIT ,PORT_CS_INIT ,10 ,100 );
@@ -643,7 +688,7 @@ static int sil24_do_softreset(struct ata_link *link, unsigned int *class,static int sil24_softreset (struct ata_link *link,unsigned int *class,unsigned long deadline) { -return sil24_do_softreset (link, class,0 , deadline); +return sil24_do_softreset (link, class,SATA_PMP_CTRL_PORT , deadline); } static int sil24_hardreset(struct ata_link *link, unsigned int *class,
@@ -711,6 +756,38 @@ static inline void sil24_fill_sg(struct ata_queued_cmd *qc, } } +static int sil24_qc_defer (struct ata_queued_cmd *qc) +{ +struct ata_link *link = qc->dev->link; +struct ata_port *ap = link->ap; +u8 prot = qc->tf.protocol; +int is_atapi = (prot ==ATA_PROT_ATAPI || + prot ==ATA_PROT_ATAPI_NODATA || + prot ==ATA_PROT_ATAPI_DMA ); + +/* ATAPI commands completing with CHECK_SENSE cause various +* weird problems if other commands are active. PMP DMA CS +* errata doesn't cover all and HSM violation occurs even with +* only one other device active. Always run an ATAPI command +* by itself. +*/ +if (unlikely (ap->excl_link)) { +if (link == ap->excl_link) { +if (ap->nr_active_links) +return ATA_DEFER_PORT ; + qc->flags |=ATA_QCFLAG_CLEAR_EXCL ; + }else +return ATA_DEFER_PORT ; + }else if (unlikely (is_atapi)) { + ap->excl_link = link; +if (ap->nr_active_links) +return ATA_DEFER_PORT ; + qc->flags |=ATA_QCFLAG_CLEAR_EXCL ; + } + +return ata_std_qc_defer (qc); +} +static void sil24_qc_prep (struct ata_queued_cmd *qc) {struct ata_port *ap = qc->ap;
@@ -754,7 +831,7 @@ static void sil24_qc_prep(struct ata_queued_cmd *qc) } prb->ctrl =cpu_to_le16 (ctrl); -ata_tf_to_fis (&qc->tf,0 ,1 , prb->fis); +ata_tf_to_fis (&qc->tf, qc->dev->link->pmp,1 , prb->fis);if (qc->flags &ATA_QCFLAG_DMAMAP )sil24_fill_sg (qc, sge);
@@ -783,6 +860,65 @@ static void sil24_irq_clear(struct ata_port *ap)/* unused */ } +static void sil24_pmp_attach (struct ata_port *ap) +{ +sil24_config_pmp (ap,1 ); +sil24_init_port (ap); +} + +static void sil24_pmp_detach (struct ata_port *ap) +{ +sil24_init_port (ap); +sil24_config_pmp (ap,0 ); +} + +static int sil24_pmp_read (struct ata_device *dev,int pmp,int reg,u32 *r_val) +{ +struct ata_port *ap = dev->link->ap; +struct ata_taskfile tf; +int rc; + +sata_pmp_read_init_tf (&tf, dev, pmp, reg); + rc =sil24_exec_polled_cmd (ap,SATA_PMP_CTRL_PORT , &tf,1 ,0 , +SATA_PMP_SCR_TIMEOUT ); +if (rc ==0 ) { +sil24_read_tf (ap,0 , &tf); + *r_val =sata_pmp_read_val (&tf); + } +return rc; +} + +static int sil24_pmp_write (struct ata_device *dev,int pmp,int reg,u32 val) +{ +struct ata_port *ap = dev->link->ap; +struct ata_taskfile tf; + +sata_pmp_write_init_tf (&tf, dev, pmp, reg, val); +return sil24_exec_polled_cmd (ap,SATA_PMP_CTRL_PORT , &tf,1 ,0 , +SATA_PMP_SCR_TIMEOUT ); +} + +static int sil24_pmp_softreset (struct ata_link *link,unsigned int *class, +unsigned long deadline) +{ +return sil24_do_softreset (link, class, link->pmp, deadline); +} + +static int sil24_pmp_hardreset (struct ata_link *link,unsigned int *class, +unsigned long deadline) +{ +int rc; + + rc =sil24_init_port (link->ap); +if (rc) { +ata_link_printk (link,KERN_ERR , +"hardreset failed (port not ready)\n" ); +return rc; + } + +return sata_pmp_std_hardreset (link, class, deadline); +} +static void sil24_freeze (struct ata_port *ap) {void __iomem *port = ap->ioaddr.cmd_addr;
@@ -810,8 +946,10 @@ static void sil24_error_intr(struct ata_port *ap) {void __iomem *port = ap->ioaddr.cmd_addr;struct sil24_port_priv *pp = ap->private_data; -struct ata_eh_info *ehi = &ap->link.eh_info; -int freeze =0 ; +struct ata_queued_cmd *qc =NULL ; +struct ata_link *link; +struct ata_eh_info *ehi; +int abort =0 , freeze =0 ;u32 irq_stat;/* on error, we need to clear IRQ explicitly */
@@ -819,10 +957,17 @@ static void sil24_error_intr(struct ata_port *ap)writel (irq_stat, port +PORT_IRQ_STAT );/* first, analyze and record host port events */ + link = &ap->link; + ehi = &link->eh_info;ata_ehi_clear_desc (ehi);ata_ehi_push_desc (ehi,"irq_stat 0x%08x" , irq_stat); +if (irq_stat &PORT_IRQ_SDB_NOTIFY ) { +ata_ehi_push_desc (ehi,"SDB notify" ); +sata_async_notification (ap); + } +if (irq_stat & (PORT_IRQ_PHYRDY_CHG |PORT_IRQ_DEV_XCHG )) {ata_ehi_hotplugged (ehi); ata_ehi_push_desc(ehi, "%s",
@@ -842,8 +987,43 @@ static void sil24_error_intr(struct ata_port *ap)if (irq_stat &PORT_IRQ_ERROR ) {struct sil24_cerr_info *ci =NULL ;unsigned int err_mask =0 , action =0 ; -struct ata_queued_cmd *qc; -u32 cerr; +u32 context, cerr; +int pmp; + + abort =1 ; + +/* DMA Context Switch Failure in Port Multiplier Mode +* errata. If we have active commands to 3 or more +* devices, any error condition on active devices can +* corrupt DMA context switching. +*/ +if (ap->nr_active_links >=3 ) { + ehi->err_mask |=AC_ERR_OTHER ; + ehi->action |=ATA_EH_HARDRESET ; +ata_ehi_push_desc (ehi,"PMP DMA CS errata" ); + freeze =1 ; + } + +/* find out the offending link and qc */ +if (ap->nr_pmp_links) { + context =readl (port +PORT_CONTEXT ); + pmp = (context >>5 ) &0xf ; + +if (pmp < ap->nr_pmp_links) { + link = &ap->pmp_link[pmp]; + ehi = &link->eh_info; + qc =ata_qc_from_tag (ap, link->active_tag); + +ata_ehi_clear_desc (ehi); +ata_ehi_push_desc (ehi,"irq_stat 0x%08x" , + irq_stat); + }else { + err_mask |=AC_ERR_HSM ; + action |=ATA_EH_HARDRESET ; + freeze =1 ; + } + }else + qc =ata_qc_from_tag (ap, link->active_tag);/* analyze CMD_ERR */ cerr =readl (port +PORT_CMD_ERR );
@@ -862,7 +1042,6 @@ static void sil24_error_intr(struct ata_port *ap) }/* record error info */ - qc =ata_qc_from_tag (ap, ap->link.active_tag);if (qc) {sil24_read_tf (ap, qc->tag, &pp->tf); qc->err_mask |= err_mask;
@@ -870,13 +1049,21 @@ static void sil24_error_intr(struct ata_port *ap) ehi->err_mask |= err_mask; ehi->action |= action; + +/* if PMP, resume */ +if (ap->nr_pmp_links) +writel (PORT_CS_PMP_RESUME , port +PORT_CTRL_STAT ); }/* freeze or abort */ if (freeze)ata_port_freeze (ap); -else -ata_port_abort (ap); +else if (abort) { +if (qc) +ata_link_abort (qc->dev->link); +else +ata_port_abort (ap); + } }static void sil24_finish_qc (struct ata_queued_cmd *qc)
@@ -961,16 +1148,14 @@ static irqreturn_t sil24_interrupt(int irq, void *dev_instance)static void sil24_error_handler (struct ata_port *ap) { -struct ata_eh_context *ehc = &ap->link.eh_context; - -if (sil24_init_port (ap)) { +if (sil24_init_port (ap))ata_eh_freeze_port (ap); - ehc->i.action |=ATA_EH_HARDRESET ; - }/* perform recovery */ -ata_do_eh (ap, ata_std_prereset, sil24_softreset, sil24_hardreset, - ata_std_postreset); +sata_pmp_do_eh (ap, ata_std_prereset, sil24_softreset, sil24_hardreset, + ata_std_postreset, sata_pmp_std_prereset, + sil24_pmp_softreset, sil24_pmp_hardreset, + sata_pmp_std_postreset); }static void sil24_post_internal_cmd (struct ata_queued_cmd *qc)
@@ -978,8 +1163,8 @@ static void sil24_post_internal_cmd(struct ata_queued_cmd *qc)struct ata_port *ap = qc->ap;/* make DMA engine forget about the failed command */ -if (qc->flags &ATA_QCFLAG_FAILED ) -sil24_init_port (ap); +if ((qc->flags &ATA_QCFLAG_FAILED ) &&sil24_init_port (ap)) +ata_eh_freeze_port (ap); }static int sil24_port_start (struct ata_port *ap)
@@ -1177,6 +1362,12 @@ static int sil24_pci_device_resume(struct pci_dev *pdev) return 0; } + +static int sil24_port_resume (struct ata_port *ap) +{ +sil24_config_pmp (ap, ap->nr_pmp_links); +return 0 ; +}#endif static int __init sil24_init (void )
Signature · 1 line
1.5.0.3
PATCH 7/8 sata_sil24: implement PORT_RST
In reply to Tejun HeoAs DEV_RST (hardreset) sometimes fail to recover the controller (especially after PMP DMA CS errata). In such cases, perform PORT_RST prior to DEV_RST.
--- drivers/ata/sata_sil24.c | 93 ++++++++++++++++++++++++++++++++++----------- 1 files changed, 70 insertions(+), 23 deletions(-)
drivers/ata/sata_sil24.c +70 -23
@@ -323,6 +323,7 @@ struct sil24_port_priv {union sil24_cmd_block *cmd_block;/* 32 cmd blocks */ dma_addr_t cmd_block_dma;/* DMA base addr for them */ struct ata_taskfile tf;/* Cached taskfile registers */ +int do_port_rst; };static void sil24_dev_config (struct ata_device *dev);
@@ -540,6 +541,31 @@ static void sil24_tf_read(struct ata_port *ap, struct ata_taskfile *tf) *tf = pp->tf; } +static void sil24_config_port (struct ata_port *ap) +{ +void __iomem *port = ap->ioaddr.cmd_addr; + +/* configure IRQ WoC */ +if (ap->flags &SIL24_FLAG_PCIX_IRQ_WOC ) +writel (PORT_CS_IRQ_WOC , port +PORT_CTRL_STAT ); +else +writel (PORT_CS_IRQ_WOC , port +PORT_CTRL_CLR ); + +/* zero error counters. */ +writel (0x8000 , port +PORT_DECODE_ERR_THRESH ); +writel (0x8000 , port +PORT_CRC_ERR_THRESH ); +writel (0x8000 , port +PORT_HSHK_ERR_THRESH ); +writel (0x0000 , port +PORT_DECODE_ERR_CNT ); +writel (0x0000 , port +PORT_CRC_ERR_CNT ); +writel (0x0000 , port +PORT_HSHK_ERR_CNT ); + +/* always use 64bit activation */ +writel (PORT_CS_32BIT_ACTV , port +PORT_CTRL_CLR ); + +/* clear port multiplier enable and resume bits */ +writel (PORT_CS_PMP_EN |PORT_CS_PMP_RESUME , port +PORT_CTRL_CLR ); +} +static void sil24_config_pmp (struct ata_port *ap,int attached) {void __iomem *port = ap->ioaddr.cmd_addr;
@@ -568,6 +594,7 @@ static void sil24_clear_pmp(struct ata_port *ap)static int sil24_init_port (struct ata_port *ap) {void __iomem *port = ap->ioaddr.cmd_addr; +struct sil24_port_priv *pp = ap->private_data;u32 tmp;/* clear PMP error status */
@@ -580,8 +607,12 @@ static int sil24_init_port(struct ata_port *ap) tmp =ata_wait_register (port +PORT_CTRL_STAT ,PORT_CS_RDY ,0 ,10 ,100 ); -if ((tmp & (PORT_CS_INIT |PORT_CS_RDY )) !=PORT_CS_RDY ) +if ((tmp & (PORT_CS_INIT |PORT_CS_RDY )) !=PORT_CS_RDY ) { + pp->do_port_rst =1 ; + ap->link.eh_context.i.action |=ATA_EH_HARDRESET ;return -EIO ; + } +return 0 ; }
@@ -696,10 +727,34 @@ static int sil24_hardreset(struct ata_link *link, unsigned int *class, {struct ata_port *ap = link->ap;void __iomem *port = ap->ioaddr.cmd_addr; +struct sil24_port_priv *pp = ap->private_data; +int did_port_rst =0 ;const char *reason;int tout_msec, rc;u32 tmp; + retry: +/* Sometimes, DEV_RST is not enough to recover the controller. +* This happens often after PM DMA CS errata. +*/ +if (pp->do_port_rst) { +ata_port_printk (ap,KERN_WARNING ,"controller in dubious " +"state, performing PORT_RST\n" ); + +writel (PORT_CS_PORT_RST , port +PORT_CTRL_STAT ); +msleep (10 ); +writel (PORT_CS_PORT_RST , port +PORT_CTRL_CLR ); +ata_wait_register (port +PORT_CTRL_STAT ,PORT_CS_RDY ,0 , +10 ,5000 ); + +/* restore port configuration */ +sil24_config_port (ap); +sil24_config_pmp (ap, ap->nr_pmp_links); + + pp->do_port_rst =0 ; + did_port_rst =1 ; + } +/* sil24 does the right thing(tm) without any protection */ sata_set_spd (link);
@@ -736,6 +791,11 @@ static int sil24_hardreset(struct ata_link *link, unsigned int *class,return -EAGAIN ; err: +if (!did_port_rst) { + pp->do_port_rst =1 ; + goto retry; + } +ata_link_printk (link,KERN_ERR ,"hardreset failed (%s)\n" , reason);return -EIO ; }
@@ -1001,6 +1061,7 @@ static void sil24_error_intr(struct ata_port *ap) ehi->err_mask |=AC_ERR_OTHER ; ehi->action |=ATA_EH_HARDRESET ;ata_ehi_push_desc (ehi,"PMP DMA CS errata" ); + pp->do_port_rst =1 ; freeze =1 ; }
@@ -1148,6 +1209,8 @@ static irqreturn_t sil24_interrupt(int irq, void *dev_instance)static void sil24_error_handler (struct ata_port *ap) { +struct sil24_port_priv *pp = ap->private_data; +if (sil24_init_port (ap))ata_eh_freeze_port (ap);
@@ -1156,6 +1219,8 @@ static void sil24_error_handler(struct ata_port *ap) ata_std_postreset, sata_pmp_std_prereset, sil24_pmp_softreset, sil24_pmp_hardreset, sata_pmp_std_postreset); + + pp->do_port_rst =0 ; }static void sil24_post_internal_cmd (struct ata_queued_cmd *qc)
@@ -1202,7 +1267,6 @@ static int sil24_port_start(struct ata_port *ap)static void sil24_init_controller (struct ata_host *host) {void __iomem *host_base = host->iomap[SIL24_HOST_BAR ]; -void __iomem *port_base = host->iomap[SIL24_PORT_BAR ];u32 tmp;int i;
@@ -1214,7 +1278,8 @@ static void sil24_init_controller(struct ata_host *host)/* init ports */ for (i =0 ; i < host->n_ports; i++) { -void __iomem *port = port_base + i *PORT_REGS_SIZE ; +struct ata_port *ap = host->ports[i]; +void __iomem *port = ap->ioaddr.cmd_addr;/* Initial PHY setting */ writel (0x20c , port +PORT_PHY_CFG );
@@ -1231,26 +1296,8 @@ static void sil24_init_controller(struct ata_host *host) "failed to clear port RST\n"); } -/* Configure IRQ WoC */ -if (host->ports[0 ]->flags &SIL24_FLAG_PCIX_IRQ_WOC ) -writel (PORT_CS_IRQ_WOC , port +PORT_CTRL_STAT ); -else -writel (PORT_CS_IRQ_WOC , port +PORT_CTRL_CLR ); - -/* Zero error counters. */ -writel (0x8000 , port +PORT_DECODE_ERR_THRESH ); -writel (0x8000 , port +PORT_CRC_ERR_THRESH ); -writel (0x8000 , port +PORT_HSHK_ERR_THRESH ); -writel (0x0000 , port +PORT_DECODE_ERR_CNT ); -writel (0x0000 , port +PORT_CRC_ERR_CNT ); -writel (0x0000 , port +PORT_HSHK_ERR_CNT ); - -/* Always use 64bit activation */ -writel (PORT_CS_32BIT_ACTV , port +PORT_CTRL_CLR ); - -/* Clear port multiplier enable and resume bits */ -writel (PORT_CS_PMP_EN |PORT_CS_PMP_RESUME , - port +PORT_CTRL_CLR ); +/* configure port */ +sil24_config_port (ap); }/* Turn on interrupts */
Signature · 1 line
1.5.0.3
PATCH 8/8 ahci: implement PMP support
In reply to Tejun HeoImplement AHCI PMP support. ahci only supports command based switching. Also, for some reason, NCQ over PMP doesn't work now. Other than that, everything works.
Tested on ICH9R, JMB360/363 + SIMG3726, 4726 and 5744.
--- drivers/ata/ahci.c | 236 ++++++++++++++++++++++++++++++++++++++++++---------- 1 files changed, 191 insertions(+), 45 deletions(-)
drivers/ata/ahci.c +191 -45
@@ -46,7 +46,7 @@#include <linux/libata.h> #define DRV_NAME "ahci" -#define DRV_VERSION "2.3" +#define DRV_VERSION "3.0"enum {
@@ -97,6 +97,7 @@ enum {/* HOST_CAP bits */ HOST_CAP_SSC = (1 <<14 ),/* Slumber capable */ +HOST_CAP_PMP = (1 <<17 ),/* Port Multiplier support */ HOST_CAP_CLO = (1 <<24 ),/* Command List Override support */ HOST_CAP_SSS = (1 <<27 ),/* Staggered Spin-up */ HOST_CAP_SNTF = (1 << 29), /* SNotification register */
@@ -144,7 +145,8 @@ enum {PORT_IRQ_IF_ERR |PORT_IRQ_CONNECT |PORT_IRQ_PHYRDY | -PORT_IRQ_UNK_FIS , +PORT_IRQ_UNK_FIS | +PORT_IRQ_BAD_PMP ,PORT_IRQ_ERROR =PORT_IRQ_FREEZE |PORT_IRQ_TF_ERR | PORT_IRQ_HBUS_DATA_ERR,
@@ -154,6 +156,7 @@ enum {/* PORT_CMD bits */ PORT_CMD_ATAPI = (1 <<24 ),/* Device is ATAPI */ +PORT_CMD_PMP = (1 <<17 ),/* PMP attached */ PORT_CMD_LIST_ON = (1 <<15 ),/* cmd list DMA engine running */ PORT_CMD_FIS_ON = (1 <<14 ),/* FIS DMA engine running */ PORT_CMD_FIS_RX = (1 << 4), /* Enable FIS receive DMA engine */
@@ -229,6 +232,10 @@ static void ahci_qc_prep(struct ata_queued_cmd *qc);static u8 ahci_check_status (struct ata_port *ap);static void ahci_freeze (struct ata_port *ap);static void ahci_thaw (struct ata_port *ap); +static void ahci_pmp_attach (struct ata_port *ap); +static void ahci_pmp_detach (struct ata_port *ap); +static int ahci_pmp_read (struct ata_device *dev,int pmp,int reg,u32 *r_val); +static int ahci_pmp_write (struct ata_device *dev,int pmp,int reg,u32 val);static void ahci_error_handler (struct ata_port *ap);static void ahci_vt8251_error_handler (struct ata_port *ap);static void ahci_post_internal_cmd (struct ata_queued_cmd *qc);
@@ -270,7 +277,7 @@ static const struct ata_port_operations ahci_ops = { .tf_read = ahci_tf_read, - .qc_defer = ata_std_qc_defer, + .qc_defer = sata_pmp_qc_defer_cmd_switch, .qc_prep = ahci_qc_prep, .qc_issue = ahci_qc_issue,
@@ -287,6 +294,11 @@ static const struct ata_port_operations ahci_ops = { .error_handler = ahci_error_handler, .post_internal_cmd = ahci_post_internal_cmd, + .pmp_attach = ahci_pmp_attach, + .pmp_detach = ahci_pmp_detach, + .pmp_read = ahci_pmp_read, + .pmp_write = ahci_pmp_write, + #ifdef CONFIG_PM .port_suspend = ahci_port_suspend, .port_resume = ahci_port_resume,
@@ -305,7 +317,7 @@ static const struct ata_port_operations ahci_vt8251_ops = { .tf_read = ahci_tf_read, - .qc_defer = ata_std_qc_defer, + .qc_defer = sata_pmp_qc_defer_cmd_switch, .qc_prep = ahci_qc_prep, .qc_issue = ahci_qc_issue,
@@ -322,6 +334,11 @@ static const struct ata_port_operations ahci_vt8251_ops = { .error_handler = ahci_vt8251_error_handler, .post_internal_cmd = ahci_post_internal_cmd, + .pmp_attach = ahci_pmp_attach, + .pmp_detach = ahci_pmp_detach, + .pmp_read = ahci_pmp_read, + .pmp_write = ahci_pmp_write, + #ifdef CONFIG_PM .port_suspend = ahci_port_suspend, .port_resume = ahci_port_resume,
@@ -1118,7 +1135,12 @@ static int ahci_do_softreset(struct ata_link *link, unsigned int *class,static int ahci_softreset (struct ata_link *link,unsigned int *class,unsigned long deadline) { -return ahci_do_softreset (link, class,0 , deadline); +int pmp =0 ; + +if (link->ap->flags &ATA_FLAG_PMP ) + pmp =SATA_PMP_CTRL_PORT ; + +return ahci_do_softreset (link, class, pmp, deadline); } static int ahci_hardreset(struct ata_link *link, unsigned int *class,
@@ -1145,7 +1167,7 @@ static int ahci_hardreset(struct ata_link *link, unsigned int *class,if (rc ==0 &&ata_link_online (link)) *class =ahci_dev_classify (ap); -if (*class ==ATA_DEV_UNKNOWN ) +if (rc != -EAGAIN && *class ==ATA_DEV_UNKNOWN ) *class =ATA_DEV_NONE ;DPRINTK ("EXIT, rc=%d, class=%u\n" , rc, *class);
@@ -1200,6 +1222,12 @@ static void ahci_postreset(struct ata_link *link, unsigned int *class) } } +static int ahci_pmp_softreset (struct ata_link *link,unsigned int *class, +unsigned long deadline) +{ +return ahci_do_softreset (link, class, link->pmp, deadline); +} +static u8 ahci_check_status (struct ata_port *ap) {void __iomem *mmio = ap->ioaddr.cmd_addr;
@@ -1258,7 +1286,7 @@ static void ahci_qc_prep(struct ata_queued_cmd *qc)*/ cmd_tbl = pp->cmd_tbl + qc->tag *AHCI_CMD_TBL_SZ ; -ata_tf_to_fis (&qc->tf,0 ,1 , cmd_tbl); +ata_tf_to_fis (&qc->tf, qc->dev->link->pmp,1 , cmd_tbl);if (is_atapi) {memset (cmd_tbl +AHCI_CMD_TBL_CDB ,0 ,32 );memcpy (cmd_tbl +AHCI_CMD_TBL_CDB , qc->cdb, qc->dev->cdb_len);
@@ -1271,7 +1299,7 @@ static void ahci_qc_prep(struct ata_queued_cmd *qc)/* * Fill in command slot information. */ - opts = cmd_fis_len | n_elem <<16 ; + opts = cmd_fis_len | n_elem <<16 | (qc->dev->link->pmp <<12 );if (qc->tf.flags &ATA_TFLAG_WRITE ) opts |=AHCI_CMD_WRITE ;if (is_atapi)
@@ -1283,65 +1311,85 @@ static void ahci_qc_prep(struct ata_queued_cmd *qc)static void ahci_error_intr (struct ata_port *ap,u32 irq_stat) {struct ahci_port_priv *pp = ap->private_data; -struct ata_eh_info *ehi = &ap->link.eh_info; -unsigned int err_mask =0 , action =0 ; -struct ata_queued_cmd *qc; +struct ata_eh_info *host_ehi = &ap->link.eh_info; +struct ata_link *link =NULL ; +struct ata_queued_cmd *active_qc; +struct ata_eh_info *active_ehi;u32 serror; -ata_ehi_clear_desc (ehi); +/* determine active link */ +ata_port_for_each_link (link, ap) +if (ata_link_active (link)) +break ; +if (!link) + link = &ap->link; + + active_qc =ata_qc_from_tag (ap, link->active_tag); + active_ehi = &link->eh_info; + +/* record irq stat */ +ata_ehi_clear_desc (host_ehi); +ata_ehi_push_desc (host_ehi,"irq_stat 0x%08x " , irq_stat);/* AHCI needs SError cleared; otherwise, it might lock up */ ahci_scr_read (ap,SCR_ERROR , &serror);ahci_scr_write (ap,SCR_ERROR , serror); - -/* analyze @irq_stat */ -ata_ehi_push_desc (ehi,"irq_stat 0x%08x" , irq_stat); + host_ehi->serror |= serror;/* some controllers set IRQ_IF_ERR on device errors, ignore it */ if (ap->flags &AHCI_FLAG_IGN_IRQ_IF_ERR ) irq_stat &= ~PORT_IRQ_IF_ERR ;if (irq_stat &PORT_IRQ_TF_ERR ) { - err_mask |=AC_ERR_DEV ; +/* If qc is active, charge it; otherwise, the active +* link. There's no active qc on NCQ errors. It will +* be determined by EH by reading log page 10h. +*/ +if (active_qc) + active_qc->err_mask |=AC_ERR_DEV ; +else + active_ehi->err_mask |=AC_ERR_DEV ; +if (ap->flags &AHCI_FLAG_IGN_SERR_INTERNAL ) - serror &= ~SERR_INTERNAL ; + host_ehi->serror &= ~SERR_INTERNAL ; + } + +if (irq_stat &PORT_IRQ_UNK_FIS ) { +u32 *unk = (u32 *)(pp->rx_fis +RX_FIS_UNK ); + + active_ehi->err_mask |=AC_ERR_HSM ; + active_ehi->action |=ATA_EH_SOFTRESET ; +ata_ehi_push_desc (active_ehi, +"<unknown FIS %08x %08x %08x %08x> " , + unk[0 ], unk[1 ], unk[2 ], unk[3 ]); + } + +if (ap->nr_pmp_links && (irq_stat &PORT_IRQ_BAD_PMP )) { + active_ehi->err_mask |=AC_ERR_HSM ; + active_ehi->action |=ATA_EH_SOFTRESET ; +ata_ehi_push_desc (active_ehi,"<incorrect PMP> " ); }if (irq_stat & (PORT_IRQ_HBUS_ERR |PORT_IRQ_HBUS_DATA_ERR )) { - err_mask |=AC_ERR_HOST_BUS ; - action |=ATA_EH_SOFTRESET ; + host_ehi->err_mask |=AC_ERR_HOST_BUS ; + host_ehi->action |=ATA_EH_SOFTRESET ; +ata_ehi_push_desc (host_ehi,"<host bus error> " ); }if (irq_stat &PORT_IRQ_IF_ERR ) { - err_mask |=AC_ERR_ATA_BUS ; - action |=ATA_EH_SOFTRESET ; -ata_ehi_push_desc (ehi,"interface fatal error" ); + host_ehi->err_mask |=AC_ERR_ATA_BUS ; + host_ehi->action |=ATA_EH_SOFTRESET ; +ata_ehi_push_desc (host_ehi,"<interface fatal error> " ); }if (irq_stat & (PORT_IRQ_CONNECT |PORT_IRQ_PHYRDY )) { -ata_ehi_hotplugged (ehi); -ata_ehi_push_desc (ehi,"%s" , irq_stat &PORT_IRQ_CONNECT ? +ata_ehi_hotplugged (host_ehi); +ata_ehi_push_desc (host_ehi,"<%s> " , + irq_stat &PORT_IRQ_CONNECT ?"connection status changed" :"PHY RDY changed" ); } -if (irq_stat &PORT_IRQ_UNK_FIS ) { -u32 *unk = (u32 *)(pp->rx_fis +RX_FIS_UNK ); - - err_mask |=AC_ERR_HSM ; - action |=ATA_EH_SOFTRESET ; -ata_ehi_push_desc (ehi,"unknown FIS %08x %08x %08x %08x" , - unk[0 ], unk[1 ], unk[2 ], unk[3 ]); - } -/* okay, let's hand over to EH */ - ehi->serror |= serror; - ehi->action |= action; - - qc =ata_qc_from_tag (ap, ap->link.active_tag); -if (qc) - qc->err_mask |= err_mask; -else - ehi->err_mask |= err_mask;if (irq_stat &PORT_IRQ_FREEZE )ata_port_freeze (ap);
@@ -1360,6 +1408,9 @@ static void ahci_port_intr(struct ata_port *ap) status =readl (port_mmio +PORT_IRQ_STAT );writel (status, port_mmio +PORT_IRQ_STAT ); +if (status &PORT_IRQ_SDB_FIS ) +sata_async_notification (ap); +if (unlikely (status &PORT_IRQ_ERROR )) {ahci_error_intr (ap, status);return ;
@@ -1531,8 +1582,11 @@ static void ahci_thaw(struct ata_port *ap)writel (tmp, port_mmio +PORT_IRQ_STAT );writel (1 << ap->port_no, mmio +HOST_IRQ_STAT ); -/* turn IRQ back on */ -writel (DEF_PORT_IRQ , port_mmio +PORT_IRQ_MASK ); +/* turn IRQ back on, ignore BAD_PMP if PMP isn't attached */ + tmp =DEF_PORT_IRQ ; +if (!ap->nr_pmp_links) + tmp &= ~PORT_IRQ_BAD_PMP ; +writel (tmp, port_mmio +PORT_IRQ_MASK ); }static void ahci_error_handler (struct ata_port *ap)
@@ -1544,8 +1598,10 @@ static void ahci_error_handler(struct ata_port *ap) }/* perform recovery */ -ata_do_eh (ap, ata_std_prereset, ahci_softreset, ahci_hardreset, - ahci_postreset); +sata_pmp_do_eh (ap, ata_std_prereset, ahci_softreset, + ahci_hardreset, ahci_postreset, + sata_pmp_std_prereset, ahci_pmp_softreset, + sata_pmp_std_hardreset, sata_pmp_std_postreset); }static void ahci_vt8251_error_handler (struct ata_port *ap)
@@ -1570,11 +1626,95 @@ static void ahci_post_internal_cmd(struct ata_queued_cmd *qc)ahci_kick_engine (ap,1 ); } +static void ahci_pmp_attach (struct ata_port *ap) +{ +void __iomem *port_mmio =ahci_port_base (ap); +struct ahci_host_priv *hpriv = ap->host->private_data; +unsigned long flags; +u32 cmd; + + cmd =readl (port_mmio +PORT_CMD ); + cmd |=PORT_CMD_PMP ; +writel (cmd, port_mmio +PORT_CMD ); + +/* FIXME: For some reason, NCQ on PMP doesn't work well on +* ahci. It works as long as only one command is in flight. +* BAD_PMP, IF_ERR and device errors are reported the moment +* another command grows the queue depth. +* +* It's not certain who's to blame yet. The behavior is +* consistent on any combination of ich9r, jmb36x + sil3726, +* 4726 and 5744 + several different NCQ capable drives from +* different vendors. It could be a common flaw in those Port +* Multipliers or a bug in this driver. +*/ +if (hpriv->cap &HOST_CAP_NCQ ) { +spin_lock_irqsave (ap->lock, flags); +ata_port_printk (ap,KERN_INFO , +"ahci: NCQ is currently not supported with PMP\n" ); + ap->flags &= ~ATA_FLAG_NCQ ; +spin_unlock_irqrestore (ap->lock, flags); + } +} + +static void ahci_pmp_detach (struct ata_port *ap) +{ +void __iomem *port_mmio =ahci_port_base (ap); +struct ahci_host_priv *hpriv = ap->host->private_data; +unsigned long flags; +u32 cmd; + + cmd =readl (port_mmio +PORT_CMD ); + cmd &= ~PORT_CMD_PMP ; +writel (cmd, port_mmio +PORT_CMD ); + +if (hpriv->cap &HOST_CAP_NCQ ) { +spin_lock_irqsave (ap->lock, flags); + ap->flags |=ATA_FLAG_NCQ ; +spin_unlock_irqrestore (ap->lock, flags); + } +} + +static int ahci_pmp_read (struct ata_device *dev,int pmp,int reg,u32 *r_val) +{ +struct ata_port *ap = dev->link->ap; +struct ata_taskfile tf; +int rc; + +ahci_kick_engine (ap,0 ); + +sata_pmp_read_init_tf (&tf, dev, pmp, reg); + rc =ahci_exec_polled_cmd (ap,SATA_PMP_CTRL_PORT , &tf,1 ,0 , +SATA_PMP_SCR_TIMEOUT ); +if (rc ==0 ) { +ahci_tf_read (ap, &tf); + *r_val =sata_pmp_read_val (&tf); + } +return rc; +} + +static int ahci_pmp_write (struct ata_device *dev,int pmp,int reg,u32 val) +{ +struct ata_port *ap = dev->link->ap; +struct ata_taskfile tf; + +ahci_kick_engine (ap,0 ); + +sata_pmp_write_init_tf (&tf, dev, pmp, reg, val); +return ahci_exec_polled_cmd (ap,SATA_PMP_CTRL_PORT , &tf,1 ,0 , +SATA_PMP_SCR_TIMEOUT ); +} +static int ahci_port_resume (struct ata_port *ap) {ahci_power_up (ap);ahci_start_port (ap); +if (ap->nr_pmp_links) +ahci_pmp_attach (ap); +else +ahci_pmp_detach (ap); +return 0 ; }
@@ -1849,6 +1989,12 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)if (hpriv->cap &HOST_CAP_NCQ ) pi.flags |=ATA_FLAG_NCQ ; +if (hpriv->cap &HOST_CAP_PMP ) + pi.flags |=ATA_FLAG_PMP ; + +if (hpriv->cap &HOST_CAP_SNTF ) + pi.flags |=ATA_FLAG_SDB_NOTIFY ; + host =ata_host_alloc_pinfo (&pdev->dev, ppi,fls (hpriv->port_map));if (!host)return -ENOMEM ;
Signature · 1 line
1.5.0.3