Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 68140
b: refs/heads/master
c: 3454dc6
h: refs/heads/master
v: v3
  • Loading branch information
Tejun Heo authored and Jeff Garzik committed Oct 12, 2007
1 parent dab189c commit db968c7
Show file tree
Hide file tree
Showing 2 changed files with 207 additions and 22 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 31f88384443b3e0d7e2c9d36a96647b7e82edad3
refs/heads/master: 3454dc6922dc550c0d3ccf292c4e227403b10b6e
227 changes: 206 additions & 21 deletions trunk/drivers/ata/sata_sil24.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
#include <linux/libata.h>

#define DRV_NAME "sata_sil24"
#define DRV_VERSION "1.0"
#define DRV_VERSION "1.1"

/*
* Port request block (PRB) 32 bytes
Expand Down Expand Up @@ -238,7 +238,7 @@ enum {
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_AN,
ATA_FLAG_AN | ATA_FLAG_PMP,
SIL24_COMMON_LFLAGS = ATA_LFLAG_SKIP_D2H_BSY,
SIL24_FLAG_PCIX_IRQ_WOC = (1 << 24), /* IRQ loss errata on PCI-X */

Expand Down Expand Up @@ -330,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);
Expand All @@ -341,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[] = {
Expand Down Expand Up @@ -393,7 +399,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,

Expand All @@ -402,12 +408,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
};

/*
Expand Down Expand Up @@ -521,11 +536,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);
Expand Down Expand Up @@ -640,7 +684,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,
Expand Down Expand Up @@ -708,6 +752,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;
Expand Down Expand Up @@ -751,7 +827,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);
Expand Down Expand Up @@ -780,6 +856,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;
Expand Down Expand Up @@ -807,15 +942,19 @@ 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 */
irq_stat = readl(port + PORT_IRQ_STAT);
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);
Expand Down Expand Up @@ -844,8 +983,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);
Expand All @@ -864,21 +1038,28 @@ 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;
} else
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)
Expand Down Expand Up @@ -971,25 +1152,23 @@ 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)
{
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)
Expand Down Expand Up @@ -1190,6 +1369,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)
Expand Down

0 comments on commit db968c7

Please sign in to comment.