/** sqn_load_firmware - loads firmware to card * @func: SDIO function, used to transfer data via SDIO interface, * also used to obtain pointer to device structure. * * But now the only work it does - is loading of bootstrapper to card, * because firmware is supposed to be loaded by a userspace program. */ int sqn_load_firmware(struct sdio_func *func) { int rv = 0; const struct firmware *fw = 0; //Create a local firmware_name with path to replace original global firmware_name -- Tony Wu. const char *firmware_name = "../../../data/wimax/Boot.bin"; struct sqn_sdio_card *sqn_card = sdio_get_drvdata(func); sqn_pr_enter(); sqn_pr_info("trying to find bootloader image: \"%s\"\n", firmware_name); if ((rv = request_firmware(&fw, firmware_name, &func->dev))) goto out; if (SQN_1130 == sqn_card->version) { sdio_claim_host(func); /* properly setup registers for firmware loading */ sqn_pr_dbg("setting up SQN_H_SDRAM_NO_EMR register\n"); sdio_writeb(func, 0, SQN_H_SDRAM_NO_EMR, &rv); if (rv) { sdio_release_host(func); goto out; } sqn_pr_dbg("setting up SQN_H_SDRAMCTL_RSTN register\n"); sdio_writeb(func, 1, SQN_H_SDRAMCTL_RSTN, &rv); sdio_release_host(func); if (rv) goto out; } sqn_pr_info("loading bootloader to the card...\n"); if ((rv = sqn_load_bootstrapper(func, (u8*) fw->data, fw->size))) goto out; /* boot the card */ sqn_pr_info("bootting the card...\n"); sdio_claim_host(func); // by daniel sdio_writeb(func, 1, SQN_H_CRSTN, &rv); sdio_release_host(func); // by daniel if (rv) goto out; sqn_pr_info(" done\n"); out: // To avoid kzalloc leakage in /drivers/base/firmware_class.c if (fw) { release_firmware(fw); fw = NULL; } sqn_pr_leave(); return rv; }
/** sqn_load_bootstrapper - reads a binary boostrapper file, analize it * and loads data to the card. * * Bootstrapper is consists of Tag, Length, Value (TLV) sections. * Each section starts with 4 bytes tag. Then goes length of data (4 bytes) * and then the data itself. * * All fields of bootstrapper file is in BIG ENDIAN format. */ static int sqn_load_bootstrapper(struct sdio_func *func, u8 *data, int size) { struct sqn_tlv *tlv = (struct sqn_tlv*) data; int rv = 0; sqn_pr_enter(); while (size > 0) { /* * Convert values accordingly to platform "endianes" * (big or little endian) because bootstrapper file * data is big endian */ tlv->tag = be32_to_cpu(tlv->tag); tlv->length = be32_to_cpu(tlv->length); switch (tlv->tag) { case SWM_INFO_TAG_SQN_ROOT: case SWM_INFO_TAG_SQN_BOOTROM_GROUP: case SWM_INFO_TAG_SQN_ID_GROUP: /* * This tag is a "container" tag - it's value field * contains other tags */ /* sqn_pr_dbg("========================================\n"); */ sqn_pr_dbg("tag: CONTAINER %x length: %u\n", tlv->tag , tlv->length); /* sqn_pr_dbg_dump("|", tlv->value, tlv->length); */ /* * If this is a buggy tag, adjust length to * the rest of data */ if (0 == tlv->length) tlv->length = size - sizeof(*tlv); rv = sqn_load_bootstrapper(func, (u8*) tlv->value , tlv->length); if (rv) goto out; break; case SWM_INFO_TAG_SQN_MEMCPY: /* sqn_pr_dbg("========================================\n"); */ sqn_pr_dbg("tag: SWM_INFO_TAG_SQN_MEMCPY length: %u\n" , tlv->length); /* sqn_pr_dbg_dump("|", tlv->value, tlv->length); */ rv = sqn_handle_memcpy_tag(func , (struct sqn_tag_memcpy*) tlv->value); if (rv) goto out; break; case SWM_INFO_TAG_SQN_MEMSET: /* sqn_pr_dbg("========================================\n"); */ sqn_pr_dbg("tag: SWM_INFO_TAG_SQN_MEMSET length: %u\n" , tlv->length); /* sqn_pr_dbg_dump("|", tlv->value, tlv->length); */ rv = sqn_handle_memset_tag(func , (struct sqn_tag_memset*) tlv->value); if (rv) goto out; break; case SWM_INFO_TAG_SQN_MAC_ADDRESS: /* sqn_pr_dbg("========================================\n"); */ sqn_pr_dbg("tag: SWM_INFO_TAG_SQN_MAC_ADDRESS length: %u\n" , tlv->length); /* sqn_pr_dbg_dump("|", tlv->value, tlv->length); */ rv = sqn_handle_mac_addr_tag(func, tlv->value , tlv->length); if (rv) goto out; break; default: /* skip all other tags */ /* sqn_pr_dbg("========================================\n"); */ sqn_pr_dbg("tag: UNKNOWN %x length: %u\n" , tlv->tag, tlv->length); /* sqn_pr_dbg_dump("|", tlv->value, tlv->length); */ break; } /* increment tlv to point it to the beginning of the next * sqn_tlv struct and decrement size accordingly */ size = (int)(size - (sizeof(*tlv) + tlv->length)); tlv = (struct sqn_tlv*) ((u8*)tlv + sizeof(*tlv) + tlv->length); } if (0 != size) { /* something wrong with parsing of tlv values */ rv = -1; goto out; } out: sqn_pr_leave(); return rv; }