123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622 |
- /*
- * Freescale UUT driver
- *
- * Copyright 2008-2013 Freescale Semiconductor, Inc.
- * Copyright 2008-2009 Embedded Alley Solutions, Inc All Rights Reserved.
- */
- /*
- * The code contained herein is licensed under the GNU General Public
- * License. You may obtain a copy of the GNU General Public License
- * Version 2 or later at the following locations:
- *
- * http://www.opensource.org/licenses/gpl-license.html
- * http://www.gnu.org/copyleft/gpl.html
- */
- #include <common.h>
- #include <asm/arch/vf610_qspi.h>
- #define pr_debug printf
- #define pr_info printf
- #define UTP_FLAG_COMMAND 0x00000001
- #define UTP_FLAG_DATA 0x00000002
- #define UTP_FLAG_STATUS 0x00000004
- #define UTP_FLAG_REPORT_BUSY 0x10000000
- struct fsg_common;
- enum utp_media_type {
- NOMEDIA = 0,
- SD,
- NAND,
- NOR,
- SPI,
- QSPI,
- };
- struct utp_pipe_info {
- enum utp_media_type media_type;
- int media_id;
- unsigned long int index;
- size_t blksz;
- unsigned long int addr;
- } pinfo = {
- .media_type = NOMEDIA,
- .media_id = 0,
- .index = 0,
- .blksz = 0,
- };
- static u64 get_be64(u8 *buf)
- {
- return ((u64)get_unaligned_be32(buf) << 32) |
- get_unaligned_be32(buf + 4);
- }
- static int utp_init(struct fsg_dev *fsg)
- {
- /* the max message is 64KB */
- utp_context.buffer = vmalloc(0x10000);
- if (!utp_context.buffer) {
- printf("not enough memory!\n");
- return -EIO;
- }
- utp_context.utp_version = 0x1ull;
- fsg->utp = &utp_context;
- return 0;
- }
- static void utp_exit(struct fsg_dev *fsg)
- {
- vfree(utp_context.buffer);
- }
- static struct utp_user_data *utp_user_data_alloc(size_t blksz, size_t size)
- {
- struct utp_user_data *uud;
- size_t malloc_size = size + sizeof(*uud) + blksz;
- uud = malloc(malloc_size);
- if (!uud)
- return uud;
- memset(uud, 0, malloc_size);
- uud->data.size = size + sizeof(uud->data);
- return uud;
- }
- static void utp_user_data_free(struct utp_user_data *uud)
- {
- kfree(uud);
- }
- /* Will be called when the host wants to get the sense data */
- static int utp_get_sense(struct fsg_common *common)
- {
- struct fsg_dev *fsg;
- fsg = common->fsg;
- if (UTP_CTX(fsg)->processed == 0)
- return -1;
- UTP_CTX(fsg)->processed = 0;
- return 0;
- }
- static int utp_do_read(struct fsg_common *common, void *data, size_t size)
- {
- struct fsg_buffhd *bh;
- struct fsg_dev *fsg;
- int rc;
- u32 amount_left;
- unsigned int amount;
- /* Get the starting Logical Block Address and check that it's
- * not too big */
- fsg = common->fsg;
- amount_left = size;
- if (unlikely(amount_left == 0))
- return -EIO; /* No default reply*/
- pr_debug("%s: sending %d\n", __func__, size);
- for (;;) {
- /* Figure out how much we need to read:
- * Try to read the remaining amount.
- * But don't read more than the buffer size.
- * And don't try to read past the end of the file.
- * Finally, if we're not at a page boundary, don't read past
- * the next page.
- * If this means reading 0 then we were asked to read past
- * the end of file. */
- amount = min((unsigned int) amount_left, FSG_BUFLEN);
- /* Wait for the next buffer to become available */
- bh = common->next_buffhd_to_fill;
- while (bh->state != BUF_STATE_EMPTY) {
- rc = sleep_thread(common);
- if (rc)
- return rc;
- }
- /* If we were asked to read past the end of file,
- * end with an empty buffer. */
- if (amount == 0) {
- bh->inreq->length = 0;
- bh->state = BUF_STATE_FULL;
- break;
- }
- /* Perform the read */
- pr_info("Copied to %p, %d bytes started from %d\n",
- bh->buf, amount, size - amount_left);
- /* from upt buffer to file_storeage buffer */
- memcpy(bh->buf, data + size - amount_left, amount);
- amount_left -= amount;
- common->residue -= amount;
- bh->inreq->length = amount;
- bh->state = BUF_STATE_FULL;
- /* Send this buffer and go read some more */
- bh->inreq->zero = 0;
- /* USB Physical transfer: Data from device to host */
- start_transfer(fsg, fsg->bulk_in, bh->inreq,
- &bh->inreq_busy, &bh->state);
- common->next_buffhd_to_fill = bh->next;
- if (amount_left <= 0)
- break;
- }
- return size - amount_left;
- }
- static int utp_do_write(struct fsg_common *common, void *data, size_t size)
- {
- struct fsg_buffhd *bh;
- struct fsg_dev *fsg;
- int get_some_more;
- u32 amount_left_to_req, amount_left_to_write;
- unsigned int amount;
- int rc;
- loff_t offset;
- fsg = common->fsg;
- /* Carry out the file writes */
- get_some_more = 1;
- amount_left_to_req = amount_left_to_write = size;
- if (unlikely(amount_left_to_write == 0))
- return -EIO;
- offset = 0;
- while (amount_left_to_write > 0) {
- /* Queue a request for more data from the host */
- bh = common->next_buffhd_to_fill;
- if (bh->state == BUF_STATE_EMPTY && get_some_more) {
- /* Figure out how much we want to get:
- * Try to get the remaining amount.
- * But don't get more than the buffer size.
- * And don't try to go past the end of the file.
- * If we're not at a page boundary,
- * don't go past the next page.
- * If this means getting 0, then we were asked
- * to write past the end of file.
- * Finally, round down to a block boundary. */
- amount = min(amount_left_to_req, FSG_BUFLEN);
- if (amount == 0) {
- get_some_more = 0;
- /* cry now */
- continue;
- }
- /* Get the next buffer */
- amount_left_to_req -= amount;
- if (amount_left_to_req == 0)
- get_some_more = 0;
- /* amount is always divisible by 512, hence by
- * the bulk-out maxpacket size */
- bh->outreq->length = bh->bulk_out_intended_length =
- amount;
- bh->outreq->short_not_ok = 1;
- start_transfer(fsg, fsg->bulk_out, bh->outreq,
- &bh->outreq_busy, &bh->state);
- common->next_buffhd_to_fill = bh->next;
- continue;
- }
- /* Write the received data to the backing file */
- bh = common->next_buffhd_to_drain;
- if (bh->state == BUF_STATE_EMPTY && !get_some_more)
- break; /* We stopped early */
- if (bh->state == BUF_STATE_FULL) {
- common->next_buffhd_to_drain = bh->next;
- bh->state = BUF_STATE_EMPTY;
- /* Did something go wrong with the transfer? */
- if (bh->outreq->status != 0)
- /* cry again, COMMUNICATION_FAILURE */
- break;
- amount = bh->outreq->actual;
- /* Perform the write */
- memcpy(data + offset, bh->buf, amount);
- offset += amount;
- amount_left_to_write -= amount;
- common->residue -= amount;
- /* Did the host decide to stop early? */
- if (bh->outreq->actual != bh->outreq->length) {
- common->short_packet_received = 1;
- break;
- }
- continue;
- }
- /* Wait for something to happen */
- rc = sleep_thread(common);
- if (rc)
- return rc;
- }
- return -EIO;
- }
- static inline void utp_set_sense(struct fsg_common *common, u16 code, u64 reply)
- {
- struct fsg_dev *fsg;
- fsg = common->fsg;
- UTP_CTX(fsg)->processed = true;
- UTP_CTX(fsg)->sdinfo = reply & 0xFFFFFFFF;
- UTP_CTX(fsg)->sdinfo_h = (reply >> 32) & 0xFFFFFFFF;
- UTP_CTX(fsg)->sd = (UTP_SENSE_KEY << 16) | code;
- }
- static void utp_poll(struct fsg_common *common)
- {
- UTP_SS_PASS(common);
- return;
- }
- static int utp_exec(struct fsg_common *common,
- char *command,
- int cmdsize,
- unsigned long long payload)
- {
- struct fsg_dev *fsg = common->fsg;
- struct utp_user_data *uud = NULL, *uud2r;
- struct utp_context *ctx = UTP_CTX(fsg);
- uud2r = utp_user_data_alloc(0, cmdsize + 1);
- uud2r->data.flags = UTP_FLAG_COMMAND;
- uud2r->data.payload = payload;
- strncpy(uud2r->data.command, command, cmdsize);
- uud = utp_interpret_message(uud2r);
- utp_user_data_free(uud2r);
- if (command[0] == '!') /* there will be no response */
- return 0;
- #ifdef DEBUG
- pr_info("UUD:\n\tFlags = %02X\n", uud->data.flags);
- if (uud->data.flags & UTP_FLAG_DATA) {
- pr_info("\tbufsize = %d\n", uud->data.bufsize);
- print_hex_dump(KERN_DEBUG, "\t", DUMP_PREFIX_NONE,
- 16, 2, uud->data.data, uud->data.bufsize, true);
- }
- if (uud->data.flags & UTP_FLAG_REPORT_BUSY)
- pr_info("\tBUSY\n");
- #endif
- if (uud->data.flags & UTP_FLAG_DATA) {
- memcpy(ctx->buffer, uud->data.data, uud->data.bufsize);
- UTP_SS_SIZE(common, uud->data.bufsize);
- } else if (uud->data.flags & UTP_FLAG_REPORT_BUSY) {
- ctx->counter = 0xFFFF;
- UTP_SS_BUSY(common, ctx->counter);
- } else if (uud->data.flags & UTP_FLAG_STATUS) {
- printk(KERN_WARNING "%s: exit with status %d\n", __func__,
- uud->data.status);
- UTP_SS_EXIT(common, uud->data.status);
- } else {
- pr_debug("%s: pass\n", __func__);
- UTP_SS_PASS(common);
- }
- utp_user_data_free(uud);
- return 0;
- }
- static int utp_send_status(struct fsg_common *common)
- {
- struct fsg_buffhd *bh;
- struct fsg_dev *fsg = common->fsg;
- u8 status = USB_STATUS_PASS;
- struct bulk_cs_wrap *csw;
- int rc;
- /* Wait for the next buffer to become available */
- bh = common->next_buffhd_to_fill;
- while (bh->state != BUF_STATE_EMPTY) {
- rc = sleep_thread(common);
- if (rc)
- return rc;
- }
- if (common->phase_error) {
- DBG(fsg, "sending phase-error status\n");
- status = USB_STATUS_PHASE_ERROR;
- } else if ((UTP_CTX(fsg)->sd & 0xFFFF) != UTP_REPLY_PASS) {
- status = USB_STATUS_FAIL;
- }
- csw = bh->buf;
- /* Store and send the Bulk-only CSW */
- csw->Signature = __constant_cpu_to_le32(USB_BULK_CS_SIG);
- csw->Tag = common->tag;
- csw->Residue = cpu_to_le32(common->residue);
- csw->Status = status;
- bh->inreq->length = USB_BULK_CS_WRAP_LEN;
- bh->inreq->zero = 0;
- start_transfer(fsg, fsg->bulk_in, bh->inreq,
- &bh->inreq_busy, &bh->state);
- common->next_buffhd_to_fill = bh->next;
- return 0;
- }
- static int utp_handle_message(struct fsg_common *common,
- u8 *cdb_data,
- int default_reply)
- {
- struct utp_msg *m = (struct utp_msg *)cdb_data;
- struct fsg_dev *fsg = common->fsg;
- void *data = NULL;
- int r;
- struct utp_user_data *uud2r, *uud;
- unsigned long long param;
- unsigned long tag;
- if (m->f0 != 0xF0)
- return default_reply;
- tag = get_unaligned_be32((void *)&m->utp_msg_tag);
- param = get_be64((void *)&m->param);
- pr_debug("Type 0x%x, tag 0x%08lx, param %llx\n",
- m->utp_msg_type, tag, param);
- switch ((enum utp_msg_type)m->utp_msg_type) {
- case UTP_POLL:
- if (get_be64((void *)&m->param) == 1) {
- pr_debug("%s: version request\n", __func__);
- UTP_SS_EXIT(common, UTP_CTX(fsg)->utp_version);
- break;
- }
- utp_poll(common);
- break;
- case UTP_EXEC:
- pr_debug("%s: EXEC\n", __func__);
- data = kzalloc(common->data_size, GFP_KERNEL);
- /* copy data from usb buffer to utp buffer */
- utp_do_write(common, data, common->data_size);
- utp_exec(common, data, common->data_size, param);
- kfree(data);
- break;
- case UTP_GET: /* data from device to host */
- pr_debug("%s: GET, %d bytes\n", __func__, common->data_size);
- r = utp_do_read(common, UTP_CTX(fsg)->buffer, common->data_size);
- UTP_SS_PASS(common);
- break;
- case UTP_PUT: /* data from host to device */
- pr_debug("%s: PUT, %d bytes\n", __func__, common->data_size);
- uud2r = utp_user_data_alloc(pinfo.blksz, common->data_size);
- if (!uud2r) {
- printf("utp alloc fail\n");
- return -ENOMEM;
- }
- uud2r->data.bufsize = common->data_size;
- uud2r->data.flags = UTP_FLAG_DATA;
- utp_do_write(common, uud2r->data.data, common->data_size);
- /* don't know what will be written */
- utp_interpret_message(uud2r);
- utp_user_data_free(uud2r);
- /*
- * Return PASS or FAIL according to uuc's status
- * Please open it if need to check uuc's status
- * and use another version uuc
- */
- #if 0
- struct utp_user_data *uud = NULL;
- struct utp_context *ctx;
- WAIT_ACTIVITY(write);
- ctx = UTP_CTX(fsg);
- mutex_lock(&ctx->lock);
- if (!list_empty(&ctx->write))
- uud = list_first_entry(&ctx->write,
- struct utp_user_data, link);
- mutex_unlock(&ctx->lock);
- if (uud) {
- if (uud->data.flags & UTP_FLAG_STATUS) {
- printk(KERN_WARNING "%s: exit with status %d\n",
- __func__, uud->data.status);
- UTP_SS_EXIT(fsg, uud->data.status);
- } else {
- pr_debug("%s: pass\n", __func__);
- UTP_SS_PASS(fsg);
- }
- utp_user_data_free(uud);
- } else{
- UTP_SS_PASS(fsg);
- }
- #endif
- /* workaround, normally would SS_PASS */
- if (common->data_size == 65536)
- {
- pr_debug("%s: Sending UTP_SS_BUSY\n", __func__);
- UTP_SS_BUSY(common, UTP_CTX(fsg)->counter);
- }
- else
- {
- pr_debug("%s: Sending UTP_SS_PASS\n", __func__);
- UTP_SS_PASS(common);
- }
- break;
- }
- utp_send_status(common);
- return -1;
- }
- u32 utp_run(char *cmd)
- {
- return run_command(cmd, 0);
- }
- struct utp_user_data *utp_handle_command(char *cmd, u64 payload)
- {
- u32 flags, status;
- size_t size;
- struct utp_user_data *answer;
- unsigned long int dev;
- unsigned long int skip;
- unsigned long int addr;
- char argbuf[20];
- char *tmp;
- /* defaults */
- status = 0;
- flags = 0;
- size = 0;
- if (cmd[0] == '$') {
- status = utp_run(cmd + 2);
- if (status)
- flags = UTP_FLAG_STATUS;
- } else if (strncmp(cmd, "pipesd", 6) == 0) {
- pinfo.media_type = SD;
- pinfo.index = 0;
- pinfo.blksz = 512; /* TODO: find sd card block size */
- cmd += 7;
- tmp = strsep(&cmd, " ");
- printf(tmp);
- dev = simple_strtoul(tmp+4, NULL, 10);
- printf("dev: %lu\n", dev);
- pinfo.media_id = dev;
- sprintf(argbuf, "mmc dev %lu", dev);
- tmp = strsep(&cmd, " ");
- printf(tmp);
- skip = simple_strtoul(tmp+5, NULL, 10);
- pinfo.index += skip;
- utp_run(argbuf);
- } else if (strncmp(cmd, "pipeqspi", 8) == 0) {
- pinfo.media_type = QSPI;
- cmd += 9;
- tmp = strsep(&cmd, " ");
- if (strncmp(tmp, "addr=", 5) == 0) {
- addr = simple_strtoul(tmp+5, NULL, 16);
- printf("addr: 0x%x\n", addr);
- pinfo.addr = addr;
- } else {
- pinfo.addr = (pinfo.media_id ? QSPI1_FLASH_BASE_ADDR
- : QSPI0_FLASH_BASE_ADDR);
- }
- } else if (strncmp(cmd, "qspiinit", 8) == 0) {
- pinfo.media_type = QSPI;
- pinfo.index = 0;
- pinfo.blksz = 0x200;
- sprintf(argbuf, cmd);
- cmd += 7;
- tmp = strsep(&cmd, " ");
- printf(tmp);
- dev = simple_strtoul(tmp+4, NULL, 10);
- printf("dev: %lu\n", dev);
- pinfo.media_id = dev;
- printf("%s\n", cmd);
- utp_run(argbuf);
- }
- else if (strncmp (cmd, "ubootcmd", 8) == 0) {
- sprintf(argbuf, cmd + 8);
- utp_run(argbuf);
- }
- answer = utp_user_data_alloc(0, size);
- if (flags & UTP_FLAG_STATUS)
- answer->data.status = status;
- return answer;
- }
- int utp_pipe_sd(u8 *data, size_t bufsize)
- {
- pr_debug("got data chunk, size: %zx\n", bufsize);
- unsigned int blkcnt;
- char argbuf[100];
- blkcnt = (bufsize / pinfo.blksz) + (((bufsize % pinfo.blksz) != 0) ? 1 : 0);
- pr_debug("blkcnt: %x, index: %lx, bufsize: %zx\n", blkcnt, pinfo.index, bufsize);
- sprintf(argbuf, "mmc write %x %lx %x", data, pinfo.index, blkcnt);
- utp_run(argbuf);
- pr_debug("after write to sd\n");
- pinfo.index += blkcnt;
- return 0;
- }
- int utp_pipe_qspi(u8 *data, size_t bufsize)
- {
- pr_debug("got data chunk, size: %zx\n", bufsize);
- unsigned int blkcnt, dest, len;
- char argbuf[100];
- blkcnt = (bufsize / pinfo.blksz) + (((bufsize % pinfo.blksz) != 0) ? 1 : 0);
- dest = pinfo.addr + pinfo.blksz * pinfo.index;
- len = bufsize;
- pr_debug("dest: %x, data: %lx, len: %zx\n", dest, data, len);
- sprintf(argbuf, "qspiwrite %x %lx %x",dest, data, len);
- pr_debug("%s\n", argbuf);
- utp_run(argbuf);
- pr_debug("after write to qspi\n");
- pinfo.index += blkcnt;
- return 0;
- }
- struct utp_user_data *utp_interpret_message(struct utp_user_data *uud2r)
- {
- struct utp_user_data *answer;
- struct utp_message *msg = &uud2r->data;
- if (msg->flags & UTP_FLAG_COMMAND) {
- answer = utp_handle_command(msg->command, msg->payload);
- if (answer)
- return answer;
- } else if (msg->flags & UTP_FLAG_DATA) {
- switch(pinfo.media_type) {
- case SD:
- utp_pipe_sd(msg->data, msg->bufsize);
- break;
- case QSPI:
- utp_pipe_qspi(msg->data, msg->bufsize);
- default:
- break;
- }
- }
- return NULL;
- }
|