| /* |
| * Copyright (c) 2013, 2015-2017, 2020 The Linux Foundation. All rights reserved. |
| * |
| * Based on smem.c from lk. |
| * |
| * Copyright (c) 2009, Google Inc. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * 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. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
| * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
| * COPYRIGHT OWNER OR CONTRIBUTORS 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 <common.h> |
| #include <asm/io.h> |
| #include <asm/errno.h> |
| #include <linux/sizes.h> |
| #include <asm/arch-qca-common/smem.h> |
| #include <nand.h> |
| #include "fdt_info.h" |
| #include <ubi_uboot.h> |
| #include <command.h> |
| |
| #ifdef IPQ_UBI_VOL_WRITE_SUPPORT |
| static struct ubi_device *ubi; |
| #endif |
| typedef struct smem_pmic_type |
| { |
| unsigned pmic_model; |
| unsigned pmic_die_revision; |
| }pmic_type; |
| |
| typedef struct qca_platform_v1 { |
| unsigned format; |
| unsigned id; |
| unsigned version; |
| char build_id[BUILD_ID_LEN]; |
| unsigned raw_id; |
| unsigned raw_version; |
| unsigned hw_platform; |
| unsigned platform_version; |
| unsigned accessory_chip; |
| unsigned hw_platform_subtype; |
| }qca_platform_v1; |
| |
| typedef struct qca_platform_v2 { |
| qca_platform_v1 v1; |
| pmic_type pmic_info[3]; |
| unsigned foundry_id; |
| }qca_platform_v2; |
| |
| typedef struct qca_platform_v3 { |
| qca_platform_v2 v2; |
| unsigned chip_serial; |
| } qca_platform_v3; |
| |
| union qca_platform { |
| qca_platform_v1 v1; |
| qca_platform_v2 v2; |
| qca_platform_v3 v3; |
| }; |
| |
| struct smem_proc_comm { |
| unsigned command; |
| unsigned status; |
| unsigned data1; |
| unsigned data2; |
| }; |
| |
| struct smem_heap_info { |
| unsigned initialized; |
| unsigned free_offset; |
| unsigned heap_remaining; |
| unsigned reserved; |
| }; |
| |
| struct smem_alloc_info { |
| unsigned allocated; |
| unsigned offset; |
| unsigned size; |
| unsigned reserved; |
| }; |
| |
| struct smem_machid_info { |
| unsigned format; |
| unsigned machid; |
| }; |
| |
| struct smem { |
| struct smem_proc_comm proc_comm[4]; |
| unsigned version_info[32]; |
| struct smem_heap_info heap_info; |
| struct smem_alloc_info alloc_info[SMEM_MAX_SIZE]; |
| }; |
| |
| #define ALIGN_LEN 0x00000007 |
| |
| #define SMEM_PTN_NAME_MAX 16 |
| #define SMEM_PTABLE_PARTS_MAX 32 |
| #define SMEM_PTABLE_PARTS_DEFAULT 16 |
| |
| struct smem_ptn { |
| char name[SMEM_PTN_NAME_MAX]; |
| unsigned start; |
| unsigned size; |
| unsigned attr; |
| } __attribute__ ((__packed__)); |
| |
| #define __str_fmt(x) "%-" #x "s" |
| #define _str_fmt(x) __str_fmt(x) |
| #define smem_ptn_name_fmt _str_fmt(SMEM_PTN_NAME_MAX) |
| |
| struct smem_ptable { |
| #define _SMEM_PTABLE_MAGIC_1 0x55ee73aa |
| #define _SMEM_PTABLE_MAGIC_2 0xe35ebddb |
| unsigned magic[2]; |
| unsigned version; |
| unsigned len; |
| struct smem_ptn parts[SMEM_PTABLE_PARTS_MAX]; |
| } __attribute__ ((__packed__)); |
| |
| /* partition table from SMEM */ |
| static struct smem_ptable smem_ptable; |
| |
| static struct smem *smem = (void *)(CONFIG_QCA_SMEM_BASE); |
| |
| qca_smem_flash_info_t qca_smem_flash_info; |
| qca_smem_bootconfig_info_t qca_smem_bootconfig_info; |
| static AvbABData dualboot_info; |
| static AvbABSlotData s0,s1; |
| |
| #ifdef CONFIG_SMEM_VERSION_C |
| |
| #define SMEM_COMMON_HOST 0xFFFE |
| #ifndef CONFIG_QCA_SMEM_SIZE |
| #define CONFIG_QCA_SMEM_SIZE 0x100000 |
| #endif |
| |
| enum { |
| smem_enu_sucess, |
| smem_enu_failed, |
| smem_enu_no_init |
| }; |
| |
| u8 smem_enumeration_status = smem_enu_no_init; |
| |
| /** |
| * struct smem_ptable_entry - one entry in the @smem_ptable list |
| * @offset: offset, within the main shared memory region, of the partition |
| * @size: size of the partition |
| * @flags: flags for the partition (currently unused) |
| * @host0: first processor/host with access to this partition |
| * @host1: second processor/host with access to this partition |
| * @reserved: reserved entries for later use |
| */ |
| struct smem_ptable_entry { |
| __le32 offset; |
| __le32 size; |
| __le32 flags; |
| __le16 host0; |
| __le16 host1; |
| __le32 reserved[8]; |
| }; |
| |
| /** |
| * struct smem_private_ptable - partition table for the private partitions |
| * @magic: magic number, must be SMEM_PTABLE_MAGIC |
| * @version: version of the partition table |
| * @num_entries: number of partitions in the table |
| * @reserved: for now reserved entries |
| * @entry: list of @smem_ptable_entry for the @num_entries partitions |
| */ |
| struct smem_private_ptable { |
| u8 magic[4]; |
| __le32 version; |
| __le32 num_entries; |
| __le32 reserved[5]; |
| struct smem_ptable_entry entry[]; |
| }; |
| |
| /** |
| * struct smem_private_entry - header of each item in the private partition |
| * @canary: magic number, must be SMEM_PRIVATE_CANARY |
| * @item: identifying number of the smem item |
| * @size: size of the data, including padding bytes |
| * @padding_data: number of bytes of padding of data |
| * @padding_hdr: number of bytes of padding between the header and the data |
| * @reserved: for now reserved entry |
| */ |
| struct smem_private_entry { |
| u16 canary; /* bytes are the same so no swapping needed */ |
| __le16 item; |
| __le32 size; /* includes padding bytes */ |
| __le16 padding_data; |
| __le16 padding_hdr; |
| __le32 reserved; |
| }; |
| |
| #define SMEM_PRIVATE_CANARY 0xa5a5 |
| |
| static const u8 SMEM_PTABLE_MAGIC[] = { 0x24, 0x54, 0x4f, 0x43 }; /* "$TOC" */ |
| /** |
| * struct smem_partition_header - header of the partitions |
| * @magic: magic number, must be SMEM_PART_MAGIC |
| * @host0: first processor/host with access to this partition |
| * @host1: second processor/host with access to this partition |
| * @size: size of the partition |
| * @offset_free_uncached: offset to the first free byte of uncached memory in |
| * this partition |
| * @offset_free_cached: offset to the first free byte of cached memory in this |
| * partition |
| * @reserved: for now reserved entries |
| */ |
| struct smem_partition_header { |
| u8 magic[4]; |
| __le16 host0; |
| __le16 host1; |
| __le32 size; |
| __le32 offset_free_uncached; |
| __le32 offset_free_cached; |
| __le32 reserved[3]; |
| }; |
| |
| static const u8 SMEM_PART_MAGIC[] = { 0x24, 0x50, 0x52, 0x54 }; /*$PRT*/ |
| |
| struct smem_partition_header *smem_cmn_partition; |
| |
| /* Pointer to the one and only smem handle */ |
| |
| static struct smem_private_entry * |
| phdr_to_first_private_entry(struct smem_partition_header *phdr) |
| { |
| void *p = phdr; |
| |
| return p + sizeof(*phdr); |
| } |
| |
| static struct smem_private_entry * |
| phdr_to_last_private_entry(struct smem_partition_header *phdr) |
| { |
| void *p = phdr; |
| |
| return p + le32_to_cpu(phdr->offset_free_uncached); |
| } |
| |
| static struct smem_private_entry * |
| private_entry_next(struct smem_private_entry *e) |
| { |
| void *p = e; |
| |
| return p + sizeof(*e) + le16_to_cpu(e->padding_hdr) + |
| le32_to_cpu(e->size); |
| } |
| |
| static void *entry_to_item(struct smem_private_entry *e) |
| { |
| void *p = e; |
| |
| return p + sizeof(*e) + le16_to_cpu(e->padding_hdr); |
| } |
| |
| static void *qcom_smem_get_private(struct smem_partition_header *phdr, |
| unsigned item, |
| size_t *size) |
| { |
| struct smem_private_entry *e, *end; |
| |
| e = phdr_to_first_private_entry(phdr); |
| end = phdr_to_last_private_entry(phdr); |
| |
| while (e < end) { |
| if (e->canary != SMEM_PRIVATE_CANARY) { |
| printf("Found invalid canary in\ |
| host common partition\n"); |
| return ERR_PTR(-EINVAL); |
| } |
| if (le16_to_cpu(e->item) == item) { |
| if (size != NULL) |
| *size = le32_to_cpu(e->size) - |
| le16_to_cpu(e->padding_data); |
| |
| return entry_to_item(e); |
| } |
| |
| e = private_entry_next(e); |
| } |
| return ERR_PTR(-ENOENT); |
| } |
| |
| static int qcom_smem_enumerate_partitions(void) |
| { |
| struct smem_partition_header *header; |
| struct smem_ptable_entry *entry; |
| struct smem_private_ptable *ptable; |
| u32 version, host0, host1; |
| int i; |
| |
| ptable = (struct smem_private_ptable*) (CONFIG_QCA_SMEM_BASE + \ |
| CONFIG_QCA_SMEM_SIZE - SZ_4K); |
| if (memcmp(ptable->magic, SMEM_PTABLE_MAGIC, sizeof(ptable->magic))) |
| return -EINVAL; |
| |
| version = le32_to_cpu(ptable->version); |
| if (version != 1) { |
| printf("Unsupported partition header version %d\n", version); |
| return -EINVAL; |
| } |
| for (i = 0; i < le32_to_cpu(ptable->num_entries); i++) { |
| entry = &ptable->entry[i]; |
| host0 = le16_to_cpu(entry->host0); |
| host1 = le16_to_cpu(entry->host1); |
| |
| if (host0 != SMEM_COMMON_HOST || host1 != SMEM_COMMON_HOST) |
| continue; |
| |
| if (!le32_to_cpu(entry->offset)) |
| continue; |
| printf("\noffset: 0X%X %u", entry->offset, entry->offset); |
| |
| if (!le32_to_cpu(entry->size)) |
| continue; |
| printf("\nsize: 0X%X %u", entry->size, entry->size); |
| |
| if (smem_cmn_partition) { |
| printf("Already found a partition for host %x \n", |
| SMEM_COMMON_HOST); |
| return -EINVAL; |
| } |
| |
| header = (struct smem_partition_header*) (CONFIG_QCA_SMEM_BASE + \ |
| le32_to_cpu(entry->offset)); |
| host0 = le16_to_cpu(header->host0); |
| host1 = le16_to_cpu(header->host1); |
| |
| if (memcmp(header->magic, SMEM_PART_MAGIC, |
| sizeof(header->magic))) { |
| printf("Partition %d has invalid magic\n", i); |
| return -EINVAL; |
| } |
| |
| if (host0 != SMEM_COMMON_HOST || host1 != SMEM_COMMON_HOST) { |
| printf("Partition %d hosts are invalid\n", i); |
| return -EINVAL; |
| } |
| |
| if (header->size != entry->size) { |
| printf("Partition %d has invalid size\n", i); |
| return -EINVAL; |
| } |
| |
| if (le32_to_cpu(header->offset_free_uncached) > le32_to_cpu(header->size)) { |
| printf("Partition %d has invalid free pointer\n", i); |
| return -EINVAL; |
| } |
| |
| smem_cmn_partition = header; |
| } |
| smem_enumeration_status = smem_enu_sucess; |
| return 0; |
| } |
| #endif |
| |
| /** |
| * smem_read_alloc_entry - reads an entry from SMEM |
| * @type: the entry to read |
| * @buf: the buffer location where the data will be stored |
| * @len: the size of the buffer |
| * |
| * Reads and stores the entry in @buf. Returns 0 on success and 1 on |
| * failure. |
| * |
| * NOTE: buf MUST be 4byte aligned, and len MUST be a multiple of 8. |
| */ |
| unsigned smem_read_alloc_entry(smem_mem_type_t type, void *buf, int len) |
| { |
| struct smem_alloc_info *ainfo; |
| unsigned *dest = buf; |
| unsigned src; |
| unsigned size; |
| #ifdef CONFIG_SMEM_VERSION_C |
| void *ptr; |
| int ret; |
| #endif |
| |
| if (((len & 0x3) != 0) || (((unsigned)buf & 0x3) != 0)) |
| return 1; |
| |
| if (type < SMEM_FIRST_VALID_TYPE || type > SMEM_LAST_VALID_TYPE) |
| { |
| return 1; |
| } |
| |
| #ifdef CONFIG_SMEM_VERSION_C |
| if(smem_enumeration_status == smem_enu_no_init) { |
| ret = qcom_smem_enumerate_partitions(); |
| if (ret) { |
| printf("Common SMEM Partition is not available\n"); |
| smem_enumeration_status = smem_enu_failed; |
| return ret; |
| } |
| } |
| |
| if ( smem_enumeration_status == smem_enu_sucess) { |
| ptr = qcom_smem_get_private(smem_cmn_partition,type, &size); |
| if (IS_ERR(ptr)) { |
| ret = PTR_ERR(ptr); |
| return ret; |
| } |
| memcpy(dest, ptr, len); |
| return 0; |
| } |
| return -EINVAL; |
| #endif |
| ainfo = &smem->alloc_info[type]; |
| if (readl(&ainfo->allocated) == 0) |
| { |
| return 1; |
| } |
| |
| size = readl(&ainfo->size); |
| |
| if (size != (unsigned)((len + 7) & ~ALIGN_LEN)) |
| { |
| return 1; |
| } |
| |
| src = CONFIG_QCA_SMEM_BASE + readl(&ainfo->offset); |
| for (; len > 0; src += 4, len -= 4) |
| *(dest++) = readl(src); |
| |
| return 0; |
| } |
| |
| /** |
| * smem_ptable_init - initializes SMEM partition table |
| * |
| * Initialize partition table from SMEM. |
| */ |
| int smem_ptable_init(void) |
| { |
| unsigned ret; |
| |
| ret = smem_read_alloc_entry(SMEM_AARM_PARTITION_TABLE, |
| &smem_ptable, sizeof(smem_ptable)); |
| |
| if (ret != 0) { |
| /* |
| * Trying for max partition 16 in case of smem_read_alloc_entry fails |
| */ |
| ret = smem_read_alloc_entry(SMEM_AARM_PARTITION_TABLE, &smem_ptable, |
| sizeof(smem_ptable) - (sizeof(struct smem_ptn) * SMEM_PTABLE_PARTS_DEFAULT)); |
| } |
| if (ret != 0) |
| return -ENOMSG; |
| |
| if (smem_ptable.magic[0] != _SMEM_PTABLE_MAGIC_1 || |
| smem_ptable.magic[1] != _SMEM_PTABLE_MAGIC_2) |
| return -ENOMSG; |
| |
| debug("smem ptable found: ver: %d len: %d\n", |
| smem_ptable.version, smem_ptable.len); |
| |
| return 0; |
| } |
| |
| /* |
| * smem_bootconfig_info - retrieve bootconfig flags |
| */ |
| int smem_bootconfig_info(void) |
| { |
| unsigned ret; |
| |
| #ifndef CONFIG_ANDROID_AB_BOOT |
| ret = smem_read_alloc_entry(SMEM_BOOT_DUALPARTINFO, |
| &qca_smem_bootconfig_info, sizeof(qca_smem_bootconfig_info_t)); |
| if ((ret != 0) || |
| (qca_smem_bootconfig_info.magic_start != _SMEM_DUAL_BOOTINFO_MAGIC_START) || |
| (qca_smem_bootconfig_info.magic_end != _SMEM_DUAL_BOOTINFO_MAGIC_END)) |
| return -ENOMSG; |
| #else |
| ret = smem_read_alloc_entry(SMEM_BOOT_DUALPARTINFO, |
| &dualboot_info, sizeof(AvbABData)); |
| if(ret != 0) { |
| printk("couldn't find a/b boot info from smem\n"); |
| return -ENOMSG; |
| } |
| |
| s0 = dualboot_info.slots[0]; |
| s1 = dualboot_info.slots[1]; |
| printk(" \n Version: %u.%u " |
| "[prio:%u tries:%u succ:%u]" |
| "[prio:%u tries:%u succ:%u]\n", |
| dualboot_info.version_major, dualboot_info.version_minor, |
| s0.priority, s0.tries_remaining, s0.successful_boot, |
| s1.priority, s1.tries_remaining, s1.successful_boot); |
| #endif |
| return 0; |
| } |
| |
| inline |
| unsigned int update_partnames(unsigned int active, int type) |
| { |
| extern char rootfspart[]; |
| extern char kernelpart[]; |
| |
| char *suffix[][2] = { { "_a", "_b" }, { "", "_1" }, }; |
| char *rprefix[] = { "system", "rootfs" }; |
| char *kprefix[] = { "boot", "0:HLOS" }; |
| |
| sprintf(rootfspart, "%s%s", rprefix[type], suffix[type][active]); |
| sprintf(kernelpart, "%s%s", kprefix[type], suffix[type][active]); |
| |
| return active; |
| } |
| |
| unsigned int get_rootfs_active_partition(void) |
| { |
| #ifndef CONFIG_ANDROID_AB_BOOT |
| int i; |
| |
| for (i = 0; i < qca_smem_bootconfig_info.numaltpart; i++) { |
| struct per_part_info *pi; |
| |
| pi = &qca_smem_bootconfig_info.per_part_entry[i]; |
| |
| if (strncmp("system_a", pi->name, ALT_PART_NAME_LENGTH) == 0) |
| return update_partnames(pi->primaryboot, 0); |
| |
| if (strncmp("rootfs", pi->name, ALT_PART_NAME_LENGTH) == 0) |
| return update_partnames(pi->primaryboot, 1); |
| } |
| #else |
| return s1.priority > s0.priority ? 1 : 0; |
| #endif |
| |
| return 0; /* alt partition not available */ |
| } |
| #ifdef CONFIG_CMD_NAND |
| /* |
| * get nand block size by device id. |
| * dev_id is 0 for parallel nand. |
| * dev_id is 1 for spi nand. |
| */ |
| uint32_t get_nand_block_size(uint8_t dev_id) |
| { |
| struct mtd_info *mtd; |
| |
| mtd = &nand_info[dev_id]; |
| |
| return mtd->erasesize; |
| } |
| #endif |
| /* |
| * get flash block size based on partition name. |
| */ |
| static inline uint32_t get_flash_block_size(char *name, |
| qca_smem_flash_info_t *smem) |
| { |
| #ifdef CONFIG_CMD_NAND |
| return (get_which_flash_param(name) == 1) ? |
| get_nand_block_size(is_spi_nand_available()) |
| : smem->flash_block_size; |
| #else |
| return smem->flash_block_size; |
| #endif |
| } |
| |
| #define part_which_flash(p) (((p)->attr & 0xff000000) >> 24) |
| |
| static inline uint32_t get_part_block_size(struct smem_ptn *p, |
| qca_smem_flash_info_t *sfi) |
| { |
| #ifdef CONFIG_CMD_NAND |
| return (part_which_flash(p) == 1) ? |
| get_nand_block_size(is_spi_nand_available()) |
| : sfi->flash_block_size; |
| #else |
| return sfi->flash_block_size; |
| #endif |
| } |
| |
| void qca_set_part_entry(char *name, qca_smem_flash_info_t *smem, |
| qca_part_entry_t *part, uint32_t start, uint32_t size) |
| { |
| uint32_t bsize = get_flash_block_size(name, smem); |
| part->offset = ((loff_t)start) * bsize; |
| part->size = ((loff_t)size) * bsize; |
| } |
| |
| static inline uint32_t get_device_id_by_part(struct smem_ptn *p) |
| { |
| return (part_which_flash(p) == 1) ? |
| is_spi_nand_available() : CONFIG_SPI_FLASH_INFO_IDX; |
| } |
| |
| __weak unsigned int get_smem_spi_addr_len(void) |
| { |
| return SPI_DEFAULT_ADDR_LEN; |
| } |
| |
| unsigned int get_partition_table_offset(void) |
| { |
| int ret; |
| uint32_t primary_mibib; |
| |
| ret = smem_read_alloc_entry(SMEM_PARTITION_TABLE_OFFSET, |
| &primary_mibib, sizeof(uint32_t)); |
| if (ret != 0) { |
| primary_mibib = 0; |
| } |
| |
| return primary_mibib; |
| } |
| |
| /* |
| * smem_getpart_from_offset - retreive partition start and size for given offset |
| * belongs to. |
| * @part_name: offset for which part start and size needed |
| * @start: location where the start offset is to be stored |
| * @size: location where the size is to be stored |
| * |
| * Returns 0 at success or -ENOENT otherwise. |
| */ |
| int smem_getpart_from_offset(uint32_t offset, uint32_t *start, uint32_t *size) |
| { |
| unsigned i; |
| qca_smem_flash_info_t *sfi = &qca_smem_flash_info; |
| struct smem_ptn *p; |
| uint32_t bsize; |
| |
| for (i = 0; i < smem_ptable.len; i++) { |
| p = &smem_ptable.parts[i]; |
| *start = p->start; |
| bsize = get_part_block_size(p, sfi); |
| |
| if (p->size == (~0u)) { |
| /* |
| * Partition size is 'till end of device', calculate |
| * appropriately |
| */ |
| #ifdef CONFIG_CMD_NAND |
| *size = (nand_info[get_device_id_by_part(p)].size / |
| bsize) - p->start; |
| #else |
| *size = 0; |
| bsize = bsize; |
| #endif |
| } else { |
| *size = p->size; |
| } |
| *start = *start * bsize; |
| *size = *size * bsize; |
| if (*start <= offset && *start + *size > offset) { |
| return 0; |
| } |
| } |
| |
| return -ENOENT; |
| |
| } |
| /* |
| * smem_getpart - retreive partition start and size |
| * @part_name: partition name |
| * @start: location where the start offset is to be stored |
| * @size: location where the size is to be stored |
| * |
| * Retreive the start offset in blocks and size in blocks, of the |
| * specified partition. |
| */ |
| int smem_getpart(char *part_name, uint32_t *start, uint32_t *size) |
| { |
| unsigned i; |
| qca_smem_flash_info_t *sfi = &qca_smem_flash_info; |
| struct smem_ptn *p; |
| uint32_t bsize; |
| |
| for (i = 0; i < smem_ptable.len; i++) { |
| if (!strncmp(smem_ptable.parts[i].name, part_name, |
| SMEM_PTN_NAME_MAX)) |
| break; |
| } |
| if (i == smem_ptable.len) |
| return -ENOENT; |
| |
| p = &smem_ptable.parts[i]; |
| bsize = get_part_block_size(p, sfi); |
| |
| *start = p->start; |
| |
| if (p->size == (~0u)) { |
| /* |
| * Partition size is 'till end of device', calculate |
| * appropriately |
| */ |
| #ifdef CONFIG_CMD_NAND |
| *size = (nand_info[get_device_id_by_part(p)].size / |
| bsize) - p->start; |
| #else |
| *size = 0; |
| bsize = bsize; |
| #endif |
| } else { |
| *size = p->size; |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * smem_get_boot_flash - retreive the boot flash info |
| * @flash_type: location where the flash type is to be stored |
| * @flash_index: location where the flash index is to be stored |
| * @flash_chip_select: location where the flash chip select is to be stored |
| * @flash_block_size: location where the block size is to be stored |
| * @flash_density: location where the flash size is to be stored |
| * |
| * Retreive the flash type and flash index, of the boot flash. |
| */ |
| int smem_get_boot_flash(uint32_t *flash_type, |
| uint32_t *flash_index, |
| uint32_t *flash_chip_select, |
| uint32_t *flash_block_size, |
| uint32_t *flash_density) |
| { |
| int ret; |
| |
| ret = smem_read_alloc_entry(SMEM_BOOT_FLASH_TYPE, |
| flash_type, sizeof(uint32_t)); |
| if (ret != 0) { |
| printf("smem: read flash type failed\n"); |
| *flash_type = SMEM_BOOT_NO_FLASH; |
| } |
| |
| ret = smem_read_alloc_entry(SMEM_BOOT_FLASH_INDEX, |
| flash_index, sizeof(uint32_t)); |
| if (ret != 0) { |
| printf("smem: read flash index failed\n"); |
| *flash_index = 0; |
| } |
| |
| ret = smem_read_alloc_entry(SMEM_BOOT_FLASH_CHIP_SELECT, |
| flash_chip_select, sizeof(uint32_t)); |
| if (ret != 0) { |
| printf("smem: read flash chip select failed\n"); |
| *flash_chip_select = 0; |
| } |
| |
| ret = smem_read_alloc_entry(SMEM_BOOT_FLASH_BLOCK_SIZE, |
| flash_block_size, sizeof(uint32_t)); |
| if (ret != 0) { |
| printf("smem: read flash block size failed\n"); |
| *flash_block_size = 0; |
| } |
| ret = smem_read_alloc_entry(SMEM_BOOT_FLASH_DENSITY, |
| flash_density, sizeof(uint32_t)); |
| if (ret != 0) { |
| printf("smem: read flash density failed\n"); |
| *flash_density = 0; |
| } |
| |
| return 0; |
| } |
| |
| int ipq_smem_get_boot_version(char *version_name, int buf_size) |
| { |
| int ret; |
| qca_platform_v1 platform_info; |
| |
| ret = smem_read_alloc_entry(SMEM_HW_SW_BUILD_ID, &platform_info, |
| sizeof(qca_platform_v1)); |
| if (ret != 0) |
| return -ENOMSG; |
| |
| snprintf(version_name, buf_size, "%s\n", platform_info.build_id); |
| return ret; |
| } |
| |
| int ipq_smem_get_boot_flash(uint32_t *flash_name) |
| { |
| int ret; |
| |
| ret = smem_read_alloc_entry(SMEM_BOOT_FLASH_TYPE, |
| flash_name, sizeof(uint32_t)); |
| if (ret != 0) |
| return -ENOMSG; |
| |
| return ret; |
| } |
| |
| int smem_get_build_version(char *version_name, int buf_size, int index) |
| { |
| int ret; |
| struct version_entry version_entry[32]; |
| |
| ret = smem_read_alloc_entry(SMEM_IMAGE_VERSION_TABLE, |
| &version_entry, sizeof(version_entry)); |
| if (ret != 0) |
| return -ENOMSG; |
| |
| snprintf(version_name, buf_size, "%s-%s\n", |
| version_entry[index].oem_version_string, |
| version_entry[index].version_string); |
| |
| return ret; |
| } |
| |
| unsigned int smem_read_platform_type(union qca_platform *platform_type) |
| { |
| unsigned status; |
| |
| status = smem_read_alloc_entry(SMEM_HW_SW_BUILD_ID, platform_type, |
| sizeof(qca_platform_v1)); |
| if (!status) |
| return status; |
| |
| debug("smem: Mapping platform type v1 failed. Trying v2...\n"); |
| status = smem_read_alloc_entry(SMEM_HW_SW_BUILD_ID, |
| platform_type, |
| sizeof(qca_platform_v2)); |
| if (!status) |
| return status; |
| |
| debug("smem: Mapping platform type v2 failed. Trying v3...\n"); |
| status = smem_read_alloc_entry(SMEM_HW_SW_BUILD_ID, |
| platform_type, |
| sizeof(qca_platform_v3)); |
| return status; |
| } |
| |
| __weak unsigned int get_dts_machid(unsigned int machid) |
| { |
| return machid; |
| } |
| |
| unsigned int smem_get_board_platform_type() |
| { |
| union qca_platform platform_type; |
| struct smem_machid_info machid_info; |
| unsigned int machid = 0; |
| |
| if (!smem_read_alloc_entry(SMEM_MACHID_INFO_LOCATION, |
| &machid_info, sizeof(machid_info))) { |
| machid = machid_info.machid; |
| return machid; |
| } |
| |
| if (!smem_read_alloc_entry(SMEM_HW_SW_BUILD_ID, |
| &platform_type, sizeof(qca_platform_v2)) || |
| !smem_read_alloc_entry(SMEM_HW_SW_BUILD_ID, |
| &platform_type, sizeof(qca_platform_v3))) { |
| machid = ((platform_type.v1.hw_platform << 24) | |
| ((SOCINFO_VERSION_MAJOR(platform_type.v1.platform_version)) << 16) | |
| ((SOCINFO_VERSION_MINOR(platform_type.v1.platform_version)) << 8) | |
| (platform_type.v1.hw_platform_subtype)); |
| |
| return machid; |
| } |
| printf("smem: Failed to fetch the board machid.\n"); |
| return 0; |
| } |
| |
| int ipq_smem_get_socinfo_cpu_type(uint32_t *cpu_type) |
| { |
| unsigned int smem_status; |
| union qca_platform platform_type; |
| |
| smem_status = smem_read_platform_type(&platform_type); |
| |
| if (!smem_status) { |
| *cpu_type = platform_type.v1.id; |
| debug("smem: socinfo - cpu type = %d\n",*cpu_type); |
| } else { |
| printf("smem: Get socinfo - cpu type failed\n"); |
| } |
| |
| return smem_status; |
| } |
| |
| int ipq_smem_get_socinfo_version(uint32_t *version) |
| { |
| unsigned int smem_status; |
| union qca_platform platform_type; |
| |
| smem_status = smem_read_platform_type(&platform_type); |
| |
| if (!smem_status) { |
| *version = platform_type.v1.version; |
| debug("smem: socinfo - version = 0x%x\n",*version); |
| } else { |
| printf("smem: Get socinfo - version failed\n"); |
| } |
| |
| return smem_status; |
| } |
| |
| /* |
| * smem_ptable_init - initializes RAM partition table from SMEM |
| * |
| */ |
| int smem_ram_ptable_init(struct smem_ram_ptable *smem_ram_ptable) |
| { |
| unsigned i; |
| |
| i = smem_read_alloc_entry(SMEM_USABLE_RAM_PARTITION_TABLE, |
| smem_ram_ptable, |
| sizeof(struct smem_ram_ptable)); |
| if (i != 0) |
| return 0; |
| |
| if (smem_ram_ptable->magic[0] != _SMEM_RAM_PTABLE_MAGIC_1 || |
| smem_ram_ptable->magic[1] != _SMEM_RAM_PTABLE_MAGIC_2) |
| return 0; |
| |
| printf("smem ram ptable found: ver: %d len: %d\n", |
| smem_ram_ptable->version, smem_ram_ptable->len); |
| |
| |
| return 1; |
| } |
| |
| /* |
| * smem_ptable_init - initializes RAM partition table from SMEM |
| * |
| */ |
| #ifdef CONFIG_SMEM_VERSION_C |
| int smem_ram_ptable_init_v2(struct usable_ram_partition_table *usable_ram_partition_table) |
| { |
| unsigned i; |
| |
| i = smem_read_alloc_entry(SMEM_USABLE_RAM_PARTITION_TABLE, |
| usable_ram_partition_table, |
| sizeof(struct usable_ram_partition_table)); |
| if (i != 0) |
| return 0; |
| |
| if (usable_ram_partition_table->magic1 != _SMEM_RAM_PTABLE_MAGIC_1 || |
| usable_ram_partition_table->magic2 != _SMEM_RAM_PTABLE_MAGIC_2) { |
| return 0; |
| } |
| return 1; |
| } |
| #endif |
| |
| void qca_smem_part_to_mtdparts(char *mtdid, int len) |
| { |
| qca_smem_flash_info_t *sfi = &qca_smem_flash_info; |
| int i, ret; |
| int device_id = 0; |
| char *part = mtdid, *unit; |
| int init = 0; |
| uint32_t bsize; |
| |
| ret = snprintf(part, len, "%s:", mtdid); |
| part += ret; |
| len -= ret; |
| |
| for (i = 0; i < smem_ptable.len && len > 0; i++) { |
| struct smem_ptn *p = &smem_ptable.parts[i]; |
| loff_t psize; |
| bsize = get_part_block_size(p, sfi); |
| |
| if (part_which_flash(p) && init == 0) { |
| device_id = is_spi_nand_available(); |
| ret = snprintf(part, len, ";nand%d:", device_id); |
| part += ret; |
| len -= ret; |
| init = 1; |
| } |
| if (p->size == (~0u)) { |
| /* |
| * Partition size is 'till end of device', calculate |
| * appropriately |
| */ |
| #ifdef CONFIG_CMD_NAND |
| psize = (nand_info[get_device_id_by_part(p)].size |
| - (((loff_t)p->start) * bsize)); |
| #else |
| psize = 0; |
| #endif |
| } else { |
| psize = ((loff_t)p->size) * bsize; |
| } |
| |
| if ((psize > SZ_1M) && (((psize & (SZ_1M - 1)) == 0))) { |
| psize /= SZ_1M; |
| unit = "M@"; |
| } else if ((psize > SZ_1K) && (((psize & (SZ_1K - 1)) == 0))) { |
| psize /= SZ_1K; |
| unit = "K@"; |
| } else { |
| unit = "@"; |
| } |
| |
| ret = snprintf(part, len, "%lld%s0x%llx(%s),", psize, unit, |
| ((loff_t)p->start) * bsize, p->name); |
| part += ret; |
| len -= ret; |
| } |
| |
| if (i == 0) |
| *mtdid = '\0'; |
| |
| *(part-1) = 0; /* Remove the trailing ',' character */ |
| } |
| |
| /* |
| * retrieve the which_flash flag based on partition name. |
| * flash_var is 1 if partition is in NAND. |
| * flash_var is 0 if partition is in NOR. |
| */ |
| unsigned int get_which_flash_param(char *part_name) |
| { |
| int i; |
| int flash_var = 0; |
| |
| for (i = 0; i < smem_ptable.len; i++) { |
| struct smem_ptn *p = &smem_ptable.parts[i]; |
| if (strcmp(p->name, part_name) == 0) |
| flash_var = part_which_flash(p); |
| } |
| |
| return flash_var; |
| } |
| |
| /* |
| * getpart_offset_size - retreive partition offset and size |
| * @part_name - partition name |
| * @offset - location where the offset of partition to be stored |
| * @size - location where partition size to be stored |
| * |
| * Retreive partition offset and size in bytes with respect to the |
| * partition specific flash block size |
| */ |
| int getpart_offset_size(char *part_name, uint32_t *offset, uint32_t *size) |
| { |
| int i; |
| uint32_t bsize; |
| qca_smem_flash_info_t *sfi = &qca_smem_flash_info; |
| |
| for (i = 0; i < smem_ptable.len; i++) { |
| struct smem_ptn *p = &smem_ptable.parts[i]; |
| loff_t psize; |
| if (!strncmp(p->name, part_name, SMEM_PTN_NAME_MAX)) { |
| bsize = get_part_block_size(p, sfi); |
| if (p->size == (~0u)) { |
| /* |
| * Partition size is 'till end of device', calculate |
| * appropriately |
| */ |
| #ifdef CONFIG_CMD_NAND |
| psize = nand_info[get_device_id_by_part(p)].size |
| - (((loff_t)p->start) * bsize); |
| #else |
| psize = 0; |
| #endif |
| } else { |
| psize = ((loff_t)p->size) * bsize; |
| } |
| |
| *offset = ((loff_t)p->start) * bsize; |
| *size = psize; |
| break; |
| } |
| } |
| |
| if (i == smem_ptable.len) |
| return -ENOENT; |
| |
| return 0; |
| } |
| |
| #ifdef IPQ_UBI_VOL_WRITE_SUPPORT |
| int ubi_set_rootfs_part(void) |
| { |
| int ret; |
| uint32_t offset; |
| uint32_t part_size = 0; |
| uint32_t size_block, start_block; |
| qca_smem_flash_info_t *sfi = &qca_smem_flash_info; |
| char runcmd[256]; |
| int i; |
| |
| if (((sfi->flash_type == SMEM_BOOT_NAND_FLASH) || |
| (sfi->flash_type == SMEM_BOOT_QSPI_NAND_FLASH))) { |
| ret = smem_getpart(QCA_ROOT_FS_PART_NAME, |
| &start_block, &size_block); |
| if (ret) |
| return ret; |
| |
| offset = sfi->flash_block_size * start_block; |
| part_size = sfi->flash_block_size * size_block; |
| } else if (sfi->flash_type == SMEM_BOOT_SPI_FLASH && |
| get_which_flash_param(QCA_ROOT_FS_PART_NAME)) { |
| ret = getpart_offset_size(QCA_ROOT_FS_PART_NAME, &offset, |
| &part_size); |
| if (ret) |
| return ret; |
| } |
| |
| if (!part_size) |
| return -ENOENT; |
| |
| if (ubi) { |
| for (i = 0; i < ubi->vtbl_slots; i++) { |
| if (ubi->volumes[i]) { |
| kfree(ubi->volumes[i]->eba_tbl); |
| kfree(ubi->volumes[i]); |
| ubi->volumes[i] = NULL; |
| } |
| } |
| } |
| |
| snprintf(runcmd, sizeof(runcmd), |
| "nand device %d && " |
| "setenv mtdids nand%d=nand%d && " |
| "setenv mtdparts mtdparts=nand%d:0x%x@0x%x(fs),${msmparts} && " |
| "ubi part fs && ", is_spi_nand_available(), |
| is_spi_nand_available(), |
| is_spi_nand_available(), |
| is_spi_nand_available(), |
| part_size, offset); |
| |
| if (run_command(runcmd, 0) != CMD_RET_SUCCESS) |
| return CMD_RET_FAILURE; |
| |
| if (ubi) { |
| kfree(ubi); |
| ubi = NULL; |
| } |
| |
| ubi = ubi_devices[0]; |
| return 0; |
| } |
| |
| static void print_ubi_vol_info(void) |
| { |
| int i; |
| int j=0; |
| struct ubi_volume *vol; |
| |
| for (i = 0; i < (ubi->vtbl_slots + 1); i++) { |
| vol = ubi->volumes[i]; |
| if (!vol) |
| continue; /* Empty record */ |
| if (vol->name_len <= UBI_VOL_NAME_MAX && strnlen(vol->name, |
| vol->name_len + 1) == vol->name_len) { |
| printf("\tubi vol %d %s\n", vol->vol_id, vol->name); |
| j++; |
| } else { |
| printf("\tubi vol %d %c%c%c%c%c\n", |
| vol->vol_id, vol->name[0], vol->name[1], |
| vol->name[2], vol->name[3], vol->name[4]); |
| j++; |
| } |
| if (j == ubi->vol_count - UBI_INT_VOL_COUNT) |
| break; |
| } |
| } |
| #endif |
| |
| int do_smeminfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) |
| { |
| qca_smem_flash_info_t *sfi = &qca_smem_flash_info; |
| int i; |
| uint32_t bsize; |
| #ifdef IPQ_UBI_VOL_WRITE_SUPPORT |
| ubi_set_rootfs_part(); |
| #endif |
| if(sfi->flash_density != 0) { |
| printf( "flash_type: 0x%x\n" |
| "flash_index: 0x%x\n" |
| "flash_chip_select: 0x%x\n" |
| "flash_block_size: 0x%x\n" |
| "flash_density: 0x%x\n" |
| "partition table offset 0x%x\n", |
| sfi->flash_type, sfi->flash_index, |
| sfi->flash_chip_select, sfi->flash_block_size, |
| sfi->flash_density, get_partition_table_offset()); |
| } else { |
| printf( "flash_type: 0x%x\n" |
| "flash_index: 0x%x\n" |
| "flash_chip_select: 0x%x\n" |
| "flash_block_size: 0x%x\n" |
| "partition table offset 0x%x\n", |
| sfi->flash_type, sfi->flash_index, |
| sfi->flash_chip_select, sfi->flash_block_size, |
| get_partition_table_offset()); |
| } |
| |
| if (smem_ptable.len > 0) { |
| printf("%-3s: " smem_ptn_name_fmt " %10s %16s %16s\n", |
| "No.", "Name", "Attributes", "Start", "Size"); |
| } else { |
| printf("Partition information not available\n"); |
| } |
| |
| for (i = 0; i < smem_ptable.len; i++) { |
| struct smem_ptn *p = &smem_ptable.parts[i]; |
| loff_t psize; |
| bsize = get_part_block_size(p, sfi); |
| |
| if (p->size == (~0u)) { |
| /* |
| * Partition size is 'till end of device', calculate |
| * appropriately |
| */ |
| #ifdef CONFIG_CMD_NAND |
| psize = nand_info[get_device_id_by_part(p)].size |
| - (((loff_t)p->start) * bsize); |
| #else |
| psize = 0; |
| #endif |
| } else { |
| psize = ((loff_t)p->size) * bsize; |
| } |
| |
| printf("%3d: " smem_ptn_name_fmt " 0x%08x %#16llx %#16llx\n", |
| i, p->name, p->attr, ((loff_t)p->start) * bsize, psize); |
| #ifdef IPQ_UBI_VOL_WRITE_SUPPORT |
| if (!strncmp(p->name, QCA_ROOT_FS_PART_NAME, SMEM_PTN_NAME_MAX) |
| && ubi) { |
| print_ubi_vol_info(); |
| } |
| #endif |
| } |
| return 0; |
| } |
| |
| U_BOOT_CMD( |
| smeminfo, 1, 1, do_smeminfo, |
| "print SMEM FLASH information", |
| "\n - print flash details gathered from SMEM\n" |
| ); |