/**
 * \file
 *
 * \brief SD/MMC card detect handler
 *
 * Card detect handler that listens for interrupt on gpio line and notifies
 * slot on changed status.
 *
 * - Compiler:           IAR EWAVR32 and GNU GCC for AVR32
 * - Supported devices:  All devices
 * - AppNote:
 *
 * \author               Atmel Corporation: http://www.atmel.com \n
 *                       Support and FAQ: http://support.atmel.no/
 *
 * \page License
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. The name of Atmel may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * 4. This software may only be redistributed and used in connection with an
 * Atmel AVR product.
 *
 * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
 * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 */

#include <debug.h>
#include <delayed_work.h>
#include <malloc.h>
#include <slist.h>
#include <workqueue.h>
#include <chip/portmux.h>
#include <gpio/gpio_irq_handler.h>
#include <sdmmc/sdmmc.h>

#include <app/timer.h>
#include <app/workqueue.h>

/**
 * \brief SD/MMC debounce interval for card detection
 *
 * 20 ms is assumed to be a good compromise between responsiveness and
 * robustness.
 */
#define SDMMC_CD_DELAY_US	20000

struct sdmmc_cd {
	struct sdmmc_slot		*slot;
	gpio_pin_t			pin;
	struct workqueue_item		work;
	struct delayed_work		dwork;
	struct gpio_irq_handler		irqh;
};

static void sdmmc_cd_interrupt(void *data)
{
	struct sdmmc_cd *cd = data;

	gpio_disable_interrupt(cd->pin);

	/* Wait for WP and CD signals to stabilize before detecting */
	delayed_work_run_us(&cd->dwork, &cd->work, SDMMC_CD_DELAY_US);
}

static void sdmmc_cd_process(void *data)
{
	struct sdmmc_cd	*cd = data;
	int		value;

	gpio_clear_int_flag(cd->pin);
	gpio_enable_interrupt(cd->pin);

	value = !gpio_get_value(cd->pin);

	dbg_verbose("Notify slot%u card detect %d!\n", cd->slot->id, value);
	sdmmc_slot_notify_card_detect(cd->slot, value);
}

struct sdmmc_cd *sdmmc_cd_init(struct sdmmc_slot *slot, gpio_pin_t pin)
{
	struct sdmmc_cd	*cd;

	cd = malloc(sizeof(struct sdmmc_cd));
	if (cd == NULL) {
		dbg_error("sdmmc_cd_init: Out of memory!\n");
		return NULL;
	}
	cd->slot = slot;
	cd->pin = pin;
	workqueue_init_item(&cd->work, sdmmc_cd_process, cd);
	delayed_work_init(&cd->dwork, &sdmmc_timer, &sdmmc_workqueue);

	gpio_init_irq_handler(&cd->irqh, pin, sdmmc_cd_interrupt, cd);

	gpio_register_irq_handler(&cd->irqh);

	return cd;
}

void sdmmc_cd_enable(struct sdmmc_cd *cd)
{
	/*
	 * Wait for pins to stabilize after power-on, and send initial
	 * slot notify event.
	 */
	delayed_work_run_us(&cd->dwork, &cd->work, SDMMC_CD_DELAY_US);
}

