/**
 * \file
 *
 * \brief SD/MMC card logic
 *
 * SD/MMC card logic providing utility functions to analyze card information.
 *
 * - 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"

static inline bool sdmmc_card_is_sd(struct sdmmc_card *card)
{
	return card->type == SDMMC_CARD_TYPE_SD;
}

static inline bool sdmmc_card_is_sdhc(struct sdmmc_card *card)
{
	return card->type == SDMMC_CARD_TYPE_SDHC;
}

static const unsigned int tran_exp[] = {
	10000,		100000,		1000000,	10000000,
	0,		0,		0,		0
};

static const unsigned char tran_mant[] = {
	0,	10,	12,	13,	15,	20,	25,	30,
	35,	40,	45,	50,	55,	60,	70,	80,
};

static const unsigned int tacc_exp[] = {
	1,	10,	100,	1000,	10000,	100000,	1000000, 10000000,
};

static const unsigned int tacc_mant[] = {
	0,	10,	12,	13,	15,	20,	25,	30,
	35,	40,	45,	50,	55,	60,	70,	80,
};

static void sdmmc_dump_cid(const struct sdmmc_cid *cid)
{
	dbg_printf("CID dump:\n");
	dbg_printf("  Manuacturer ID:  0x%x\n", cid->manufacturer_id);
	dbg_printf("  Application ID:  0x%x\n", cid->application_id);
	dbg_printf("  Name:            %c%c%c%c%c\n",
			cid->name[0], cid->name[1],
			cid->name[2], cid->name[3],
			cid->name[4]);
	dbg_printf("  Revision:        0x%x\n", cid->revision);
	dbg_printf("  Serial:          0x%x\n", cid->serial);
	dbg_printf("  Manufacturing date: %u/%u\n",
			cid->manu_year, cid->manu_month);
}

/*
 * Given the decoded CSD structure, decode the raw CID to our CID structure.
 */
void sdmmc_decode_sd_cid(struct sdmmc_card *card, const be32_t *raw_cid)
{
	uint32_t	resp[4];
	unsigned int	i;

	for (i = 0; i < 4; i++)
		resp[3 - i] = be32_to_cpu(raw_cid[i]);

	card->cid.manufacturer_id =
		extract_bitfield(120, 8, resp);
	card->cid.application_id =
		extract_bitfield(104, 16, resp);
	card->cid.name[0]    = extract_bitfield(96, 8, resp);
	card->cid.name[1]    = extract_bitfield(88, 8, resp);
	card->cid.name[2]    = extract_bitfield(80, 8, resp);
	card->cid.name[3]    = extract_bitfield(72, 8, resp);
	card->cid.name[4]    = extract_bitfield(64, 8, resp);
	card->cid.revision   = extract_bitfield(56, 8, resp);
	card->cid.serial     = extract_bitfield(24, 32, resp);
	card->cid.manu_year  = extract_bitfield(12, 8, resp) + 2000;
	card->cid.manu_month = extract_bitfield(8, 4, resp);
	card->cid.checksum   = extract_bitfield(1, 7, resp);

	sdmmc_dump_cid(&card->cid);
}

int sdmmc_decode_mmc_cid(struct sdmmc_card *card, const be32_t *raw_cid)
{
	uint32_t	resp[4];
	unsigned int	i;

	for (i = 0; i < 4; i++)
		resp[3 - i] = be32_to_cpu(raw_cid[i]);

	switch (card->csd.mmc_version) {
	case 0:	/* MMC v1.0 - v1.2 */
	case 1: /* MMC v1.4 */
		card->cid.manufacturer_id =
			extract_bitfield(104, 24, resp);
		card->cid.name[0] =
			extract_bitfield(96, 8, resp);
		card->cid.name[1] =
			extract_bitfield(88, 8, resp);
		card->cid.name[2] =
			extract_bitfield(80, 8, resp);
		card->cid.name[3] =
			extract_bitfield(72, 8, resp);
		card->cid.name[4] =
			extract_bitfield(64, 8, resp);
		card->cid.name[5] =
			extract_bitfield(56, 8, resp);
		card->cid.name[6] =
			extract_bitfield(48, 8, resp);
		card->cid.revision =
			extract_bitfield(40, 8, resp);
		card->cid.serial =
			extract_bitfield(16, 24, resp);
		card->cid.manu_month =
			extract_bitfield(12, 4, resp);
		card->cid.manu_year =
			extract_bitfield(8, 4, resp) + 1997;
		break;

	case 2: /* MMC v2.0 - v2.2 */
	case 3: /* MMC v3.1 - v3.3 */
	case 4: /* MMC v4 */
		card->cid.manufacturer_id =
			extract_bitfield(120, 8, resp);
		card->cid.application_id =
			extract_bitfield(104, 16, resp);
		card->cid.name[0] =
			extract_bitfield(96, 8, resp);
		card->cid.name[1] =
			extract_bitfield(88, 8, resp);
		card->cid.name[2] =
			extract_bitfield(80, 8, resp);
		card->cid.name[3] =
			extract_bitfield(72, 8, resp);
		card->cid.name[4] =
			extract_bitfield(64, 8, resp);
		card->cid.name[5] =
			extract_bitfield(56, 8, resp);
		card->cid.serial =
			extract_bitfield(16, 32, resp);
		card->cid.manu_month =
			extract_bitfield(12, 4, resp);
		card->cid.manu_year =
			extract_bitfield(8, 4, resp) + 1997;
		card->cid.checksum =
			extract_bitfield(1, 7, resp);
		break;

	default:
		dbg_printf("mmc: card has unknown MMCA version %d\n",
			card->csd.mmc_version);
		return -EINVAL;
	}

	sdmmc_dump_cid(&card->cid);

	return 0;
}

/*
 * Given a 128-bit response, decode to our card CSD structure.
 */
int sdmmc_decode_sd_csd(struct sdmmc_card *card, const be32_t *raw_csd)
{
	struct sdmmc_csd *csd = &card->csd;
	unsigned int exp, mul, csd_structure;
	uint32_t resp[4];
	unsigned int i;

	for (i = 0; i < 4; i++)
		resp[3 - i] = be32_to_cpu(raw_csd[i]);

	dbg_printf("mmc csd: %08x %08x %08x %08x\n",
			resp[0], resp[1], resp[2], resp[3]);

	csd_structure = extract_bitfield(126, 2, resp);

	switch (csd_structure) {
	case 0:
		mul = extract_bitfield(115, 4, resp);
		exp = extract_bitfield(112, 3, resp);
		csd->read_access_time_ns =
			(tacc_exp[exp] * tacc_mant[mul] + 9) / 10;
		csd->read_access_time_clks =
			extract_bitfield(104, 8, resp) * 100;

		mul = extract_bitfield(99, 4, resp);
		exp = extract_bitfield(96, 3, resp);
		csd->max_transfer_rate =
			tran_exp[exp] * tran_mant[mul];
		csd->command_classes =
			extract_bitfield(84, 12, resp);

		mul = extract_bitfield(62, 12, resp);
		exp = extract_bitfield(47, 3, resp);
		csd->capacity = (mul + 1) << (exp + 2);

		csd->read_block_length =
			extract_bitfield(80, 4, resp);
		csd->can_read_partial =
			extract_bitfield(79, 1, resp);
		csd->can_write_misaligned =
			extract_bitfield(78, 1, resp);
		csd->can_read_misaligned =
			extract_bitfield(77, 1, resp);
		csd->write_speed_factor =
			extract_bitfield(26, 3, resp);
		csd->write_block_length =
			extract_bitfield(22, 4, resp);
		csd->can_write_partial =
			extract_bitfield(21, 1, resp);
		break;
	case 1:
		/*
		 * This is a block-addressed SDHC card. Most
		 * interesting fields are unused and have fixed
		 * values. To avoid getting tripped by buggy cards,
		 * we assume those fixed values ourselves.
		 */
		csd->read_access_time_ns	 = 0; /* Unused */
		csd->read_access_time_clks	 = 0; /* Unused */

		mul = extract_bitfield(99, 4, resp);
		exp = extract_bitfield(96, 3, resp);
		csd->max_transfer_rate = tran_exp[exp] * tran_mant[mul];
		csd->command_classes =
			extract_bitfield(84, 12, resp);

		mul = extract_bitfield(48, 22, resp);
		csd->capacity = (mul + 1) << 10;

		csd->read_block_length = 9;
		csd->can_read_partial = 0;
		csd->can_write_misaligned = 0;
		csd->can_read_misaligned = 0;
		csd->write_speed_factor = 4; /* Unused */
		csd->write_block_length = 9;
		csd->can_write_partial = 0;
		break;
	default:
		dbg_printf("mmc: unrecognised CSD structure version %d\n",
				csd_structure);
		return -EINVAL;
	}

	/* Card timeouts might have changed so flush them */
	sdmmc_slot_update(sdmmc_card_get_slot(card));

	return 0;
}

int sdmmc_decode_mmc_csd(struct sdmmc_card *card, const be32_t *raw_csd)
{
	struct sdmmc_csd *csd = &card->csd;
	unsigned int exp, mul, csd_structure;
	uint32_t resp[4];
	unsigned int i;

	for (i = 0; i < 4; i++)
		resp[3 - i] = be32_to_cpu(raw_csd[i]);

	dbg_printf("mmc csd: %08x %08x %08x %08x\n",
			resp[0], resp[1], resp[2], resp[3]);

	csd_structure = extract_bitfield(126, 2, resp);
	if (csd_structure != 1 && csd_structure != 2) {
		dbg_printf("mmc: unrecognised CSD structure version %d\n",
			csd_structure);
		return -EINVAL;
	}

	csd->mmc_version = extract_bitfield(122, 4, resp);
	mul = extract_bitfield(115, 4, resp);
	exp = extract_bitfield(112, 3, resp);
	csd->read_access_time_ns =
		(tacc_exp[exp] * tacc_mant[mul] + 9) / 10;
	csd->read_access_time_clks =
		extract_bitfield(104, 8, resp) * 100;

	/* Card timeouts might have changed so flush them */
	sdmmc_slot_update(sdmmc_card_get_slot(card));

	mul = extract_bitfield(99, 4, resp);
	exp = extract_bitfield(96, 3, resp);
	csd->max_transfer_rate = tran_exp[exp] * tran_mant[mul];
	csd->command_classes = extract_bitfield(84, 12, resp);

	exp = extract_bitfield(47, 3, resp);
	mul = extract_bitfield(62, 12, resp);
	csd->capacity = (mul + 1) << (exp + 2);

	csd->read_block_length = extract_bitfield(80, 4, resp);
	csd->can_read_partial = extract_bitfield(79, 1, resp);
	csd->can_write_misaligned = extract_bitfield(78, 1, resp);
	csd->can_read_misaligned = extract_bitfield(77, 1, resp);
	csd->write_speed_factor = extract_bitfield(26, 3, resp);
	csd->write_block_length = extract_bitfield(22, 4, resp);
	csd->can_write_partial = extract_bitfield(21, 1, resp);

	return 0;
}

static void sdmmc_card_calc_timeout(struct sdmmc_card *card, uint32_t f_cur,
		bool write)
{
	uint32_t		*timeout_ns;
	uint32_t		*timeout_clks;
	uint32_t		mult;

	if (write) {
		timeout_ns = &card->write_timeout_ns;
		timeout_clks = &card->write_timeout_clks;
	} else {
		timeout_ns = &card->read_timeout_ns;
		timeout_clks = &card->read_timeout_clks;
	}

	if (sdmmc_card_is_sdhc(card)) {
		/* SDHC cards use fixed values */
		if (write)
			*timeout_ns = 2500000000;
		else
			*timeout_ns = 1000000000;
		*timeout_clks = 0;
	} else {
		mult = sdmmc_card_is_sd(card) ? 100 : 10;
		if (write)
			mult <<= card->csd.read_access_time_ns;

		*timeout_ns = card->csd.read_access_time_ns * mult;
		*timeout_clks = card->csd.read_access_time_clks * mult;

		if (sdmmc_card_is_sd(card)) {
			unsigned int timeout_us, limit_us;

			timeout_us = *timeout_ns / 1000;
			timeout_us += *timeout_clks * 1000 / (f_cur / 1000);

			if (write)
				limit_us = 2500000;
			else
				limit_us = 1000000;

			if (timeout_us > limit_us) {
				*timeout_ns = limit_us * 1000;
				*timeout_clks = 0;
			}
		}
	}
}

void sdmmc_card_update_timeouts(struct sdmmc_card *card, uint32_t mmc_hz)
{
	sdmmc_card_calc_timeout(card, mmc_hz, true);
	sdmmc_card_calc_timeout(card, mmc_hz, false);
}
