/*
* libata-pmp.c - libata port multiplier support
*
* Copyright (c) 2007 SUSE Linux Products GmbH
* Copyright (c) 2007 Tejun Heo <teheo@suse.de>
*
* This file is released under the GPLv2.
*/
#include <linux/kernel.h>
#include <linux/libata.h>
#include "libata.h"
/**
* sata_pmp_read - read PMP register
* @link: link to read PMP register for
* @reg: register to read
* @r_val: resulting value
*
* Wrapper around ap->ops->pmp_read to make it easier to call and
* nomarlize error return value.
*
* LOCKING:
* Kernel thread context (may sleep).
*
* RETURNS:
* 0 on success, -errno on failure.
*/
static 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;
int rc;
might_sleep();
rc = ap->ops->pmp_read(pmp_dev, link->pmp, reg, r_val);
if (rc)
rc = -EIO;
return rc;
}
/**
* sata_pmp_write - write PMP register
* @link: link to write PMP register for
* @reg: register to write
* @r_val: value to write
*
* Wrapper around ap->ops->pmp_write to make it easier to call
* and nomarlize error return value.
*
* LOCKING:
* Kernel thread context (may sleep).
*
* RETURNS:
* 0 on success, -errno on failure.
*/
static 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;
int rc;
might_sleep();
rc = ap->ops->pmp_write(pmp_dev, link->pmp, reg, val);
if (rc)
rc = -EIO;
return rc;
}
/**
* 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
* @pmp: port multiplier port number
* @reg: register to read
*
* Initialize @tf for PMP read command.
*
* LOCKING:
* None.
*/
void sata_pmp_read_init_tf(struct ata_taskfile *tf,
struct ata_device *dev, int pmp, int reg)
{
ata_tf_init(dev, tf);
tf->command = ATA_CMD_PMP_READ;
tf->protocol = ATA_PROT_NODATA;
tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
tf->feature = reg;
tf->device = pmp;
}
/**
* sata_pmp_read_val - extract PMP read result from TF
* @tf: target TF
*
* Determine PMP read result from @tf.
*
* LOCKING:
* None.
*/
u32 sata_pmp_read_val(const struct ata_taskfile *tf)
{
return tf->nsect