/**
 * \file
 *
 * \brief SD/MMC slot driver
 *
 * SD/MMC slot driver is an abstraction on top of the host.
 *
 * - 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 <bitops.h>
#include <byteorder.h>
#include <debug.h>
#include <errno.h>
#include <stdbool.h>
#include <string.h>
#include <util.h>
#include <sdmmc/sdmmc.h>
#include "protocol.h"

void sdmmc_slot_submit_req(struct sdmmc_slot *slot, struct sdmmc_request *req)
{
	req->slot = slot;
	slot->host->submit_req(slot->host, req);
}

uint32_t sdmmc_slot_set_voltage(struct sdmmc_slot *slot, uint32_t ocr)
{
	unsigned int bit;

	/*
	 * Sanity check the voltages that the card claims to
	 * support.
	 */
	if (ocr & 0x7F) {
		dbg_verbose("sdmmc: card claims to support voltages "
				"below the defined range. These will "
				"be ignored.\n");
		ocr &= ~0x7F;
	}

	if (ocr & SDMMC_OCR_LOW_VOLTAGE) {
		dbg_verbose("sdmmc: SD card claims to support the "
				"incompletely defined 'low voltage range'. "
				"This will be ignored.\n");
		ocr &= ~SDMMC_OCR_LOW_VOLTAGE;
	}

	/* Mask out the voltages the host doesn't support */
	ocr &= slot->host->ocr_avail;

	bit = ffs(ocr);
	if (bit) {
		bit--;
		ocr &= 3 << bit;
		slot->host->set_voltage(slot->host, bit);
	} else {
		ocr = 0;
	}

	return ocr;
}

void sdmmc_slot_power_up(struct sdmmc_slot *slot)
{
	slot->bus_width = 1;
	clear_bit(SDMMC_SLOT_HIGH_SPEED, &slot->flags);
	sdmmc_slot_set_f_max(slot, 0);
	slot->host->power_up(slot->host, slot);
}

void sdmmc_slot_power_down(struct sdmmc_slot *slot)
{
	sdmmc_slot_set_f_max(slot, 0);
	slot->host->power_down(slot->host, slot);
	sdmmc_slot_set_f_max(slot, -1);
}

void sdmmc_slot_set_f_max(struct sdmmc_slot *slot, int32_t f_max)
{
	struct sdmmc_host	*host = slot->host;

	if (f_max > 0 && f_max < host->f_min)
		f_max = host->f_min;
	slot->f_max = f_max;

	sdmmc_slot_update(slot);
}

void sdmmc_slot_set_bus_width(struct sdmmc_slot *slot, unsigned int bits)
{
	slot->bus_width = bits;
	sdmmc_slot_update(slot);
}

void sdmmc_slot_notify_card_detect(struct sdmmc_slot *slot, int cd_value)
{
	if (cd_value)
		set_bit(SDMMC_SLOT_CARD_DETECT, &slot->flags);
	else
		clear_bit(SDMMC_SLOT_CARD_DETECT, &slot->flags);

	if (slot->notify_card_detect)
		slot->notify_card_detect(slot->context);
}

void sdmmc_slot_init(struct sdmmc_slot *slot, struct sdmmc_host *host,
		int slot_id)
{
	slot->host = host;
	slot->id = slot_id;
	slot->flags = 0;
	slot->f_max = -1;
	slot->notify_card_detect = NULL;
}

