/* * Write a dryice register and wait until it completes. * * This function uses interrupts to determine when the * write has completed. */ static int di_write_wait(struct rtc_drv_data *pdata, u32 val, int reg) { int ret; int rc = 0; /* serialize register writes */ mutex_lock(&pdata->write_mutex); /* enable the write-complete interrupt */ di_int_enable(pdata, DIER_WCIE); pdata->dsr = 0; /* do the register write */ di_write(pdata, val, reg); /* wait for the write to finish */ ret = wait_event_interruptible_timeout(pdata->write_wait, pdata->dsr & (DSR_WCF | DSR_WEF), 1 * HZ); if (ret == 0) dev_warn(&pdata->pdev->dev, "Write-wait timeout\n"); /* check for write error */ if (pdata->dsr & DSR_WEF) { clear_write_error(pdata); rc = -EIO; } mutex_unlock(&pdata->write_mutex); return rc; }
/* * write a dryice register and loop, waiting for it * to complete. use only during driver initialization. * returns 0 on success or 1 on write failure. */ static int di_write_loop(uint32_t val, int reg) { int rc = 0; int cnt; di_debug("FUNC: %s\n", __func__); di_write(val, reg); for (cnt = 0; cnt < DI_WRITE_LOOP_CNT; cnt++) { uint32_t dsr = di_read(DSR); if (dsr & DSR_WEF) { try_to_clear_wef(); rc = 1; } if (dsr & DSR_WCF) break; } di_debug("wait_write_loop looped %d times\n", cnt); if (cnt == DI_WRITE_LOOP_CNT) rc = 1; if (rc) di_warn("DryIce wait_write_done: WRITE ERROR!\n"); return rc; }
/* * disable a dryice interrupt */ static inline void di_int_disable(struct rtc_drv_data *pdata, u32 intr) { unsigned long flags; spin_lock_irqsave(&pdata->irq_lock, flags); di_write(pdata, di_read(pdata, DIER) & ~intr, DIER); spin_unlock_irqrestore(&pdata->irq_lock, flags); }
/* * the write-error flag is something that shouldn't get set * during normal operation. if it's set something is terribly * wrong. the best we can do is try to clear the bit and hope * that dryice will recover. this situation is similar to an * unexpected bus fault in terms of severity. */ static void try_to_clear_wef(void) { int cnt; while (1) { di_write(DSR_WEF, DSR); for (cnt = 0; cnt < DI_WRITE_LOOP_CNT; cnt++) { if ((di_read(DSR) & DSR_WEF) == 0) break; } di_warn("WARNING: DryIce cannot clear DSR_WEF " "(Write Error Flag)!\n"); } }
/* * This function attempts to clear the dryice write-error flag. * * A dryice write error is similar to a bus fault and should not occur in * normal operation. Clearing the flag requires another write, so the root * cause of the problem may need to be fixed before the flag can be cleared. */ static void clear_write_error(struct rtc_drv_data *pdata) { int cnt; dev_warn(&pdata->pdev->dev, "WARNING: Register write error!\n"); for (;;) { /* clear the write error flag */ di_write(pdata, DSR_WEF, DSR); /* wait for it to take effect */ for (cnt = 0; cnt < 100; cnt++) { if ((di_read(pdata, DSR) & DSR_WEF) == 0) return; udelay(10); } dev_err(&pdata->pdev->dev, "ERROR: Cannot clear write-error flag!\n"); } }
void todo_cur(void) { di_debug("FUNC: %s[%d]\n", __func__, todo.cur); switch (TC.action) { case TODO_ACT_WRITE_VAL: di_debug(" TODO_ACT_WRITE_VAL\n"); /* enable the write-completion interrupt */ todo.status = TODO_ST_PEND_WCF; di_write(di_read(DIER) | DIER_WCIE, DIER); di_write(TC.src, TC.dst); break; case TODO_ACT_WRITE_PTR32: di_debug(" TODO_ACT_WRITE_PTR32\n"); /* enable the write-completion interrupt */ todo.status = TODO_ST_PEND_WCF; di_write(di_read(DIER) | DIER_WCIE, DIER); di_write(*(uint32_t *)TC.src, TC.dst); break; case TODO_ACT_WRITE_PTR: { uint8_t *p = (uint8_t *)TC.src; uint32_t val = 0; int num = TC.num; di_debug(" TODO_ACT_WRITE_PTR\n"); while (num--) val = (val << 8) | *p++; /* enable the write-completion interrupt */ todo.status = TODO_ST_PEND_WCF; di_write(di_read(DIER) | DIER_WCIE, DIER); di_write(val, TC.dst); } break; case TODO_ACT_ASSIGN: di_debug(" TODO_ACT_ASSIGN\n"); switch (TC.num) { case 1: *(uint8_t *)TC.dst = TC.src; break; case 2: *(uint16_t *)TC.dst = TC.src; break; case 4: *(uint32_t *)TC.dst = TC.src; break; default: di_warn("Unexpected size in TODO_ACT_ASSIGN\n"); break; } break; case TODO_ACT_WAIT_RKG: di_debug(" TODO_ACT_WAIT_RKG\n"); /* enable the random-key interrupt */ todo.status = TODO_ST_PEND_RKG; di_write(di_read(DIER) | DIER_RKIE, DIER); break; default: di_debug(" TODO_ACT_NOOP\n"); break; } }