Tejun, Here's a first cut at this for discussion. You may prefer different names for the invoking functions inside libata-pmp.c, rather than simply pmp_read() and pmp_write(), but I've been up too long and couldn't think of a better name. An alternative to all this, might be to expose the "select_pmp()" function shown in the sample code, and have libata-pmp.c call that, instead of having the new new .pmp_{read,write} functions. What do you think? * * * SATA host controllers which are not purely FIS-based require setup of a special register to select the active pmp for taskfile accesses. This can be done in the low-level driver for regular command issue on a link. But commands directed at a port-multiplier cause problems, because they leave the original link pmp de-selected afterwards. To fix this in a sane fashion, without tons of code duplication from libata into the low-level driver, it is necessary to be able to wrap the sata_pmp_read() and sata_pmp_write() functions. This patch provides two new struct ata_port_operations methods for this, and modifies the code in libata-pmp to use them if provided. In practice, the low-level driver might implement them like this: static int pmp_read(struct ata_link *link, unsigned int reg, u32 *val) { int old = select_pmp(link->ap, SATA_PMP_CTRL_PORT); int rc = sata_pmp_read(link, reg, val); select_pmp(link->ap, old); return rc; } static int pmp_write(struct ata_link *link, unsigned int reg, u32 val) { int old = select_pmp(link->ap, link->pmp); int rc = sata_pmp_write(link, reg, val); select_pmp(link->ap, old); return rc; } Note that "select_pmp" is local to the low-level driver, and is where the chipset pmp-selection register is read/written (the return value is the previous pmp, before writing the new one). Something like this patch is needed for PMP support in sata_mv, and possibly other drivers. Signed-off-by: Mark Lord <mlord@xxxxxxxxx> --- drivers/ata/libata-core.c | 2 ++ drivers/ata/libata-pmp.c | 36 ++++++++++++++++++++++++++---------- include/linux/libata.h | 5 +++++ 3 files changed, 33 insertions(+), 10 deletions(-) diff -u --recursive --new-file --exclude-from=old/Documentation/dontdiff --exclude='*.lds' --exclude-from=old/.gitignore old/drivers/ata/libata-core.c new/drivers/ata/libata-core.c --- old/drivers/ata/libata-core.c 2008-02-21 22:45:38.000000000 -0500 +++ new/drivers/ata/libata-core.c 2008-02-21 22:59:13.000000000 -0500 @@ -7856,6 +7856,8 @@ EXPORT_SYMBOL_GPL(sata_pmp_std_hardreset); EXPORT_SYMBOL_GPL(sata_pmp_std_postreset); EXPORT_SYMBOL_GPL(sata_pmp_do_eh); +EXPORT_SYMBOL_GPL(sata_pmp_read); +EXPORT_SYMBOL_GPL(sata_pmp_write); EXPORT_SYMBOL_GPL(__ata_ehi_push_desc); EXPORT_SYMBOL_GPL(ata_ehi_push_desc); diff -u --recursive --new-file --exclude-from=old/Documentation/dontdiff --exclude='*.lds' --exclude-from=old/.gitignore old/drivers/ata/libata-pmp.c new/drivers/ata/libata-pmp.c --- old/drivers/ata/libata-pmp.c 2008-02-21 22:47:32.000000000 -0500 +++ new/drivers/ata/libata-pmp.c 2008-02-21 22:57:43.000000000 -0500 @@ -25,7 +25,7 @@ * RETURNS: * 0 on success, AC_ERR_* mask on failure. */ -static unsigned int sata_pmp_read(struct ata_link *link, int reg, u32 *r_val) +unsigned int sata_pmp_read(struct ata_link *link, int reg, u32 *r_val) { struct ata_port *ap = link->ap; struct ata_device *pmp_dev = ap->link.device; @@ -48,6 +48,14 @@ return 0; } +static unsigned int pmp_read(struct ata_link *link, int reg, u32 *r_val) +{ + if (link->ap->ops->pmp_read) + return link->ap->ops->pmp_read(link,reg,r_val); + else + return sata_pmp_read(link, reg, r_val); +} + /** * sata_pmp_write - write PMP register * @link: link to write PMP register for @@ -62,7 +70,7 @@ * RETURNS: * 0 on success, AC_ERR_* mask on failure. */ -static unsigned int sata_pmp_write(struct ata_link *link, int reg, u32 val) +unsigned int sata_pmp_write(struct ata_link *link, int reg, u32 val) { struct ata_port *ap = link->ap; struct ata_device *pmp_dev = ap->link.device; @@ -83,6 +91,14 @@ SATA_PMP_SCR_TIMEOUT); } +static unsigned int pmp_write(struct ata_link *link, int reg, u32 val) +{ + if (link->ap->ops->pmp_write) + return link->ap->ops->pmp_write(link,reg,val); + else + return sata_pmp_write(link, reg, val); +} + /** * sata_pmp_qc_defer_cmd_switch - qc_defer for command switching PMP * @qc: ATA command in question @@ -135,7 +151,7 @@ if (reg > SATA_PMP_PSCR_CONTROL) return -EINVAL; - err_mask = sata_pmp_read(link, reg, r_val); + err_mask = pmp_read(link, reg, r_val); if (err_mask) { ata_link_printk(link, KERN_WARNING, "failed to read SCR %d " "(Emask=0x%x)\n", reg, err_mask); @@ -166,7 +182,7 @@ if (reg > SATA_PMP_PSCR_CONTROL) return -EINVAL; - err_mask = sata_pmp_write(link, reg, val); + err_mask = pmp_write(link, reg, val); if (err_mask) { ata_link_printk(link, KERN_WARNING, "failed to write SCR %d " "(Emask=0x%x)\n", reg, err_mask); @@ -332,7 +348,7 @@ int reg = gscr_to_read[i]; unsigned int err_mask; - err_mask = sata_pmp_read(dev->link, reg, &gscr[reg]); + err_mask = pmp_read(dev->link, reg, &gscr[reg]); if (err_mask) { ata_dev_printk(dev, KERN_ERR, "failed to read PMP " "GSCR[%d] (Emask=0x%x)\n", reg, err_mask); @@ -375,7 +391,7 @@ dev->flags |= ATA_DFLAG_AN; /* monitor SERR_PHYRDY_CHG on fan-out ports */ - err_mask = sata_pmp_write(dev->link, SATA_PMP_GSCR_ERROR_EN, + err_mask = pmp_write(dev->link, SATA_PMP_GSCR_ERROR_EN, SERR_PHYRDY_CHG); if (err_mask) { rc = -EIO; @@ -387,7 +403,7 @@ if (gscr[SATA_PMP_GSCR_FEAT_EN] & SATA_PMP_FEAT_NOTIFY) { gscr[SATA_PMP_GSCR_FEAT_EN] &= ~SATA_PMP_FEAT_NOTIFY; - err_mask = sata_pmp_write(dev->link, SATA_PMP_GSCR_FEAT_EN, + err_mask = pmp_write(dev->link, SATA_PMP_GSCR_FEAT_EN, gscr[SATA_PMP_GSCR_FEAT_EN]); if (err_mask) { rc = -EIO; @@ -792,7 +808,7 @@ unsigned int err_mask; u32 prod_id; - err_mask = sata_pmp_read(dev->link, SATA_PMP_GSCR_PROD_ID, &prod_id); + err_mask = pmp_read(dev->link, SATA_PMP_GSCR_PROD_ID, &prod_id); if (err_mask) { ata_dev_printk(dev, KERN_ERR, "failed to read PMP product ID " "(Emask=0x%x)\n", err_mask); @@ -1086,7 +1102,7 @@ if (pmp_dev->flags & ATA_DFLAG_AN) { pmp_dev->gscr[SATA_PMP_GSCR_FEAT_EN] |= SATA_PMP_FEAT_NOTIFY; - err_mask = sata_pmp_write(pmp_dev->link, SATA_PMP_GSCR_FEAT_EN, + err_mask = pmp_write(pmp_dev->link, SATA_PMP_GSCR_FEAT_EN, pmp_dev->gscr[SATA_PMP_GSCR_FEAT_EN]); if (err_mask) { ata_dev_printk(pmp_dev, KERN_ERR, "failed to write " @@ -1097,7 +1113,7 @@ } /* check GSCR_ERROR */ - err_mask = sata_pmp_read(pmp_link, SATA_PMP_GSCR_ERROR, &gscr_error); + err_mask = pmp_read(pmp_link, SATA_PMP_GSCR_ERROR, &gscr_error); if (err_mask) { ata_dev_printk(pmp_dev, KERN_ERR, "failed to read " "PMP_GSCR_ERROR (Emask=0x%x)\n", err_mask); diff -u --recursive --new-file --exclude-from=old/Documentation/dontdiff --exclude='*.lds' --exclude-from=old/.gitignore old/include/linux/libata.h new/include/linux/libata.h --- old/include/linux/libata.h 2008-02-21 22:45:39.000000000 -0500 +++ new/include/linux/libata.h 2008-02-21 23:01:14.000000000 -0500 @@ -717,6 +717,9 @@ int (*scr_read) (struct ata_port *ap, unsigned int sc_reg, u32 *val); int (*scr_write) (struct ata_port *ap, unsigned int sc_reg, u32 val); + int (*pmp_read) (struct ata_link *link, unsigned int sc_reg, u32 *val); + int (*pmp_write) (struct ata_link *link, unsigned int sc_reg, u32 val); + int (*port_suspend) (struct ata_port *ap, pm_message_t mesg); int (*port_resume) (struct ata_port *ap); int (*enable_pm) (struct ata_port *ap, enum link_pm policy); @@ -1043,6 +1046,8 @@ 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); +extern unsigned int sata_pmp_read(struct ata_link *link, int reg, u32 *val); +extern unsigned int sata_pmp_write(struct ata_link *link, int reg, u32 val); /* * EH - To unsubscribe from this list: send the line "unsubscribe linux-ide" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html