Martin Roth 0443ac2827 AUTHORS: Move src/commonlib copyrights into AUTHORS file
As discussed on the mailing list and voted upon, the coreboot project
is going to move the majority of copyrights out of the headers and into
an AUTHORS file.  This will happen a bit at a time, as we'll be unifying
license headers at the same time.

Signed-off-by: Martin Roth <martin@coreboot.org>
Change-Id: I4c9351652d81040cc4e7b85bdd1ba85709a74192
Reviewed-on: https://review.coreboot.org/c/coreboot/+/35178
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Patrick Georgi <pgeorgi@google.com>
2019-09-10 12:50:35 +00:00

354 lines
9.7 KiB
C

/*
* This file is part of the coreboot project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* MultiMediaCard (MMC), eMMC and Secure Digital (SD) common code which
* transitions the card from the standby state to the transfer state. The
* common code supports read operations, erase and write operations are in
* a separate modules. This code is controller independent.
*/
#include <commonlib/storage.h>
#include "sd_mmc.h"
#include "storage.h"
#include <string.h>
#define DECIMAL_CAPACITY_MULTIPLIER 1000ULL
#define HEX_CAPACITY_MULTIPLIER 1024ULL
struct capacity {
const char *const units;
uint64_t bytes;
};
static void display_capacity(struct storage_media *media, int partition_number)
{
uint64_t capacity;
uint64_t decimal_divisor;
const char *decimal_units;
uint64_t hex_divisor;
const char *hex_units;
int index;
const char *name;
const char *separator;
const struct capacity decimal_list[] = {
{"TB", DECIMAL_CAPACITY_MULTIPLIER * DECIMAL_CAPACITY_MULTIPLIER
* DECIMAL_CAPACITY_MULTIPLIER
* DECIMAL_CAPACITY_MULTIPLIER},
{"GB", DECIMAL_CAPACITY_MULTIPLIER * DECIMAL_CAPACITY_MULTIPLIER
* DECIMAL_CAPACITY_MULTIPLIER},
{"MB", DECIMAL_CAPACITY_MULTIPLIER
* DECIMAL_CAPACITY_MULTIPLIER},
{"KB", DECIMAL_CAPACITY_MULTIPLIER},
{"B", 1}
};
const struct capacity hex_list[] = {
{"TiB", HEX_CAPACITY_MULTIPLIER * HEX_CAPACITY_MULTIPLIER
* HEX_CAPACITY_MULTIPLIER * HEX_CAPACITY_MULTIPLIER},
{"GiB", HEX_CAPACITY_MULTIPLIER * HEX_CAPACITY_MULTIPLIER
* HEX_CAPACITY_MULTIPLIER},
{"MiB", HEX_CAPACITY_MULTIPLIER * HEX_CAPACITY_MULTIPLIER},
{"KiB", HEX_CAPACITY_MULTIPLIER},
{"B", 1}
};
/* Get the partition name */
capacity = media->capacity[partition_number];
name = storage_partition_name(media, partition_number);
separator = "";
if (CONFIG(COMMONLIB_STORAGE_MMC) && !IS_SD(media))
separator = ": ";
/* Determine the decimal divisor for the capacity */
for (index = 0; index < ARRAY_SIZE(decimal_list) - 1; index++) {
if (capacity >= decimal_list[index].bytes)
break;
}
decimal_divisor = decimal_list[index].bytes;
decimal_units = decimal_list[index].units;
/* Determine the hex divisor for the capacity */
for (index = 0; index < ARRAY_SIZE(hex_list) - 1; index++) {
if (capacity >= hex_list[index].bytes)
break;
}
hex_divisor = hex_list[index].bytes;
hex_units = hex_list[index].units;
/* Display the capacity */
sdhc_debug("%3lld.%03lld %sytes (%3lld.%03lld %sytes)%s%s\n",
capacity / decimal_divisor,
(capacity / (decimal_divisor / 1000)) % 1000,
decimal_units,
capacity / hex_divisor,
((capacity / (hex_divisor / 1024)) * 1000 / 1024) % 1000,
hex_units,
separator,
name);
}
void storage_display_setup(struct storage_media *media)
{
int partition_number;
/* Display the device info */
sd_mmc_debug("Man %06x Snr %u ",
media->cid[0] >> 24,
(((media->cid[2] & 0xffff) << 16) |
((media->cid[3] >> 16) & 0xffff)));
sd_mmc_debug("Product %c%c%c%c", media->cid[0] & 0xff,
(media->cid[1] >> 24), (media->cid[1] >> 16) & 0xff,
(media->cid[1] >> 8) & 0xff);
if (!IS_SD(media)) /* eMMC product string is longer */
sd_mmc_debug("%c%c", media->cid[1] & 0xff,
(media->cid[2] >> 24) & 0xff);
sd_mmc_debug(" Revision %d.%d\n", (media->cid[2] >> 20) & 0xf,
(media->cid[2] >> 16) & 0xf);
/* Display the erase block size */
sdhc_debug("Erase block size: 0x%08x\n", media->erase_blocks
* media->write_bl_len);
/* Display the partition capacities */
if (CONFIG(SDHC_DEBUG)) {
for (partition_number = 0; partition_number
< ARRAY_SIZE(media->capacity); partition_number++) {
if (!media->capacity[partition_number])
continue;
display_capacity(media, partition_number);
}
}
}
int storage_startup(struct storage_media *media)
{
int err;
uint64_t capacity;
uint64_t cmult, csize;
struct mmc_command cmd;
struct sd_mmc_ctrlr *ctrlr = media->ctrlr;
/* Determine the storage capacity */
if (media->high_capacity) {
cmult = 8;
csize = sd_mmc_extract_uint32_bits(media->csd, 58, 22);
} else {
csize = sd_mmc_extract_uint32_bits(media->csd, 54, 12);
cmult = sd_mmc_extract_uint32_bits(media->csd, 78, 3);
}
capacity = (csize + 1) << (cmult + 2);
capacity *= media->read_bl_len;
media->capacity[0] = capacity;
/* Limit the block size to 512 bytes */
if (media->read_bl_len > 512)
media->read_bl_len = 512;
if (media->write_bl_len > 512)
media->write_bl_len = 512;
/* Get the erase size in blocks */
media->erase_blocks =
(sd_mmc_extract_uint32_bits(media->csd, 47, 3) + 1)
* (sd_mmc_extract_uint32_bits(media->csd, 42, 5) + 1);
/* Select the card, and put it into Transfer Mode */
cmd.cmdidx = MMC_CMD_SELECT_CARD;
cmd.resp_type = CARD_RSP_R1;
cmd.cmdarg = media->rca << 16;
cmd.flags = 0;
err = ctrlr->send_cmd(ctrlr, &cmd, NULL);
if (err)
return err;
/* Increase the bus frequency */
if (CONFIG(COMMONLIB_STORAGE_SD) && IS_SD(media))
err = sd_change_freq(media);
else if (CONFIG(COMMONLIB_STORAGE_MMC)) {
err = mmc_change_freq(media);
if (!err)
mmc_update_capacity(media);
}
if (err)
return err;
/* Restrict card's capabilities by what the controller can do */
media->caps &= ctrlr->caps;
/* Increase the bus width if possible */
if (CONFIG(COMMONLIB_STORAGE_SD) && IS_SD(media))
err = sd_set_bus_width(media);
else if (CONFIG(COMMONLIB_STORAGE_MMC))
err = mmc_set_bus_width(media);
if (err)
return err;
/* Display the card setup */
storage_display_setup(media);
return 0;
}
int storage_setup_media(struct storage_media *media, struct sd_mmc_ctrlr *ctrlr)
{
int err;
memset(media, 0, sizeof(*media));
media->ctrlr = ctrlr;
err = sd_mmc_enter_standby(media);
if (err)
return err;
return storage_startup(media);
}
static int storage_read(struct storage_media *media, void *dest, uint32_t start,
uint32_t block_count)
{
struct mmc_command cmd;
struct sd_mmc_ctrlr *ctrlr = media->ctrlr;
cmd.resp_type = CARD_RSP_R1;
cmd.flags = 0;
if (block_count > 1)
cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK;
else
cmd.cmdidx = MMC_CMD_READ_SINGLE_BLOCK;
if (media->high_capacity)
cmd.cmdarg = start;
else
cmd.cmdarg = start * media->read_bl_len;
struct mmc_data data;
data.dest = dest;
data.blocks = block_count;
data.blocksize = media->read_bl_len;
data.flags = DATA_FLAG_READ;
if (ctrlr->send_cmd(ctrlr, &cmd, &data))
return 0;
if ((block_count > 1) && !(ctrlr->caps
& DRVR_CAP_AUTO_CMD12)) {
cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
cmd.cmdarg = 0;
cmd.resp_type = CARD_RSP_R1b;
cmd.flags = CMD_FLAG_IGNORE_INHIBIT;
if (ctrlr->send_cmd(ctrlr, &cmd, NULL)) {
sd_mmc_error("Failed to send stop cmd\n");
return 0;
}
/* Waiting for the ready status */
sd_mmc_send_status(media, SD_MMC_IO_RETRIES);
}
return block_count;
}
/////////////////////////////////////////////////////////////////////////////
// BlockDevice utilities and callbacks
int storage_block_setup(struct storage_media *media, uint64_t start,
uint64_t count, int is_read)
{
struct sd_mmc_ctrlr *ctrlr = media->ctrlr;
int partition_number;
if (count == 0)
return 0;
uint32_t bl_len = is_read ? media->read_bl_len :
media->write_bl_len;
/* Validate the block range */
partition_number = media->partition_config & EXT_CSD_PART_ACCESS_MASK;
if (((start * bl_len) > media->capacity[partition_number])
|| (((start + count) * bl_len) >
media->capacity[partition_number])) {
sd_mmc_error("Block range exceeds device capacity\n");
return 0;
}
/*
* CMD16 only applies to single data rate mode, and block
* length for double data rate is always 512 bytes.
*/
if ((ctrlr->timing == BUS_TIMING_UHS_DDR50) ||
(ctrlr->timing == BUS_TIMING_MMC_DDR52) ||
(ctrlr->timing == BUS_TIMING_MMC_HS400) ||
(ctrlr->timing == BUS_TIMING_MMC_HS400ES))
return 1;
if (sd_mmc_set_blocklen(ctrlr, bl_len))
return 0;
return 1;
}
uint64_t storage_block_read(struct storage_media *media, uint64_t start,
uint64_t count, void *buffer)
{
uint8_t *dest = (uint8_t *)buffer;
if (storage_block_setup(media, start, count, 1) == 0)
return 0;
uint64_t todo = count;
struct sd_mmc_ctrlr *ctrlr = media->ctrlr;
do {
uint32_t cur = (uint32_t)MIN(todo, ctrlr->b_max);
if (storage_read(media, dest, start, cur) != cur)
return 0;
todo -= cur;
sd_mmc_trace("%s: Got %d blocks, more %d (total %d) to go.\n",
__func__, (int)cur, (int)todo, (int)count);
start += cur;
dest += cur * media->read_bl_len;
} while (todo > 0);
return count;
}
int storage_set_partition(struct storage_media *media,
unsigned int partition_number)
{
int err;
/* Select the partition */
err = -1;
if (CONFIG(COMMONLIB_STORAGE_SD) && IS_SD(media))
err = sd_set_partition(media, partition_number);
else if (CONFIG(COMMONLIB_STORAGE_MMC))
err = mmc_set_partition(media, partition_number);
if (err)
sd_mmc_error("Invalid partition number!\n");
return err;
}
const char *storage_partition_name(struct storage_media *media,
unsigned int partition_number)
{
const char *name;
/* Get the partition name */
name = NULL;
if (CONFIG(COMMONLIB_STORAGE_SD) && IS_SD(media))
name = sd_partition_name(media, partition_number);
else if (CONFIG(COMMONLIB_STORAGE_MMC))
name = mmc_partition_name(media, partition_number);
return name;
}
unsigned int storage_get_current_partition(struct storage_media *media)
{
return media->partition_config & EXT_CSD_PART_ACCESS_MASK;
}