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 <[EMAIL PROTECTED]>
---
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 [EMAIL PROTECTED]
More majordomo info at http://vger.kernel.org/majordomo-info.html