Merge "target: Add target uninit weak function"
diff --git a/app/aboot/aboot.c b/app/aboot/aboot.c
index 59f43dd..86cbadc 100644
--- a/app/aboot/aboot.c
+++ b/app/aboot/aboot.c
@@ -591,7 +591,7 @@
#if DEVICE_TREE
struct dt_table *table;
- struct dt_entry *dt_entry_ptr;
+ struct dt_entry dt_entry;
unsigned dt_table_offset;
uint32_t dt_actual;
#endif
@@ -735,29 +735,24 @@
memmove((void *) dt_buf, (char *)dt_table_offset, page_size);
- /* Restriction that the device tree entry table should be less than a page*/
- ASSERT(((table->num_entries * sizeof(struct dt_entry))+ DEV_TREE_HEADER_SIZE) < hdr->page_size);
-
- /* Validate the device tree table header */
- if((table->magic != DEV_TREE_MAGIC) && (table->version != DEV_TREE_VERSION)) {
+ if (dev_tree_validate(table, hdr->page_size) != 0) {
dprintf(CRITICAL, "ERROR: Cannot validate Device Tree Table \n");
return -1;
}
-
/* Find index of device tree within device tree table */
- if((dt_entry_ptr = dev_tree_get_entry_ptr(table)) == NULL){
+ if(dev_tree_get_entry_info(table, &dt_entry) != 0){
dprintf(CRITICAL, "ERROR: Device Tree Blob cannot be found\n");
return -1;
}
/* Validate and Read device device tree in the "tags_add */
- if (check_aboot_addr_range_overlap(hdr->tags_addr, dt_entry_ptr->size))
+ if (check_aboot_addr_range_overlap(hdr->tags_addr, dt_entry.size))
{
dprintf(CRITICAL, "Device tree addresses overlap with aboot addresses.\n");
return -1;
}
- memmove((void *)hdr->tags_addr, (char *)dt_table_offset + dt_entry_ptr->offset, dt_entry_ptr->size);
+ memmove((void *)hdr->tags_addr, (char *)dt_table_offset + dt_entry.offset, dt_entry.size);
} else {
/*
* If appended dev tree is found, update the atags with
@@ -820,30 +815,26 @@
}
table = (struct dt_table*) dt_buf;
- /* Restriction that the device tree entry table should be less than a page*/
- ASSERT(((table->num_entries * sizeof(struct dt_entry))+ DEV_TREE_HEADER_SIZE) < hdr->page_size);
-
- /* Validate the device tree table header */
- if((table->magic != DEV_TREE_MAGIC) && (table->version != DEV_TREE_VERSION)) {
+ if (dev_tree_validate(table, hdr->page_size) != 0) {
dprintf(CRITICAL, "ERROR: Cannot validate Device Tree Table \n");
return -1;
}
- /* Calculate the offset of device tree within device tree table */
- if((dt_entry_ptr = dev_tree_get_entry_ptr(table)) == NULL){
+ /* Find index of device tree within device tree table */
+ if(dev_tree_get_entry_info(table, &dt_entry) != 0){
dprintf(CRITICAL, "ERROR: Getting device tree address failed\n");
return -1;
}
/* Validate and Read device device tree in the "tags_add */
- if (check_aboot_addr_range_overlap(hdr->tags_addr, dt_entry_ptr->size))
+ if (check_aboot_addr_range_overlap(hdr->tags_addr, dt_entry.size))
{
dprintf(CRITICAL, "Device tree addresses overlap with aboot addresses.\n");
return -1;
}
- if(mmc_read(ptn + offset + dt_entry_ptr->offset,
- (void *)hdr->tags_addr, dt_entry_ptr->size)) {
+ if(mmc_read(ptn + offset + dt_entry.offset,
+ (void *)hdr->tags_addr, dt_entry.size)) {
dprintf(CRITICAL, "ERROR: Cannot read device tree\n");
return -1;
}
@@ -894,7 +885,7 @@
#if DEVICE_TREE
struct dt_table *table;
- struct dt_entry *dt_entry_ptr;
+ struct dt_entry dt_entry;
uint32_t dt_actual;
#endif
@@ -1024,7 +1015,7 @@
memmove((void*) hdr->ramdisk_addr, (char *)(image_addr + page_size + kernel_actual), hdr->ramdisk_size);
#if DEVICE_TREE
/* Validate and Read device device tree in the "tags_add */
- if (check_aboot_addr_range_overlap(hdr->tags_addr, dt_entry_ptr->size))
+ if (check_aboot_addr_range_overlap(hdr->tags_addr, dt_entry.size))
{
dprintf(CRITICAL, "Device tree addresses overlap with aboot addresses.\n");
return -1;
@@ -1087,31 +1078,27 @@
table = (struct dt_table*) dt_buf;
- /* Restriction that the device tree entry table should be less than a page*/
- ASSERT(((table->num_entries * sizeof(struct dt_entry))+ DEV_TREE_HEADER_SIZE) < hdr->page_size);
-
- /* Validate the device tree table header */
- if((table->magic != DEV_TREE_MAGIC) && (table->version != DEV_TREE_VERSION)) {
+ if (dev_tree_validate(table, hdr->page_size) != 0) {
dprintf(CRITICAL, "ERROR: Cannot validate Device Tree Table \n");
return -1;
}
- /* Calculate the offset of device tree within device tree table */
- if((dt_entry_ptr = dev_tree_get_entry_ptr(table)) == NULL){
+ /* Find index of device tree within device tree table */
+ if(dev_tree_get_entry_info(table, &dt_entry) != 0){
dprintf(CRITICAL, "ERROR: Getting device tree address failed\n");
return -1;
}
/* Validate and Read device device tree in the "tags_add */
- if (check_aboot_addr_range_overlap(hdr->tags_addr, dt_entry_ptr->size))
+ if (check_aboot_addr_range_overlap(hdr->tags_addr, dt_entry.size))
{
dprintf(CRITICAL, "Device tree addresses overlap with aboot addresses.\n");
return -1;
}
/* Read device device tree in the "tags_add */
- if(flash_read(ptn, offset + dt_entry_ptr->offset,
- (void *)hdr->tags_addr, dt_entry_ptr->size)) {
+ if(flash_read(ptn, offset + dt_entry.offset,
+ (void *)hdr->tags_addr, dt_entry.size)) {
dprintf(CRITICAL, "ERROR: Cannot read device tree\n");
return -1;
}
@@ -1298,7 +1285,7 @@
uint32 dt_image_offset = 0;
uint32_t n;
struct dt_table *table;
- struct dt_entry *dt_entry_ptr;
+ struct dt_entry dt_entry;
struct boot_img_hdr *hdr = (struct boot_img_hdr *) (boot_image_start);
@@ -1322,23 +1309,18 @@
/* offset now point to start of dt.img */
table = (struct dt_table*)(boot_image_start + dt_image_offset);
- /* Restriction that the device tree entry table should be less than a page*/
- ASSERT(((table->num_entries * sizeof(struct dt_entry))+ DEV_TREE_HEADER_SIZE) < hdr->page_size);
-
- /* Validate the device tree table header */
- if((table->magic != DEV_TREE_MAGIC) && (table->version != DEV_TREE_VERSION)) {
+ if (dev_tree_validate(table, hdr->page_size) != 0) {
dprintf(CRITICAL, "ERROR: Cannot validate Device Tree Table \n");
return -1;
}
-
- /* Calculate the offset of device tree within device tree table */
- if((dt_entry_ptr = dev_tree_get_entry_ptr(table)) == NULL){
+ /* Find index of device tree within device tree table */
+ if(dev_tree_get_entry_info(table, &dt_entry) != 0){
dprintf(CRITICAL, "ERROR: Getting device tree address failed\n");
return -1;
}
/* Validate and Read device device tree in the "tags_add */
- if (check_aboot_addr_range_overlap(hdr->tags_addr, dt_entry_ptr->size))
+ if (check_aboot_addr_range_overlap(hdr->tags_addr, dt_entry.size))
{
dprintf(CRITICAL, "Device tree addresses overlap with aboot addresses.\n");
return -1;
@@ -1346,8 +1328,8 @@
/* Read device device tree in the "tags_add */
memmove((void*) hdr->tags_addr,
- boot_image_start + dt_image_offset + dt_entry_ptr->offset,
- dt_entry_ptr->size);
+ boot_image_start + dt_image_offset + dt_entry.offset,
+ dt_entry.size);
} else
return -1;
diff --git a/platform/msm_shared/board.c b/platform/msm_shared/board.c
index 58bf4a3..dcc750d 100644
--- a/platform/msm_shared/board.c
+++ b/platform/msm_shared/board.c
@@ -142,6 +142,11 @@
return board.platform_hw;
}
+uint32_t board_hardware_subtype(void)
+{
+ return board.platform_subtype;
+}
+
uint8_t board_pmic_info(struct board_pmic_data *info, uint8_t num_ent)
{
uint8_t i;
diff --git a/platform/msm_shared/dev_tree.c b/platform/msm_shared/dev_tree.c
index f5dbb4e..de5384b 100644
--- a/platform/msm_shared/dev_tree.c
+++ b/platform/msm_shared/dev_tree.c
@@ -36,6 +36,15 @@
#include <platform.h>
#include <board.h>
+struct dt_entry_v1
+{
+ uint32_t platform_id;
+ uint32_t variant_id;
+ uint32_t soc_rev;
+ uint32_t offset;
+ uint32_t size;
+};
+
extern int target_is_emmc_boot(void);
extern uint32_t target_dev_tree_mem(void *fdt, uint32_t memory_node_offset);
/* TODO: This function needs to be moved to target layer to check violations
@@ -98,47 +107,118 @@
return NULL;
}
-/* Function to return the pointer to the start of the correct device tree
+/* Returns 0 if the device tree is valid. */
+int dev_tree_validate(struct dt_table *table, unsigned int page_size)
+{
+ int dt_entry_size;
+
+ /* Validate the device tree table header */
+ if(table->magic != DEV_TREE_MAGIC) {
+ dprintf(CRITICAL, "ERROR: Bad magic in device tree table \n");
+ return -1;
+ }
+
+ if (table->version == DEV_TREE_VERSION_V1) {
+ dt_entry_size = sizeof(struct dt_entry_v1);
+ } else if (table->version == DEV_TREE_VERSION_V2) {
+ dt_entry_size = sizeof(struct dt_entry);
+ } else {
+ dprintf(CRITICAL, "ERROR: Unsupported version (%d) in DT table \n",
+ table->version);
+ return -1;
+ }
+
+ /* Restriction that the device tree entry table should be less than a page*/
+ ASSERT(((table->num_entries * dt_entry_size)+ DEV_TREE_HEADER_SIZE) < page_size);
+
+ return 0;
+}
+
+/* Function to obtain the index information for the correct device tree
* based on the platform data.
+ * If a matching device tree is found, the information is returned in the
+ * "dt_entry_info" out parameter and a function value of 0 is returned, otherwise
+ * a non-zero function value is returned.
*/
-struct dt_entry * dev_tree_get_entry_ptr(struct dt_table *table)
+int dev_tree_get_entry_info(struct dt_table *table, struct dt_entry *dt_entry_info)
{
uint32_t i;
- struct dt_entry *dt_entry_ptr;
- struct dt_entry *latest_dt_entry = NULL;
+ unsigned char *table_ptr;
+ struct dt_entry dt_entry_buf_1;
+ struct dt_entry dt_entry_buf_2;
+ struct dt_entry *cur_dt_entry;
+ struct dt_entry *best_match_dt_entry;
+ struct dt_entry_v1 *dt_entry_v1;
- dt_entry_ptr = (struct dt_entry *)((char *)table + DEV_TREE_HEADER_SIZE);
+ if (!dt_entry_info) {
+ dprintf(CRITICAL, "ERROR: Bad parameter passed to %s \n",
+ __func__);
+ return -1;
+ }
+
+ table_ptr = (unsigned char *)table + DEV_TREE_HEADER_SIZE;
+ cur_dt_entry = &dt_entry_buf_1;
+ best_match_dt_entry = NULL;
for(i = 0; i < table->num_entries; i++)
{
+ memset(cur_dt_entry, 0, sizeof(struct dt_entry));
+ switch(table->version) {
+ case DEV_TREE_VERSION_V1:
+ dt_entry_v1 = (struct dt_entry_v1 *)table_ptr;
+ cur_dt_entry->platform_id = dt_entry_v1->platform_id;
+ cur_dt_entry->variant_id = dt_entry_v1->variant_id;
+ cur_dt_entry->soc_rev = dt_entry_v1->soc_rev;
+ cur_dt_entry->board_hw_subtype = board_hardware_subtype();
+ cur_dt_entry->offset = dt_entry_v1->offset;
+ cur_dt_entry->size = dt_entry_v1->size;
+ table_ptr += sizeof(struct dt_entry_v1);
+ break;
+ case DEV_TREE_VERSION_V2:
+ memcpy(cur_dt_entry, (struct dt_entry *)table_ptr,
+ sizeof(struct dt_entry));
+ table_ptr += sizeof(struct dt_entry);
+ break;
+ default:
+ dprintf(CRITICAL, "ERROR: Unsupported version (%d) in DT table \n",
+ table->version);
+ return -1;
+ }
+
/* DTBs are stored in the ascending order of soc revision.
* For eg: Rev0..Rev1..Rev2 & so on.
* we pickup the DTB with highest soc rev number which is less
* than or equal to actual hardware
*/
- if((dt_entry_ptr->platform_id == board_platform_id()) &&
- (dt_entry_ptr->variant_id == board_hardware_id()) &&
- (dt_entry_ptr->soc_rev == board_soc_version()))
- {
- return dt_entry_ptr;
+ if((cur_dt_entry->platform_id == board_platform_id()) &&
+ (cur_dt_entry->variant_id == board_hardware_id()) &&
+ (cur_dt_entry->board_hw_subtype == board_hardware_subtype()))
+ {
+ if(cur_dt_entry->soc_rev == board_soc_version()) {
+ /* copy structure */
+ *dt_entry_info = *cur_dt_entry;
+ return 0;
+ } else if (cur_dt_entry->soc_rev < board_soc_version()){
+ /* Keep this as the next best candidate. */
+ if (!best_match_dt_entry) {
+ best_match_dt_entry = cur_dt_entry;
+ cur_dt_entry = &dt_entry_buf_2;
+ } else {
+ /* Swap dt_entry buffers */
+ struct dt_entry *temp = cur_dt_entry;
+ cur_dt_entry = best_match_dt_entry;
+ best_match_dt_entry = temp;
+ }
}
- /* if the exact match not found, return the closest match
- * assuming it to be the nearest soc version
- */
- if((dt_entry_ptr->platform_id == board_platform_id()) &&
- (dt_entry_ptr->variant_id == board_hardware_id()) &&
- (dt_entry_ptr->soc_rev <= board_soc_version())) {
- latest_dt_entry = dt_entry_ptr;
}
- dt_entry_ptr++;
}
- if (latest_dt_entry) {
- dprintf(SPEW, "Loading DTB with SOC version:%x\n", latest_dt_entry->soc_rev);
- return latest_dt_entry;
+ if (best_match_dt_entry) {
+ *dt_entry_info = *best_match_dt_entry;
+ return 0;
}
- return NULL;
+ return -1;
}
/* Function to add the first RAM partition info to the device tree.
diff --git a/platform/msm_shared/include/board.h b/platform/msm_shared/include/board.h
index f863a26..ffc65e8 100644
--- a/platform/msm_shared/include/board.h
+++ b/platform/msm_shared/include/board.h
@@ -60,5 +60,5 @@
uint32_t board_hardware_id();
uint8_t board_pmic_info(struct board_pmic_data *, uint8_t num_ent);
uint32_t board_soc_version();
-
+uint32_t board_hardware_subtype(void);
#endif
diff --git a/platform/msm_shared/include/dev_tree.h b/platform/msm_shared/include/dev_tree.h
index 2c216aa..d0b0ca8 100644
--- a/platform/msm_shared/include/dev_tree.h
+++ b/platform/msm_shared/include/dev_tree.h
@@ -34,7 +34,8 @@
#define DEV_TREE_SUCCESS 0
#define DEV_TREE_MAGIC 0x54444351 /* "QCDT" */
#define DEV_TREE_MAGIC_LEN 4
-#define DEV_TREE_VERSION 1
+#define DEV_TREE_VERSION_V1 1
+#define DEV_TREE_VERSION_V2 2
#define DEV_TREE_HEADER_SIZE 12
#define DTB_MAGIC 0xedfe0dd0
@@ -46,6 +47,7 @@
{
uint32_t platform_id;
uint32_t variant_id;
+ uint32_t board_hw_subtype;
uint32_t soc_rev;
uint32_t offset;
uint32_t size;
@@ -64,7 +66,8 @@
DT_OP_FAILURE = -1,
};
-struct dt_entry * dev_tree_get_entry_ptr(struct dt_table *);
+int dev_tree_validate(struct dt_table *table, unsigned int page_size);
+int dev_tree_get_entry_info(struct dt_table *table, struct dt_entry *dt_entry_info);
int update_device_tree(void *, const char *, void *, unsigned);
int dev_tree_add_mem_info(void *fdt, uint32_t offset, uint32_t size, uint32_t addr);
void *dev_tree_appended(void *kernel, void *tags, uint32_t kernel_size);
diff --git a/platform/msm_shared/include/mmc.h b/platform/msm_shared/include/mmc.h
index f1c9762..12181b3 100644
--- a/platform/msm_shared/include/mmc.h
+++ b/platform/msm_shared/include/mmc.h
@@ -284,6 +284,7 @@
#define CMD2_ALL_SEND_CID 2
#define CMD3_SEND_RELATIVE_ADDR 3
#define CMD4_SET_DSR 4
+#define CMD5_SLEEP_AWAKE 5
#define CMD6_SWITCH_FUNC 6
#define ACMD6_SET_BUS_WIDTH 6 /* SD card */
#define CMD7_SELECT_DESELECT_CARD 7
@@ -422,6 +423,9 @@
#define MMC_BOOT_SD_DEV_READY 0x80000000
#define MMC_BOOT_SD_SWITCH_HS 0x80FFFFF1
+/* Put the card to sleep */
+#define MMC_CARD_SLEEP (1 << 15)
+
/* Data structure definitions */
struct mmc_boot_command {
unsigned int cmd_index;
@@ -562,6 +566,7 @@
#define MAX_FILE_ENTRIES 20
#define MMC_RCA 2
+#define MMC_CARD_RCA_BIT 16
/* Can be used to unpack array of upto 32 bits data */
#define UNPACK_BITS(array, start, len, size_of) \
@@ -618,5 +623,6 @@
uint8_t card_supports_ddr_mode();
uint8_t card_supports_hs200_mode();
uint64_t mmc_get_device_capacity();
+void mmc_put_card_to_sleep(void);
#endif
#endif
diff --git a/platform/msm_shared/include/mmc_sdhci.h b/platform/msm_shared/include/mmc_sdhci.h
index 99174bf..6aa323d 100644
--- a/platform/msm_shared/include/mmc_sdhci.h
+++ b/platform/msm_shared/include/mmc_sdhci.h
@@ -37,6 +37,7 @@
#define CMD2_ALL_SEND_CID 2
#define CMD3_SEND_RELATIVE_ADDR 3
#define CMD4_SET_DSR 4
+#define CMD5_SLEEP_AWAKE 5
#define CMD6_SWITCH_FUNC 6
#define CMD7_SELECT_DESELECT_CARD 7
#define CMD8_SEND_EXT_CSD 8
@@ -80,6 +81,7 @@
#define MMC_SWITCH_FUNC_ERR_FLAG (1 << 7)
#define MMC_STATUS_INACTIVE 0
#define MMC_STATUS_ACTIVE 1
+#define MMC_READY_FOR_DATA (1 << 8)
/* EXT_CSD */
/* Offsets in the ext csd */
@@ -94,29 +96,40 @@
#define MMC_PART_CONFIG 179
#define MMC_ERASE_GRP_DEF 175
#define MMC_USR_WP 171
+#define MMC_HC_ERASE_GRP_SIZE 224
/* Values for ext csd fields */
#define MMC_HS_TIMING 0x1
#define MMC_HS200_TIMING 0x2
#define MMC_ACCESS_WRITE 0x3
+#define MMC_SET_BIT 0x1
#define MMC_HS_DDR_MODE (BIT(2) | BIT(3))
#define MMC_HS_HS200_MODE (BIT(4) | BIT(5))
#define MMC_SEC_COUNT4_SHIFT 24
#define MMC_SEC_COUNT3_SHIFT 16
#define MMC_SEC_COUNT2_SHIFT 8
+#define MMC_HC_ERASE_MULT (512 * 1024)
/* Command related */
#define MMC_MAX_COMMAND_RETRY 1000
+#define MMC_MAX_CARD_STAT_RETRY 10000
#define MMC_RD_BLOCK_LEN 512
#define MMC_WR_BLOCK_LEN 512
#define MMC_R1_BLOCK_LEN_ERR (1 << 29)
#define MMC_R1_ADDR_ERR (1 << 30)
+#define MMC_R1_WP_ERASE_SKIP BIT(15)
+#define MMC_US_PERM_WP_DIS BIT(4)
+#define MMC_US_PWR_WP_DIS BIT(3)
+#define MMC_US_PERM_WP_EN BIT(2)
+#define MMC_US_PWR_WP_EN BIT(0)
/* RCA of the card */
#define MMC_RCA 2
+#define MMC_CARD_RCA_BIT 16
/* Misc card macros */
#define MMC_BLK_SZ 512
+#define MMC_CARD_SLEEP (1 << 15)
/* Clock rates */
#define MMC_CLK_400KHZ 400000
@@ -128,6 +141,8 @@
#define MMC_CLK_96MHZ 96000000
#define MMC_CLK_200MHZ 200000000
+#define MMC_ADDR_OUT_OF_RANGE(resp) ((resp >> 31) & 0x01)
+
/* Can be used to unpack array of upto 32 bits data */
#define UNPACK_BITS(array, start, len, size_of) \
({ \
@@ -218,4 +233,12 @@
uint32_t mmc_sdhci_read(struct mmc_device *dev, void *dest, uint64_t blk_addr, uint32_t num_blocks);
/* API: Write requried number of blocks from source to card */
uint32_t mmc_sdhci_write(struct mmc_device *dev, void *src, uint64_t blk_addr, uint32_t num_blocks);
+/* API: Erase len bytes (after converting to number of erase groups), from specified address */
+uint32_t mmc_sdhci_erase(struct mmc_device *dev, uint32_t blk_addr, uint64_t len);
+/* API: Write protect or release len bytes (after converting to number of write protect groups) from specified start address*/
+uint32_t mmc_set_clr_power_on_wp_user(struct mmc_device *dev, uint32_t addr, uint64_t len, uint8_t set_clr);
+/* API: Get the WP status of write protect groups starting at addr */
+uint32_t mmc_get_wp_status(struct mmc_device *dev, uint32_t addr, uint8_t *wp_status);
+/* API: Put the mmc card in sleep mode */
+void mmc_put_card_to_sleep(struct mmc_device *dev);
#endif
diff --git a/platform/msm_shared/include/sdhci.h b/platform/msm_shared/include/sdhci.h
index 0c15c06..20861fc 100644
--- a/platform/msm_shared/include/sdhci.h
+++ b/platform/msm_shared/include/sdhci.h
@@ -88,7 +88,7 @@
struct desc_entry {
uint16_t tran_att; /* Attribute for transfer data */
uint16_t len; /* Length of data */
- void *addr; /* Address of the data */
+ uint32_t addr; /* Address of the data */
};
/*
@@ -262,7 +262,7 @@
#define SDHCI_ADMA_MASK BIT(9)
#define SDHCI_READ_MODE BIT(4)
#define SDHCI_SWITCH_CMD 6
-#define SDHCI_CMD_TIMEOUT 0xE
+#define SDHCI_CMD_TIMEOUT 0xF
#define SDHCI_MAX_CMD_RETRY 10000
#define SDHCI_MAX_TRANS_RETRY 100000
diff --git a/platform/msm_shared/mmc.c b/platform/msm_shared/mmc.c
index 851bb08..7783f10 100644
--- a/platform/msm_shared/mmc.c
+++ b/platform/msm_shared/mmc.c
@@ -552,8 +552,10 @@
/* 2a. Write command index in CMD_INDEX field */
cmd_index = cmd->cmd_index;
mmc_cmd |= cmd->cmd_index;
- /* 2b. Set RESPONSE bit to 1 for all cmds except CMD0 */
- if (cmd_index != CMD0_GO_IDLE_STATE) {
+ /* 2b. Set RESPONSE bit to 1 for all cmds except CMD0
+ * And dont set RESPONSE bit for commands with no response
+ */
+ if (cmd_index != CMD0_GO_IDLE_STATE && cmd->resp_type != MMC_BOOT_RESP_NONE) {
mmc_cmd |= MMC_BOOT_MCI_CMD_RESPONSE;
}
@@ -3405,3 +3407,32 @@
{
return mmc_card.capacity;
}
+
+void mmc_put_card_to_sleep(void)
+{
+ uint32_t mmc_ret;
+ struct mmc_boot_command cmd = {0};
+
+ cmd.cmd_index = CMD7_SELECT_DESELECT_CARD;
+ cmd.argument = 0x00000000;
+ cmd.cmd_type = MMC_BOOT_CMD_ADDRESS;
+ cmd.resp_type = MMC_BOOT_RESP_NONE;
+
+ /* send command */
+ mmc_ret = mmc_boot_send_command(&cmd);
+ if (mmc_ret != MMC_BOOT_E_SUCCESS)
+ {
+ dprintf(CRITICAL, "card deselect error: %d\n", mmc_ret);
+ return;
+ }
+
+ cmd.cmd_index = CMD5_SLEEP_AWAKE;
+ cmd.argument = (mmc_card.rca << MMC_CARD_RCA_BIT) | MMC_CARD_SLEEP;
+ cmd.cmd_type = MMC_BOOT_CMD_ADDRESS;
+ cmd.resp_type = MMC_BOOT_RESP_R1B;
+
+ /* send command */
+ mmc_ret = mmc_boot_send_command(&cmd);
+ if (mmc_ret != MMC_BOOT_E_SUCCESS)
+ dprintf(CRITICAL, "card sleep error: %d\n", mmc_ret);
+}
diff --git a/platform/msm_shared/mmc_sdhci.c b/platform/msm_shared/mmc_sdhci.c
index b565d9e..1e6aaa8 100644
--- a/platform/msm_shared/mmc_sdhci.c
+++ b/platform/msm_shared/mmc_sdhci.c
@@ -1313,3 +1313,359 @@
return mmc_ret;
}
+
+/*
+ * Send the erase group start address using CMD35
+ */
+static uint32_t mmc_send_erase_grp_start(struct mmc_device *dev, uint32_t erase_start)
+{
+ struct mmc_command cmd;
+
+ memset((struct mmc_command *)&cmd, 0, sizeof(struct mmc_command));
+
+ cmd.cmd_index = CMD35_ERASE_GROUP_START;
+ cmd.argument = erase_start;
+ cmd.cmd_type = SDHCI_CMD_TYPE_NORMAL;
+ cmd.resp_type = SDHCI_CMD_RESP_R1;
+
+ /* send command */
+ if (sdhci_send_command(&dev->host, &cmd))
+ return 1;
+
+ /*
+ * CMD35 on failure returns address out of range error
+ */
+ if (MMC_ADDR_OUT_OF_RANGE(cmd.resp[0]))
+ {
+ dprintf(CRITICAL, "Address for CMD35 is out of range\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Send the erase group end address using CMD36
+ */
+static uint32_t mmc_send_erase_grp_end(struct mmc_device *dev, uint32_t erase_end)
+{
+ struct mmc_command cmd;
+
+ memset((struct mmc_command *)&cmd, 0, sizeof(struct mmc_command));
+
+ cmd.cmd_index = CMD36_ERASE_GROUP_END;
+ cmd.argument = erase_end;
+ cmd.cmd_type = SDHCI_CMD_TYPE_NORMAL;
+ cmd.resp_type = SDHCI_CMD_RESP_R1;
+
+ /* send command */
+ if (sdhci_send_command(&dev->host, &cmd))
+ return 1;
+
+ /*
+ * CMD3 on failure returns address out of range error
+ */
+ if (MMC_ADDR_OUT_OF_RANGE(cmd.resp[0]))
+ {
+ dprintf(CRITICAL, "Address for CMD36 is out of range\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Send the erase CMD38, to erase the selected erase groups
+ */
+static uint32_t mmc_send_erase(struct mmc_device *dev)
+{
+ struct mmc_command cmd;
+ uint32_t status;
+ uint32_t retry;
+
+ memset((struct mmc_command *)&cmd, 0, sizeof(struct mmc_command));
+
+ cmd.cmd_index = CMD38_ERASE;
+ cmd.argument = 0x00000000;
+ cmd.cmd_type = SDHCI_CMD_TYPE_NORMAL;
+ cmd.resp_type = SDHCI_CMD_RESP_R1B;
+
+ /* send command */
+ if (sdhci_send_command(&dev->host, &cmd))
+ return 1;
+
+ do
+ {
+ if (mmc_get_card_status(&dev->host, &dev->card, &status))
+ {
+ dprintf(CRITICAL, "Failed to get card status after erase\n");
+ return 1;
+ }
+ /* Check if the response of erase command has eras skip status set */
+ if (status & MMC_R1_WP_ERASE_SKIP)
+ dprintf(CRITICAL, "Write Protect set for the region, only partial space was erased\n");
+
+ retry++;
+ udelay(1000);
+ if (retry == MMC_MAX_CARD_STAT_RETRY)
+ {
+ dprintf(CRITICAL, "Card status check timed out after sending erase command\n");
+ return 1;
+ }
+ } while(!(status & MMC_READY_FOR_DATA) || (MMC_CARD_STATUS(status) == MMC_PROG_STATE));
+
+
+ return 0;
+}
+
+
+/*
+ * Function: mmc sdhci erase
+ * Arg : mmc device structure, block address and length
+ * Return : 0 on Success, non zero on failure
+ * Flow : Fill in the command structure & send the command
+ */
+uint32_t mmc_sdhci_erase(struct mmc_device *dev, uint32_t blk_addr, uint64_t len)
+{
+ uint32_t erase_unit_sz = 0;
+ uint32_t erase_start;
+ uint32_t erase_end;
+ uint32_t blk_end;
+ uint32_t num_erase_grps;
+ uint32_t *out;
+
+ /*
+ * Calculate the erase unit size as per the emmc specification v4.5
+ */
+ if (dev->card.ext_csd[MMC_ERASE_GRP_DEF])
+ erase_unit_sz = (MMC_HC_ERASE_MULT * dev->card.ext_csd[MMC_HC_ERASE_GRP_SIZE]) / MMC_BLK_SZ;
+ else
+ erase_unit_sz = (dev->card.csd.erase_grp_size + 1) * (dev->card.csd.erase_grp_mult + 1);
+
+ /* Convert length in blocks */
+ len = len / MMC_BLK_SZ;
+
+ if (len < erase_unit_sz)
+ {
+ dprintf(CRITICAL, "Requested length is less than min erase group size\n");
+ return 1;
+ }
+
+ /* Calculate erase groups based on the length in blocks */
+ num_erase_grps = len / erase_unit_sz;
+
+ /* Start address of the erase range */
+ erase_start = blk_addr;
+
+ /* Last address of the erase range */
+ erase_end = blk_addr + ((num_erase_grps - 1) * erase_unit_sz);
+
+ /* Boundary check for overlap */
+ blk_end = blk_addr + len;
+
+ if (erase_end > blk_end)
+ {
+ dprintf(CRITICAL, "The erase group overlaps the max requested for erase\n");
+ erase_end -= erase_unit_sz;
+ }
+
+ /* Send CMD35 for erase group start */
+ if (mmc_send_erase_grp_start(dev, erase_start))
+ {
+ dprintf(CRITICAL, "Failed to send erase grp start address\n");
+ return 1;
+ }
+
+ /* Send CMD36 for erase group end */
+ if (mmc_send_erase_grp_end(dev, erase_end))
+ {
+ dprintf(CRITICAL, "Failed to send erase grp end address\n");
+ return 1;
+ }
+
+ /* Send CMD38 to perform erase */
+ if (mmc_send_erase(dev))
+ {
+ dprintf(CRITICAL, "Failed to erase the specified partition\n");
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Function: mmc get wp status
+ * Arg : mmc device structure, block address and buffer for getting wp status
+ * Return : 0 on Success, 1 on Failure
+ * Flow : Get the WP group status by sending CMD31
+ */
+uint32_t mmc_get_wp_status(struct mmc_device *dev, uint32_t addr, uint8_t *wp_status)
+{
+ struct mmc_command cmd;
+
+ memset((struct mmc_command *)&cmd, 0, sizeof(struct mmc_command));
+
+ cmd.cmd_index = CMD31_SEND_WRITE_PROT_TYPE;
+ cmd.argument = addr;
+ cmd.cmd_type = SDHCI_CMD_TYPE_NORMAL;
+ cmd.resp_type = SDHCI_CMD_RESP_R1;
+ cmd.trans_mode = SDHCI_MMC_READ;
+ cmd.data_present = 0x1;
+ cmd.data.data_ptr = wp_status;
+ cmd.data.num_blocks = 0x1;
+ cmd.data.blk_sz = 0x8;
+
+ if (sdhci_send_command(&dev->host, &cmd))
+ {
+ dprintf(CRITICAL, "Failed to get status of write protect bits\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Function: mmc set/clear WP on user area
+ * Arg : mmc device structure, block address,len, & flag to set or clear
+ * Return : 0 on success, 1 on failure
+ * Flow : Function to set/clear power on write protect on user area
+ */
+
+uint32_t mmc_set_clr_power_on_wp_user(struct mmc_device *dev, uint32_t addr, uint64_t len, uint8_t set_clr)
+{
+ struct mmc_command cmd;
+ uint32_t wp_grp_size;
+ uint32_t status;
+ uint32_t num_wp_grps;
+ uint32_t ret;
+ uint32_t retry;
+ uint32_t i;
+
+ memset((struct mmc_command *)&cmd, 0, sizeof(struct mmc_command));
+
+ /* Convert len into blocks */
+ len = len / MMC_BLK_SZ;
+
+ /* Disable PERM WP */
+ ret = mmc_switch_cmd(&dev->host, &dev->card, MMC_SET_BIT, MMC_USR_WP, MMC_US_PERM_WP_DIS);
+
+ if (ret)
+ {
+ dprintf(CRITICAL, "Failed to Disable PERM WP\n");
+ return ret;
+ }
+
+ /* Read the default values for user WP */
+ ret = mmc_get_ext_csd(&dev->host, &dev->card);
+
+ if (ret)
+ {
+ dprintf(CRITICAL, "Failed to read ext csd for the card\n");
+ return ret;
+ }
+
+ /* Check if user power on WP is disabled or perm WP is enabled */
+ if ((dev->card.ext_csd[MMC_USR_WP] & MMC_US_PWR_WP_DIS)
+ || (dev->card.ext_csd[MMC_USR_WP] & MMC_US_PERM_WP_EN))
+ {
+ dprintf(CRITICAL, "Power on protection is disabled, cannot be set\n");
+ return 1;
+ }
+
+ /* Calculate the wp grp size */
+ if (dev->card.ext_csd[MMC_ERASE_GRP_DEF])
+ wp_grp_size = MMC_HC_ERASE_MULT * dev->card.ext_csd[MMC_HC_ERASE_GRP_SIZE] / MMC_BLK_SZ;
+ else
+ wp_grp_size = (dev->card.csd.wp_grp_size + 1) * (dev->card.csd.erase_grp_size + 1) \
+ * (dev->card.csd.erase_grp_mult + 1);
+
+
+ if (len < wp_grp_size)
+ {
+ dprintf(CRITICAL, "Length is less than min WP size, WP was not set\n");
+ return 1;
+ }
+
+ /* Set power on USER WP */
+ ret = mmc_switch_cmd(&dev->host, &dev->card, MMC_SET_BIT, MMC_USR_WP, MMC_US_PWR_WP_EN);
+
+ if (ret)
+ {
+ dprintf(CRITICAL, "Failed to set power on WP for user\n");
+ return ret;
+ }
+
+ num_wp_grps = ROUNDUP(len, wp_grp_size) / wp_grp_size;
+
+ if (set_clr)
+ cmd.cmd_index = CMD28_SET_WRITE_PROTECT;
+ else
+ cmd.cmd_index = CMD29_CLEAR_WRITE_PROTECT;
+
+ cmd.cmd_type = SDHCI_CMD_TYPE_NORMAL;
+ cmd.resp_type = SDHCI_CMD_RESP_R1B;
+
+ for(i = 0; i < num_wp_grps; i++)
+ {
+ cmd.argument = addr + (i * wp_grp_size);
+
+ if (sdhci_send_command(&dev->host, &cmd))
+ return 1;
+
+ /* CMD28/CMD29 On failure returns address out of range error */
+ if (MMC_ADDR_OUT_OF_RANGE(cmd.resp[0]))
+ {
+ dprintf(CRITICAL, "Address for CMD28/29 is out of range\n");
+ return 1;
+ }
+
+ /* Check the card status */
+ do
+ {
+ if (mmc_get_card_status(&dev->host, &dev->card, &status))
+ {
+ dprintf(CRITICAL, "Failed to get card status afterapplying write protect\n");
+ return 1;
+ }
+
+ /* Time out for WP command */
+ retry++;
+ udelay(1000);
+ if (retry == MMC_MAX_CARD_STAT_RETRY)
+ {
+ dprintf(CRITICAL, "Card status timed out after sending write protect command\n");
+ return 1;
+ }
+ } while (!(status & MMC_READY_FOR_DATA) || (MMC_CARD_STATUS(status) == MMC_PROG_STATE));
+
+ }
+
+ return 0;
+}
+
+/* Function to put the mmc card to sleep */
+void mmc_put_card_to_sleep(struct mmc_device *dev)
+{
+ struct mmc_command cmd = {0};
+ struct mmc_card *card = &dev->card;
+
+ cmd.cmd_index = CMD7_SELECT_DESELECT_CARD;
+ cmd.argument = 0x00000000;
+ cmd.cmd_type = SDHCI_CMD_TYPE_NORMAL;
+ cmd.resp_type = SDHCI_CMD_RESP_NONE;
+
+ /* send command */
+ if(sdhci_send_command(&dev->host, &cmd))
+ {
+ dprintf(CRITICAL, "card deselect error: %s\n", __func__);
+ return;
+ }
+
+ cmd.cmd_index = CMD5_SLEEP_AWAKE;
+ cmd.argument = (card->rca << MMC_CARD_RCA_BIT) | MMC_CARD_SLEEP;
+ cmd.cmd_type = SDHCI_CMD_TYPE_NORMAL;
+ cmd.resp_type = SDHCI_CMD_RESP_R1B;
+
+ /* send command */
+ if(sdhci_send_command(&dev->host, &cmd))
+ dprintf(CRITICAL, "card sleep error: %s\n", __func__);
+}
diff --git a/platform/msm_shared/mmc_wrapper.c b/platform/msm_shared/mmc_wrapper.c
index d18c6a6..01ef7e4 100644
--- a/platform/msm_shared/mmc_wrapper.c
+++ b/platform/msm_shared/mmc_wrapper.c
@@ -144,14 +144,22 @@
* Function: mmc erase card
* Arg : Block address & length
* Return : Returns 0
- * Flow : This is dummy API for backward compatibility
- * erase is not supported for sdhci
+ * Flow : Erase the card from specified addr
*/
uint32_t mmc_erase_card(uint64_t addr, uint64_t len)
{
- /* TODO: Right now with sdhci erase function
- * is not implemented, need to be added
- */
+ struct mmc_device *dev;
+
+ dev = target_mmc_device();
+
+ ASSERT(!(addr % MMC_BLK_SZ));
+ ASSERT(!(len % MMC_BLK_SZ));
+
+ if (mmc_sdhci_erase(dev, (addr / MMC_BLK_SZ), len))
+ {
+ dprintf(CRITICAL, "MMC erase failed\n");
+ return 1;
+ }
return 0;
}
diff --git a/platform/msm_shared/sdhci.c b/platform/msm_shared/sdhci.c
index 221d2dd..21e3097 100644
--- a/platform/msm_shared/sdhci.c
+++ b/platform/msm_shared/sdhci.c
@@ -386,7 +386,7 @@
static uint8_t sdhci_cmd_complete(struct sdhci_host *host, struct mmc_command *cmd)
{
uint8_t i;
- uint16_t retry = 0;
+ uint32_t retry = 0;
uint32_t int_status;
do {
@@ -485,14 +485,14 @@
if (len <= SDHCI_ADMA_DESC_LINE_SZ) {
/* Allocate only one descriptor */
- sg_list = (struct desc_entry *) memalign(4, sizeof(struct desc_entry));
+ sg_list = (struct desc_entry *) memalign(lcm(4, CACHE_LINE), ROUNDUP(sizeof(struct desc_entry), CACHE_LINE));
if (!sg_list) {
dprintf(CRITICAL, "Error allocating memory\n");
ASSERT(0);
}
- sg_list[0].addr = data;
+ sg_list[0].addr = (uint32_t)data;
sg_list[0].len = len;
sg_list[0].tran_att = SDHCI_ADMA_TRANS_VALID | SDHCI_ADMA_TRANS_DATA
| SDHCI_ADMA_TRANS_END;
@@ -502,13 +502,14 @@
/* Calculate the number of entries in desc table */
sg_len = len / SDHCI_ADMA_DESC_LINE_SZ;
remain = len - (sg_len * SDHCI_ADMA_DESC_LINE_SZ);
- /* Allocate sg_len + 1 entries */
+
+ /* Allocate sg_len + 1 entries if there are remaining bytes at the end */
if (remain)
sg_len++;
table_len = (sg_len * sizeof(struct desc_entry));
- sg_list = (struct desc_entry *) memalign(4, table_len);
+ sg_list = (struct desc_entry *) memalign(lcm(4, CACHE_LINE), ROUNDUP(table_len, CACHE_LINE));
if (!sg_list) {
dprintf(CRITICAL, "Error allocating memory\n");
@@ -525,7 +526,7 @@
* |_____________|_______________|_____________________|
*/
for (i = 0; i < (sg_len - 1); i++) {
- sg_list[i].addr = data;
+ sg_list[i].addr = (uint32_t)data;
sg_list[i].len = SDHCI_ADMA_DESC_LINE_SZ;
sg_list[i].tran_att = SDHCI_ADMA_TRANS_VALID | SDHCI_ADMA_TRANS_DATA;
data += SDHCI_ADMA_DESC_LINE_SZ;
@@ -535,10 +536,10 @@
/* Fill the last entry of the table with Valid & End
* attributes
*/
- sg_list[sg_len - 1].addr = data;
+ sg_list[sg_len - 1].addr = (uint32_t)data;
sg_list[sg_len - 1].len = len;
- sg_list[sg_len - 1].tran_att = SDHCI_ADMA_TRANS_VALID | SDHCI_ADMA_TRANS_DATA
- | SDHCI_ADMA_TRANS_END;
+ sg_list[sg_len - 1].tran_att = SDHCI_ADMA_TRANS_VALID | SDHCI_ADMA_TRANS_DATA |
+ SDHCI_ADMA_TRANS_END;
}
arch_clean_invalidate_cache_range((addr_t)sg_list, table_len);
@@ -550,16 +551,15 @@
* Function: sdhci adma transfer
* Arg : Host structure & command stucture
* Return : Pointer to desc table
- * Flow : 1. Prepare data transfer properties
+ * Flow : 1. Prepare descriptor table
* 2. Write adma register
- * 3. Write transfer mode register
+ * 3. Write block size & block count register
*/
static struct desc_entry *sdhci_adma_transfer(struct sdhci_host *host,
struct mmc_command *cmd)
{
uint32_t num_blks = 0;
uint32_t sz;
- uint16_t trans_mode = 0;
void *data;
struct desc_entry *adma_addr;
@@ -583,35 +583,20 @@
/* Prepare adma descriptor table */
adma_addr = sdhci_prep_desc_table(data, sz);
+ /* Write adma address to adma register */
+ REG_WRITE32(host, (uint32_t) adma_addr, SDHCI_ADM_ADDR_REG);
+
/* Write the block size */
if (cmd->data.blk_sz)
REG_WRITE16(host, cmd->data.blk_sz, SDHCI_BLKSZ_REG);
else
REG_WRITE16(host, SDHCI_MMC_BLK_SZ, SDHCI_BLKSZ_REG);
- /* Enalbe auto cmd 23 for multi block transfer */
- if (num_blks > 1) {
- trans_mode |= SDHCI_TRANS_MULTI | SDHCI_AUTO_CMD23_EN | SDHCI_BLK_CNT_EN;
- REG_WRITE32(host, num_blks, SDHCI_ARG2_REG);
- }
-
/*
* Set block count in block count register
*/
-
REG_WRITE16(host, num_blks, SDHCI_BLK_CNT_REG);
- if (cmd->trans_mode == SDHCI_MMC_READ)
- trans_mode |= SDHCI_READ_MODE;
-
- trans_mode |= SDHCI_DMA_EN;
-
- /* Write adma address to adma register */
- REG_WRITE32(host, (uint32_t) adma_addr, SDHCI_ADM_ADDR_REG);
-
- /* Set transfer mode */
- REG_WRITE16(host, trans_mode, SDHCI_TRANS_MODE_REG);
-
return adma_addr;
}
@@ -628,6 +613,7 @@
{
uint8_t retry = 0;
uint32_t resp_type = 0;
+ uint16_t trans_mode = 0;
uint16_t present_state;
uint32_t flags;
struct desc_entry *sg_list = NULL;
@@ -711,6 +697,25 @@
/* Write the argument 1 */
REG_WRITE32(host, cmd->argument, SDHCI_ARGUMENT_REG);
+ /* Set the Transfer mode */
+ if (cmd->data_present)
+ {
+ /* Enable DMA */
+ trans_mode |= SDHCI_DMA_EN;
+
+ if (cmd->trans_mode == SDHCI_MMC_READ)
+ trans_mode |= SDHCI_READ_MODE;
+
+ /* Enable auto cmd 23 for multi block transfer */
+ if (cmd->data.num_blocks > 1) {
+ trans_mode |= SDHCI_TRANS_MULTI | SDHCI_AUTO_CMD23_EN | SDHCI_BLK_CNT_EN;
+ REG_WRITE32(host, cmd->data.num_blocks, SDHCI_ARG2_REG);
+ }
+ }
+
+ /* Write to transfer mode register */
+ REG_WRITE16(host, trans_mode, SDHCI_TRANS_MODE_REG);
+
/* Write the command register */
REG_WRITE16(host, SDHCI_PREP_CMD(cmd->cmd_index, flags), SDHCI_CMD_REG);
diff --git a/target/msm8226/init.c b/target/msm8226/init.c
index 026aa53..e5b33fb 100644
--- a/target/msm8226/init.c
+++ b/target/msm8226/init.c
@@ -286,6 +286,11 @@
ulpi_write(ULPI_MISC_A_VBUSVLDEXTSEL | ULPI_MISC_A_VBUSVLDEXT, ULPI_MISC_A_CLEAR);
}
+void target_uninit(void)
+{
+ mmc_put_card_to_sleep(dev);
+}
+
void target_usb_init(void)
{
uint32_t val;