di_return_t dryice_get_tamper_event(uint32_t *events, uint32_t *timestamp,
				    int flags)
{
	di_return_t rc = 0;

	if (di_busy_set())
		return DI_ERR_BUSY;

	if (di_state() == DI_STATE_VALID) {
		rc = DI_ERR_STATE;
		goto err;
	}
	if (events == NULL) {
		rc = DI_ERR_INVAL;
		goto err;
	}
		*events = di_read(DSR) & DSR_TAMPER_BITS;
	if (timestamp) {
		if (di_state() == DI_STATE_NON_VALID)
			*timestamp = di_read(DTCMR);
		else
			*timestamp = 0;
	}
err:
	di_busy_clear();
	return rc;
}
di_return_t dryice_set_random_key(int flags)
{
	uint32_t dcr;
	di_return_t rc = 0;

	if (di_busy_set())
		return DI_ERR_BUSY;

	if (di_state() == DI_STATE_FAILURE) {
		rc = DI_ERR_STATE;
		goto err;
	}
	dcr = di_read(DCR);
	if (dcr & DCR_RKHL) {
		rc = DI_ERR_HLOCK;
		goto err;
	}
	if (dcr & DCR_RKSL) {
		rc = DI_ERR_SLOCK;
		goto err;
	}
	todo_init((flags & DI_FUNC_FLAG_ASYNC) != 0);

	/* clear Random Key Error bit, if set */
	if (di_read(DSR) & DSR_RKE)
		todo_write_val(DSR_RKE, DCR);

	/* load random key */
	todo_write_val(DKCR_LRK, DKCR);

	/* wait for RKV (valid) or RKE (error) */
	todo_wait_rkg();

	if (flags & DI_FUNC_LOCK_FLAGS) {
		dcr = di_read(DCR);
		if (flags & DI_FUNC_FLAG_WRITE_LOCK) {
			if (flags & DI_FUNC_FLAG_HARD_LOCK)
				dcr |= DCR_RKHL;
			else
				dcr |= DCR_RKSL;
		}
		todo_write_val(dcr, DCR);
	}
	todo_start();

	if (flags & DI_FUNC_FLAG_ASYNC)
		return 0;

	rc = todo_wait_done();
err:
	di_busy_clear();
	return rc;
}
di_return_t dryice_get_programmed_key(uint8_t *key_data, int key_bits)
{
	int reg, byte, key_bytes;
	uint32_t dcr, dpkr;
	di_return_t rc = 0;

	if (di_busy_set())
		return DI_ERR_BUSY;

	if (key_data == NULL) {
		rc = DI_ERR_INVAL;
		goto err;
	}
	if (key_bits < 0 || key_bits > MAX_KEY_LEN || key_bits % 8) {
		rc = DI_ERR_INVAL;
		goto err;
	}
	#if 0
	if (!di->key_programmed) {
		rc = DI_ERR_UNSET;
		goto err;
	}
	#endif
	if (di_state() == DI_STATE_FAILURE) {
		rc = DI_ERR_STATE;
		goto err;
	}
	dcr = di_read(DCR);
	if (dcr & DCR_PKRHL) {
		rc = DI_ERR_HLOCK;
		goto err;
	}
	if (dcr & DCR_PKRSL) {
		rc = DI_ERR_SLOCK;
		goto err;
	}
	key_bytes = key_bits / 8;

	/* read key */
	for (reg = 0; reg < MAX_KEY_WORDS; reg++) {
		if (reg < (MAX_KEY_BYTES - key_bytes) / 4)
			continue;
		dpkr = di_read(DPKR7 - reg * 4);

		for (byte = 0; byte < 4; byte++) {
			if (reg * 4 + byte >= MAX_KEY_BYTES - key_bytes) {
				int shift = 24 - byte * 8;
				*key_data++ = (dpkr >> shift) & 0xff;
			}
		}
		dpkr = 0;	/* cleared for security */
	}
/*
 * 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;
}
Пример #5
0
/*
 * set the seconds portion of dryice alarm register
 */
static int dryice_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
	struct rtc_drv_data *pdata = dev_get_drvdata(dev);
	unsigned long now;
	unsigned long alarm_time;
	int rc;

	dev_dbg(dev, "%s\n", __func__);
	rc = rtc_tm_to_time(&alarm->time, &alarm_time);
	if (rc)
		return rc;

	/* don't allow setting alarm in the past */
	now = di_read(pdata, DTCMR);
	if (alarm_time < now)
		return -EINVAL;

	/* write the new alarm time */
	di_write_wait_err(pdata, (u32)alarm_time, DCAMR, rc, err);

	if (alarm->enabled)
		di_int_enable(pdata, DIER_CAIE);  /* enable alarm intr */
	else
		di_int_disable(pdata, DIER_CAIE); /* disable alarm intr */
err:
	return rc;
}
di_return_t dryice_release_programmed_key(void)
{
	uint32_t dcr;
	di_return_t rc = 0;

	if (di_busy_set())
		return DI_ERR_BUSY;

	if (!di->key_programmed) {
		rc = DI_ERR_UNSET;
		goto err;
	}
	dcr = di_read(DCR);
	if (dcr & DCR_PKWHL) {
		rc = DI_ERR_HLOCK;
		goto err;
	}
	if (dcr & DCR_PKWSL) {
		rc = DI_ERR_SLOCK;
		goto err;
	}
	di->key_programmed = 0;

err:
	di_busy_clear();
	return rc;
}
Пример #7
0
/*
 * 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);
}
di_return_t dryice_check_key(di_key_t *key)
{
	uint32_t dksr;
	di_return_t rc = 0;

	if (di_busy_set())
		return DI_ERR_BUSY;

	if (key == NULL) {
		rc = DI_ERR_INVAL;
		goto err;
	}

	dksr = di_read(DKSR);

	if (di_state() != DI_STATE_VALID) {
		dksr = DKSR_IIM_KEY;
		rc = DI_ERR_STATE;
	} else if (dksr == DI_KEY_RK || dksr == DI_KEY_FRK) {
		if (!(di_read(DSR) & DSR_RKV)) {
			dksr = DKSR_IIM_KEY;
			rc = DI_ERR_UNSET;
		}
	}
	switch (dksr) {
	case DKSR_IIM_KEY:
		*key = DI_KEY_FK;
		break;
	case DKSR_PROG_KEY:
		*key = DI_KEY_PK;
		break;
	case DKSR_RAND_KEY:
		*key = DI_KEY_RK;
		break;
	case DKSR_PROG_XOR_IIM_KEY:
		*key = DI_KEY_FPK;
		break;
	case DKSR_RAND_XOR_IIM_KEY:
		*key = DI_KEY_FRK;
		break;
	}
err:
	di_busy_clear();
	return rc;
}
Пример #9
0
/*
 * dryice "normal" interrupt handler
 */
static irqreturn_t dryice_norm_irq(int irq, void *dev_id)
{
	struct rtc_drv_data *pdata = dev_id;
	u32 dsr, dier;
	irqreturn_t rc = IRQ_NONE;

	dier = di_read(pdata, DIER);

	/* handle write complete and write error cases */
	if ((dier & DIER_WCIE)) {
		/*If the write wait queue is empty then there is no pending
		   operations. It means the interrupt is for DryIce -Security.
		   IRQ must be returned as none.*/
		if (list_empty_careful(&pdata->write_wait.task_list))
			return rc;

		/* DSR_WCF clears itself on DSR read */
	    dsr = di_read(pdata, DSR);
		if ((dsr & (DSR_WCF | DSR_WEF))) {
			/* mask the interrupt */
			di_int_disable(pdata, DIER_WCIE);

			/* save the dsr value for the wait queue */
			pdata->dsr |= dsr;

			wake_up_interruptible(&pdata->write_wait);
			rc = IRQ_HANDLED;
		}
	}

	/* handle the alarm case */
	if ((dier & DIER_CAIE)) {
		/* DSR_WCF clears itself on DSR read */
	    dsr = di_read(pdata, DSR);
		if (dsr & DSR_CAF) {
			/* mask the interrupt */
			di_int_disable(pdata, DIER_CAIE);

			/* finish alarm in user context */
			schedule_work(&pdata->work);
			rc = IRQ_HANDLED;
		}
	}
	return rc;
}
Пример #10
0
/*
 * read the seconds portion of the current time from the dryice time counter
 */
static int dryice_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
	struct rtc_drv_data *pdata = dev_get_drvdata(dev);
	unsigned long now;

	dev_dbg(dev, "%s\n", __func__);
	now = di_read(pdata, DTCMR);
	rtc_time_to_tm(now, tm);

	return 0;
}
/*
 * return the current state of dryice
 * (valid, non-valid, or failure)
 */
static enum di_states di_state(void)
{
	enum di_states state = DI_STATE_VALID;
	uint32_t dsr = di_read(DSR);

	if (dsr & DSR_NVF)
		state = DI_STATE_NON_VALID;
	else if (dsr & DSR_SVF)
		state = DI_STATE_FAILURE;

	return state;
}
Пример #12
0
/*
 * read the seconds portion of the alarm register.
 * the fractional part of the alarm register is always zero.
 */
static int dryice_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
	struct rtc_drv_data *pdata = dev_get_drvdata(dev);
	u32 dcamr;

	dev_dbg(dev, "%s\n", __func__);
	dcamr = di_read(pdata, DCAMR);
	rtc_time_to_tm(dcamr, &alarm->time);

	/* alarm is enabled if the interrupt is enabled */
	alarm->enabled = (di_read(pdata, DIER) & DIER_CAIE) != 0;

	/* don't allow the DSR read to mess up DSR_WCF */
	mutex_lock(&pdata->write_mutex);

	/* alarm is pending if the alarm flag is set */
	alarm->pending = (di_read(pdata, DSR) & DSR_CAF) != 0;

	mutex_unlock(&pdata->write_mutex);

	return 0;
}
/*
 * 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");
	}
}
Пример #14
0
/*
 * 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");
	}
}
/*
 * print out the contents of the dryice status register
 * with all the bits decoded
 */
static void show_dsr(const char *heading)
{
	uint32_t dsr = di_read(DSR);

	di_info("%s\n", heading);
	if (dsr & DSR_TAMPER_BITS) {
		if (dsr & DSR_WTD)
			di_info("Wire-mesh Tampering Detected\n");
		if (dsr & DSR_ETBD)
			di_info("External Tampering B Detected\n");
		if (dsr & DSR_ETAD)
			di_info("External Tampering A Detected\n");
		if (dsr & DSR_EBD)
			di_info("External Boot Detected\n");
		if (dsr & DSR_SAD)
			di_info("Security Alarm Detected\n");
		if (dsr & DSR_TTD)
			di_info("Temperature Tampering Detected\n");
		if (dsr & DSR_CTD)
			di_info("Clock Tampering Detected\n");
		if (dsr & DSR_VTD)
			di_info("Voltage Tampering Detected\n");
		if (dsr & DSR_MCO)
			di_info("Monotonic Counter Overflow\n");
		if (dsr & DSR_TCO)
			di_info("Time Counter Overflow\n");
	} else
		di_info("No Tamper Events Detected\n");

	di_info("%d Key Busy Flag\n",           !!(dsr & DSR_KBF));
	di_info("%d Write Busy Flag\n",         !!(dsr & DSR_WBF));
	di_info("%d Write Next Flag\n",         !!(dsr & DSR_WNF));
	di_info("%d Write Complete Flag\n",     !!(dsr & DSR_WCF));
	di_info("%d Write Error Flag\n",        !!(dsr & DSR_WEF));
	di_info("%d Random Key Error\n",        !!(dsr & DSR_RKE));
	di_info("%d Random Key Valid\n",        !!(dsr & DSR_RKV));
	di_info("%d Clock Alarm Flag\n",        !!(dsr & DSR_CAF));
	di_info("%d Non-Valid Flag\n",          !!(dsr & DSR_NVF));
	di_info("%d Security Violation Flag\n", !!(dsr & DSR_SVF));
}
di_return_t dryice_select_key(di_key_t key, int flags)
{
	uint32_t dcr, dksr;
	di_return_t rc = 0;

	if (di_busy_set())
		return DI_ERR_BUSY;

	switch (key) {
	case DI_KEY_FK:
		dksr = DKSR_IIM_KEY;
		break;
	case DI_KEY_PK:
		dksr = DKSR_PROG_KEY;
		break;
	case DI_KEY_RK:
		dksr = DKSR_RAND_KEY;
		break;
	case DI_KEY_FPK:
		dksr = DKSR_PROG_XOR_IIM_KEY;
		break;
	case DI_KEY_FRK:
		dksr = DKSR_RAND_XOR_IIM_KEY;
		break;
	default:
		rc = DI_ERR_INVAL;
		goto err;
	}
	if (di->key_selected) {
		rc = DI_ERR_INUSE;
		goto err;
	}
	if (di_state() != DI_STATE_VALID) {
		rc = DI_ERR_STATE;
		goto err;
	}
	dcr = di_read(DCR);
	if (dcr & DCR_KSHL) {
		rc = DI_ERR_HLOCK;
		goto err;
	}
	if (dcr & DCR_KSSL) {
		rc = DI_ERR_SLOCK;
		goto err;
	}
	todo_init((flags & DI_FUNC_FLAG_ASYNC) != 0);

	/* select key */
	todo_write_val(dksr, DKSR);

	todo_assign(di->key_selected, 1);

	if (flags & DI_FUNC_LOCK_FLAGS) {
		dcr = di_read(DCR);
		if (flags & DI_FUNC_FLAG_WRITE_LOCK) {
			if (flags & DI_FUNC_FLAG_HARD_LOCK)
				dcr |= DCR_KSHL;
			else
				dcr |= DCR_KSSL;
		}
		todo_write_val(dcr, DCR);
	}
	todo_start();

	if (flags & DI_FUNC_FLAG_ASYNC)
		return 0;

	rc = todo_wait_done();
err:
	di_busy_clear();
	return rc;
}
di_return_t dryice_set_programmed_key(const void *key_data, int key_bits,
				      int flags)
{
	uint32_t dcr;
	int key_bytes, reg;
	di_return_t rc = 0;

	if (di_busy_set())
		return DI_ERR_BUSY;

	if (key_data == NULL) {
		rc = DI_ERR_INVAL;
		goto err;
	}
	if (key_bits < 0 || key_bits > MAX_KEY_LEN || key_bits % 8) {
		rc = DI_ERR_INVAL;
		goto err;
	}
	if (flags & DI_FUNC_FLAG_WORD_KEY) {
		if (key_bits % 32 || (uint32_t)key_data & 0x3) {
			rc = DI_ERR_INVAL;
			goto err;
		}
	}
	if (di->key_programmed) {
		rc = DI_ERR_INUSE;
		goto err;
	}
	if (di_state() == DI_STATE_FAILURE) {
		rc = DI_ERR_STATE;
		goto err;
	}
	dcr = di_read(DCR);
	if (dcr & DCR_PKWHL) {
		rc = DI_ERR_HLOCK;
		goto err;
	}
	if (dcr & DCR_PKWSL) {
		rc = DI_ERR_SLOCK;
		goto err;
	}
	key_bytes = key_bits / 8;

	todo_init((flags & DI_FUNC_FLAG_ASYNC) != 0);

	/* accomodate busses that can only do 32-bit transfers */
	if (flags & DI_FUNC_FLAG_WORD_KEY) {
		uint32_t *keyp = (void *)key_data;

		for (reg = 0; reg < MAX_KEY_WORDS; reg++) {
			if (reg < MAX_KEY_WORDS - key_bytes / 4)
				todo_write_val(0, DPKR7 - reg * 4);
			else {
				todo_write_ptr32(keyp, DPKR7 - reg * 4);
				keyp++;
			}
		}
	} else {
		uint8_t *keyp = (void *)key_data;

		for (reg = 0; reg < MAX_KEY_WORDS; reg++) {
			int size = key_bytes - (MAX_KEY_WORDS - reg - 1) * 4;
			if (size <= 0)
				todo_write_val(0, DPKR7 - reg * 4);
			else {
				if (size > 4)
					size = 4;
				todo_write_ptr(keyp, DPKR7 - reg * 4, size);
				keyp += size;
			}
		}
	}
	todo_assign(di->key_programmed, 1);

	if (flags & DI_FUNC_LOCK_FLAGS) {
		dcr = di_read(DCR);
		if (flags & DI_FUNC_FLAG_READ_LOCK) {
			if (flags & DI_FUNC_FLAG_HARD_LOCK)
				dcr |= DCR_PKRHL;
			else
				dcr |= DCR_PKRSL;
		}
		if (flags & DI_FUNC_FLAG_WRITE_LOCK) {
			if (flags & DI_FUNC_FLAG_HARD_LOCK)
				dcr |= DCR_PKWHL;
			else
				dcr |= DCR_PKWSL;
		}
		todo_write_val(dcr, DCR);
	}
	todo_start();

	if (flags & DI_FUNC_FLAG_ASYNC)
		return 0;

	rc = todo_wait_done();
err:
	di_busy_clear();
	return rc;
}
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;
	}
}
Пример #19
0
/*
 * probe for dryice rtc device
 */
static int dryice_rtc_probe(struct platform_device *pdev)
{
	struct rtc_device *rtc;
	struct resource *res;
	struct rtc_drv_data *pdata = NULL;
	void __iomem *ioaddr = NULL;
	int rc = 0;

	dev_dbg(&pdev->dev, "%s\n", __func__);

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!res)
		return -ENODEV;

	pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
	if (!pdata)
		return -ENOMEM;

	pdata->pdev = pdev;
	pdata->irq = -1;
	pdata->size = res->end - res->start + 1;

	if (!request_mem_region(res->start, pdata->size, pdev->name)) {
		rc = -EBUSY;
		goto err;
	}
	pdata->baseaddr = res->start;
	ioaddr = ioremap(pdata->baseaddr, pdata->size);
	if (!ioaddr) {
		rc = -ENOMEM;
		goto err;
	}
	pdata->ioaddr = ioaddr;
	pdata->irq = platform_get_irq(pdev, 0);

	init_waitqueue_head(&pdata->write_wait);

	INIT_WORK(&pdata->work, dryice_work);

	mutex_init(&pdata->write_mutex);

	pdata->clk = clk_get(NULL, "dryice_clk");
	clk_enable(pdata->clk);

	if (pdata->irq >= 0) {
		if (request_irq(pdata->irq, dryice_norm_irq, IRQF_SHARED,
				pdev->name, pdata) < 0) {
			dev_warn(&pdev->dev, "interrupt not available.\n");
			pdata->irq = -1;
			goto err;
		}
	}

	/*
	 * Initialize dryice hardware
	 */

	/* put dryice into valid state */
	if (di_read(pdata, DSR) & DSR_NVF)
		di_write_wait_err(pdata, DSR_NVF | DSR_SVF, DSR, rc, err);

	/* mask alarm interrupt */
	di_int_disable(pdata, DIER_CAIE);

	/* initialize alarm */
	di_write_wait_err(pdata, DCAMR_UNSET, DCAMR, rc, err);
	di_write_wait_err(pdata, 0, DCALR, rc, err);

	/* clear alarm flag */
	if (di_read(pdata, DSR) & DSR_CAF)
		di_write_wait_err(pdata, DSR_CAF, DSR, rc, err);

	/* the timer won't count if it has never been written to */
	if (!di_read(pdata, DTCMR))
		di_write_wait_err(pdata, 0, DTCMR, rc, err);

	/* start keeping time */
	if (!(di_read(pdata, DCR) & DCR_TCE))
		di_write_wait_err(pdata, di_read(pdata, DCR) | DCR_TCE, DCR,
				  rc, err);

	rtc = rtc_device_register(pdev->name, &pdev->dev,
				  &dryice_rtc_ops, THIS_MODULE);
	if (IS_ERR(rtc)) {
		rc = PTR_ERR(rtc);
		goto err;
	}
	pdata->rtc = rtc;
	platform_set_drvdata(pdev, pdata);

	return 0;
err:
	if (pdata->rtc)
		rtc_device_unregister(pdata->rtc);

	if (pdata->irq >= 0)
		free_irq(pdata->irq, pdata);

	if (pdata->clk) {
		clk_disable(pdata->clk);
		clk_put(pdata->clk);
	}

	if (pdata->ioaddr)
		iounmap(pdata->ioaddr);

	if (pdata->baseaddr)
		release_mem_region(pdata->baseaddr, pdata->size);

	kfree(pdata);

	return rc;
}
//=====================================================================================
//	Main Function
//	This is the main function where program start to execute 
//=====================================================================================
void main(void)
{
	//--------------------------------------------------------------------------------
	//	Program start
	//	User can start to write/modify the program here
	//--------------------------------------------------------------------------------

	//	User is advised not to change or remove the initialization function called 
	init();						// call initialization function of the setting in program

	while(1)
	{

	
		//--------------------------------------------------------------------------------
		// This program is mainly for slave card,DI08. It will test the 3 push buttons
		// on MB00, the push buttons are active low, hence, when any of the push button 
		// is pressed, value '0' will be sent to microcontroller on master card, MB00.
		// Hence, the program will test digital input port base on program
		//--------------------------------------------------------------------------------

	
		if(sw1==0)							// Test whether SW1 on IFC-MB00 is pressed
		{
			led1 = 1;						// turn ON LED1 on MB00 by sending value '1'
			buzzer = 1;						// turn ON buzzer on MB00 by sending value '1'
			delay(50000);					// delay for buzzer ON time
			buzzer = 0;						// turn OFF buzzer on MB00 by sending value '0'
			while(1)
			{
				// this is an infinity loop after SW1 on IFC-MB00 is pressed, 
				// user need to press reset button on MB00 to exit from the mode		
				if(di_read(add_di1, 1)==0)			// If DI 1 detected signal
				{
					led5=1;							// turn ON LED5 on MB00 by sending value '1'
					buzzer = 1;						// turn ON buzzer on MB00 by sending value '1'
					delay(50000);					// delay for buzzer ON time
					buzzer = 0;						// OFF buzzer
				}
				if(di_read(add_di1,2)==0)			// If DI 2 detected signal
				{
					led6=1;							// turn ON LED6 on MB00 by sending value '1'						
					buzzer = 1;						// buzzer ON for first time
					delay(50000);					// delay for buzzer ON time                                                         
					buzzer = 0;						// OFF buzzer
					delay(50000);					// delay for buzzer OFF time
					buzzer = 1;						// buzzer ON for second times
					delay(50000);					// delay for buzzer ON time
					buzzer = 0;						// OFF buzzer
				}
				if((di_read(add_di1, 3)||di_read(add_di1, 4))==0)	// If DI 3 and DI 4 detected signal
				{
					led=0xff;						// All LED ON 
					while(1);						// infinity loop					
				}
			}
		}

		if(sw2==0)							// Test whether SW2 on IFC-MB00 is pressed
		{
			led2 = 1;						// turn ON LED2 on MB00 by sending value '1'
			buzzer = 1;						// buzzer ON for first time
			delay(50000);					// delay for buzzer ON time
			buzzer = 0;						// OFF buzzer
			delay(50000);					// delay for buzzer OFF time
			buzzer = 1;						// buzzer ON for second times
			delay(50000);					// delay for buzzer ON time
			buzzer = 0;						// OFF buzzer
			di_c1con(add_di1,1);			// Activate Counter 1
			di_c2con(add_di1,1);			// Activate Counter 2
			di_c1clr(add_di1);				// Clear value in Counter 1
			di_c2clr(add_di1);				// Clear value in Counter 2
			

			while(1)
			{				

				// this is an infinity loop after SW2 on IFC-MB00 is pressed, 
				// user need to press reset button on MB00 to exit from the mode		
				if(di_c1val(add_di1)==3)		// If value counter 1 = 3
				{
					di_c2clr(add_di1);			// Clear value in Counter 2		
					led7=1;						// turn ON LED7 on MB00 by sending value '1'
					led8=0;						// turn OFF LED8 on MB00 by sending value '0'
				}
				else if(di_c2val(add_di1)==3)	// If value counter 2 = 3	
				{
					di_c1clr(add_di1);			// Clear value in Counter 1
					led8=1;						// turn ON LED8 on MB00 by sending value '1'
					led7=0;						// turn OFF LED7 on MB00 by sending value '0'
				}
			}
		}

		if(sw3==0)			   				// Test whether SW3 on IFC-MB00 is pressed
		{
			led3 = 1;						// turn ON LED3 on MB00 by sending value '1'
			buzzer = 1;						// buzzer ON for first time
			delay(50000);					// delay for buzzer ON time
			buzzer = 0;						// OFF buzzer
			delay(50000);					// delay for buzzer OFF time
			buzzer = 1;						// buzzer ON for second times
			delay(50000);					// delay for buzzer ON time
			buzzer = 0;						// OFF buzzer
			delay(50000);					// delay for buzzer OFF time
			buzzer = 1;						// buzzer ON for third times
			delay(50000);					// delay for buzzer ON time
			buzzer = 0;						// OFF buzzer
			while(1)
			{
				// this is an infinity loop after SW3 on IFC-MB00 is pressed, 
				// user need to press reset button on MB00 to exit from the mode		 
				if(di_read(add_di1,0)==0)		// if ALL digital input detected signal
				{
					while(1)					// Infinity loop for LED blinking
					{
						led=0x0f;		
						delay(50000);
						led=0xf0;		
						delay(50000);
					}	
				}
			
			}
		}
	}
}