Loading src/drivers/ata/ahci_elphel.c +66 −23 Original line number Diff line number Diff line Loading @@ -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); } Loading Loading @@ -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; Loading @@ -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 { Loading @@ -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); Loading Loading @@ -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); Loading Loading @@ -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 { Loading @@ -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); Loading Loading @@ -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); Loading @@ -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) Loading Loading @@ -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; Loading Loading @@ -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); Loading @@ -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, Loading src/drivers/ata/ahci_elphel.h +16 −4 Original line number Diff line number Diff line Loading @@ -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 */ Loading Loading
src/drivers/ata/ahci_elphel.c +66 −23 Original line number Diff line number Diff line Loading @@ -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); } Loading Loading @@ -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; Loading @@ -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 { Loading @@ -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); Loading Loading @@ -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); Loading Loading @@ -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 { Loading @@ -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); Loading Loading @@ -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); Loading @@ -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) Loading Loading @@ -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; Loading Loading @@ -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); Loading @@ -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, Loading
src/drivers/ata/ahci_elphel.h +16 −4 Original line number Diff line number Diff line Loading @@ -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 */ Loading