Commit 3590b827 authored by Mikhail Karpenko's avatar Mikhail Karpenko
Browse files

Remove dev_dbg from IRQ and save state to buffer, read buffer from sysfs

parent c6524ce2
Loading
Loading
Loading
Loading
+66 −23
Original line number Diff line number Diff line
@@ -179,6 +179,7 @@ static void process_timeout(unsigned long data)
	struct elphel_ahci_priv *dpriv = (struct elphel_ahci_priv *)data;

	dev_err(dpriv->dev, "AHCI error: command execution timeout, LBA start = 0x%llx\n", dpriv->lba_ptr.lba_write);
	set_dscope_tstamp(dpriv, TSTMP_CMD_SYS);
	set_flag(dpriv, START_EH);
}

@@ -229,13 +230,28 @@ static irqreturn_t elphel_irq_handler(int irq, void * dev_instance)

		/* handle interrupt from internal command */
		host_irq_stat = readl(hpriv->mmio + HOST_IRQ_STAT);
		if (!host_irq_stat)
		if (!host_irq_stat) {
			dev_err(dpriv->dev, "Spurious IRQ received, host_irq_stat = 0x%x\n", host_irq_stat);
			// debug actions, check it one more time before exit
			host_irq_stat = readl(hpriv->mmio + HOST_IRQ_STAT);
			if (!host_irq_stat) {
				return IRQ_NONE;
		dpriv->flags &= ~IRQ_SIMPLE;
			} else {
				dev_err(dpriv->dev, "Spurious interrupt not confirmed, host_irq_stat = 0x%x, proceed to IRQ processing\n", host_irq_stat);
			}
		}

//		dpriv->flags &= ~IRQ_SIMPLE;
		reset_flag(dpriv, IRQ_SIMPLE);
		irq_stat = readl(port_mmio + PORT_IRQ_STAT);
		serror =  readl(port_mmio + PORT_SCR_ERR);

		dev_dbg(host->dev, "irq_stat = 0x%x, host irq_stat = 0x%x, SErr = 0x%x\n", irq_stat, host_irq_stat, serror);
		dpriv->datascope.reg_stat[REG_HOST_IS] = host_irq_stat;
		dpriv->datascope.reg_stat[REG_PxIS] = irq_stat;
		dpriv->datascope.reg_stat[REG_PxSERR] = serror;
		dpriv->datascope.reg_stat[REG_PxIE] = readl(port_mmio + PORT_IRQ_MASK);
		dpriv->datascope.reg_stat[IRQ_COUNTER] = dpriv->datascope.reg_stat[IRQ_COUNTER] + 1;
//		dev_dbg(host->dev, "irq_stat = 0x%x, host irq_stat = 0x%x, SErr = 0x%x\n", irq_stat, host_irq_stat, serror);

		if (unlikely(irq_stat & PORT_IRQ_ERROR)) {
			dpriv->eh.irq_stat = irq_stat;
@@ -246,12 +262,14 @@ static irqreturn_t elphel_irq_handler(int irq, void * dev_instance)
		writel(irq_stat, port_mmio + PORT_IRQ_STAT);
		writel(host_irq_stat, hpriv->mmio + HOST_IRQ_STAT);
		handled = IRQ_HANDLED;
		set_dscope_tstamp(dpriv, TSTMP_IRQ_MARK_1);

		/* check if this command timed out and charged error handler, do not schedule tasklet in this case as
		 * the processing will restart from error handler
		 */
		if (dpriv->flags & (START_EH | WAIT_EH)) {
			// tell to EH that the command will be processed in normal way
			set_dscope_tstamp(dpriv, TSTMP_IRQ_MARK_1);
			set_flag(dpriv, DELAYED_IRQ_RCVD);
			wake_up_interruptible(&dpriv->eh.wait);
		} else {
@@ -261,6 +279,7 @@ static irqreturn_t elphel_irq_handler(int irq, void * dev_instance)
	} else {

		set_dscope_tstamp(dpriv, TSTMP_IRQ_SYS);
		dpriv->datascope.reg_stat[IRQ_COUNTER_SYS] = dpriv->datascope.reg_stat[IRQ_COUNTER_SYS] + 1;

		/* pass handling to AHCI level and then decide if the resource should be freed */
		handled = ahci_single_irq_intr(irq, dev_instance);
@@ -295,8 +314,8 @@ void process_queue(unsigned long data)
	}

	// check if we can proceed - this was debug feature to fully stop cmd processing
//	if (dpriv->flags & (START_EH | WAIT_EH))
//		return;
	if (dpriv->flags & (START_EH | WAIT_EH))
		return;

	if (process_cmd(dpriv) == 0) {
		finish_cmd(dpriv);
@@ -1255,22 +1274,23 @@ void error_handler(struct elphel_ahci_priv *dpriv)
	rc = wait_event_interruptible_timeout(dpriv->eh.wait, dpriv->flags & DELAYED_IRQ_RCVD, deadline);
	dev_warn(dpriv->dev, "wait_event_interruptible_timeout returned %ld\n", rc);
	if (!rc) {
		/* interrupt has not been received after more than (DEFAULT_CMD_TIMEOUT + WAIT_IRQ_TIMEOUT) ms,
		 * try to recover the link using WAIT_IRQ_TIMEOUT value for next deadline
		 */
		deadline = jiffies + msecs_to_jiffies(WAIT_IRQ_TIMEOUT);
		rc = ap->ops->hardreset(link, &class, deadline);
		if (!rc)
			dev_err(dpriv->dev, "error: ahci_hardreset returned %ld\n", rc);
		ata_port_wait_eh(ap);

		// here we assume that circbuf has not been overrun yet, prepare to repeat current command
		dpriv->lba_ptr.wr_count = 0;
		dpriv->curr_cmd = 0;
		dpriv->max_data_sz = 0;
		dpriv->curr_data_chunk = 0;
		dpriv->curr_data_offset = 0;
		reset_flag(dpriv, IRQ_SIMPLE | DISK_BUSY | PROC_CMD | START_EH);
		return;
//		reset_flag(dpriv, IRQ_SIMPLE | DISK_BUSY | PROC_CMD | START_EH);
//		/* interrupt has not been received after more than (DEFAULT_CMD_TIMEOUT + WAIT_IRQ_TIMEOUT) ms,
//		 * try to recover the link using WAIT_IRQ_TIMEOUT value for next deadline
//		 */
//		deadline = jiffies + msecs_to_jiffies(WAIT_IRQ_TIMEOUT);
//		rc = ap->ops->hardreset(link, &class, deadline);
//		if (!rc)
//			dev_err(dpriv->dev, "error: ahci_hardreset returned %ld\n", rc);
//		ata_port_wait_eh(ap);
//
//		// here we assume that circbuf has not been overrun yet, prepare to repeat current command
//		dpriv->lba_ptr.wr_count = 0;
//		dpriv->curr_cmd = 0;
//		dpriv->max_data_sz = 0;
//		dpriv->curr_data_chunk = 0;
//		dpriv->curr_data_offset = 0;
		dev_dbg(dpriv->dev, "repeat command from slot (head_ptr): %u\n", dpriv->head_ptr);
//		process_cmd(dpriv);
	} else {
@@ -1295,6 +1315,7 @@ void error_handler(struct elphel_ahci_priv *dpriv)
	}
}

/** This function is called during write test only */
static void invalidate_cache(struct fvec *vect)
{
	__cpuc_flush_dcache_area(vect->iov_base, vect->iov_len);
@@ -1546,6 +1567,7 @@ static void elphel_cmd_issue(struct ata_port *ap,///< device port for which the
	dma_sync_single_for_device(ap->dev, pp->cmd_tbl_dma, AHCI_CMD_TBL_AR_SZ, DMA_TO_DEVICE);

	set_dscope_tstamp(dpriv, TSTMP_CMD_DRV);
	dpriv->datascope.reg_stat[CMD_SENT] = dpriv->datascope.reg_stat[CMD_SENT] + 1;

	/* issue command */
	writel(0x11, port_mmio + PORT_CMD);
@@ -1559,6 +1581,8 @@ static int elphel_qc_defer(struct ata_queued_cmd *qc)
	unsigned long irq_flags;
	struct elphel_ahci_priv *dpriv = dev_get_dpriv(qc->ap->dev);

	dev_dbg(qc->ap->dev, "flags: %u\n", dpriv->flags);

	/* First apply the usual rules */
	ret = ata_std_qc_defer(qc);
	if (ret != 0)
@@ -1701,7 +1725,7 @@ static ssize_t io_error_read(struct device *dev, struct device_attribute *attr,
	return sprintf(buff, "%u\n", dpriv->io_error_flag);
}

/** Reset statistics availability flag. This will indicatate that the current samples is processed */
/** Reset statistics availability flag. This will indicate that the current sample is processed */
static ssize_t stat_delay_write(struct device *dev, struct device_attribute *attr, const char *buff, size_t buff_sz)
{
	unsigned long data;
@@ -1729,9 +1753,27 @@ static ssize_t stat_delay_read(struct device *dev, struct device_attribute *attr
	return scnprintf(buff, 20, "%u\n", data);
}

static ssize_t reg_status_read(struct device *dev, struct device_attribute *attr, char *buff)
{
	int i;
	ssize_t cntr;
	unsigned int data;
	struct elphel_ahci_priv *dpriv = dev_get_dpriv(dev);

	for (i = 0, cntr = 0; i < REG_NUM; i++) {
		if ((i % 8) == 0 && i != 0)
			cntr += scnprintf(&buff[cntr], PAGE_SIZE - cntr - 1, "\n");
		cntr += scnprintf(&buff[cntr], PAGE_SIZE - cntr - 1, "0x%08x ", dpriv->datascope.reg_stat[i]);
	}
	cntr += scnprintf(&buff[cntr], PAGE_SIZE - cntr - 1, "\n");

	return cntr;
}

static DEVICE_ATTR(load_module, S_IWUSR | S_IWGRP, NULL, set_load_flag);
static DEVICE_ATTR(io_error, S_IRUSR | S_IRGRP | S_IROTH, io_error_read, NULL);
static DEVICE_ATTR(stat_irq_delay, S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP, stat_delay_read, stat_delay_write);
static DEVICE_ATTR(reg_status, S_IRUSR | S_IRGRP | S_IROTH, reg_status_read, NULL);
static DEVICE_ATTR(SYSFS_AHCI_FNAME_WRITE, S_IWUSR | S_IWGRP, NULL, rawdev_write);
static DEVICE_ATTR(SYSFS_AHCI_FNAME_START, S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP, lba_start_read, lba_start_write);
static DEVICE_ATTR(SYSFS_AHCI_FNAME_END, S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP, lba_end_read, lba_end_write);
@@ -1742,6 +1784,7 @@ static struct attribute *root_dev_attrs[] = {
		&dev_attr_load_module.attr,
		&dev_attr_io_error.attr,
		&dev_attr_stat_irq_delay.attr,
		&dev_attr_reg_status.attr,
		&dev_attr_SYSFS_AHCI_FNAME_WRITE.attr,
		&dev_attr_SYSFS_AHCI_FNAME_START.attr,
		&dev_attr_SYSFS_AHCI_FNAME_END.attr,
+16 −4
Original line number Diff line number Diff line
@@ -63,20 +63,32 @@
#define EXCLUDE_REM               0              ///< Exclude REM buffer from total size calculation
#define SPEED_SAMPLES_NUM         5              ///< Maximum number of samples for disk recording speed measurement
#define DEFAULT_CMD_TIMEOUT       100            ///< Default timeout for commands, in ms
#define WAIT_IRQ_TIMEOUT          1000           ///< Wait delayed interrupt for this amount of time, in ms
#define WAIT_IRQ_TIMEOUT          5000           ///< Wait delayed interrupt for this amount of time, in ms

#define TSTMP_CMD_SYS             1              ///< command issued by system
#define TSTMP_CMD_DRV             2              ///< command issued by driver
#define TSTMP_IRQ_SYS             3              ///< irq processed by system
#define TSTMP_IRQ_DRV             4              ///< irq processed by driver
#define TSTMP_IRQ_MARK_1          5
enum {
        PORT_VS                       = 0x70,        ///< vendor specific port address
        PORT_TIMESTAMP_ADDR           = 0x78         ///< datascope timestamp register
};
enum {
	REG_NUM = 128,                               ///< total number of DWs in status buffer
	REG_HOST_IS = 0,                             ///< value of host IS register in data buffer
	REG_PxIS,                                    ///< value of PxIS in data buffer
	REG_PxIE,                                    ///< value of PxIE in data buffer
	REG_PxSERR,                                  ///< value of PxSERR in data buffer
	IRQ_COUNTER,                                 ///< interrupts counter for internal commands
	IRQ_COUNTER_SYS,                             ///< interrupts counter for system commands
	CMD_SENT                                     ///< number of commands sent
};
struct datascope {
    void __iomem *timestamp_reg;                 ///< register in vendor specific address range (PxVS) where timestamp can be written
    unsigned int cmd_cntr;                       ///< command counter, its value can be found in timestamp (2 bits only)
    unsigned int enable;                         ///< enable/disable timestamps
    uint32_t reg_stat[REG_NUM];                  ///< buffer for registers' status, filled in in IRQ
};

/** Data for error handler */