/* Cypress West Bridge API source file (cyasstorage.c) ## =========================== ## Copyright (C) 2010 Cypress Semiconductor ## ## 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. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 51 Franklin Street, Fifth Floor ## Boston, MA 02110-1301, USA. ## =========================== */ /* * Storage Design * * The storage module is fairly straight forward once the * DMA and LOWLEVEL modules have been designed. The * storage module simple takes requests from the user, queues * the associated DMA requests for action, and then sends * the low level requests to the West Bridge firmware. * */ #include "../../include/linux/westbridge/cyashal.h" #include "../../include/linux/westbridge/cyasstorage.h" #include "../../include/linux/westbridge/cyaserr.h" #include "../../include/linux/westbridge/cyasdevice.h" #include "../../include/linux/westbridge/cyaslowlevel.h" #include "../../include/linux/westbridge/cyasdma.h" #include "../../include/linux/westbridge/cyasregs.h" /* Map a pre-V1.2 media type to the V1.2+ bus number */ cy_as_return_status_t cy_an_map_bus_from_media_type(cy_as_device *dev_p, cy_as_media_type type, cy_as_bus_number_t *bus) { cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; uint8_t code = (uint8_t)(1 << type); if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) return CY_AS_ERROR_INVALID_HANDLE; if (!cy_as_device_is_configured(dev_p)) return CY_AS_ERROR_NOT_CONFIGURED; if (!cy_as_device_is_firmware_loaded(dev_p)) return CY_AS_ERROR_NO_FIRMWARE; if (dev_p->media_supported[0] & code) { if (dev_p->media_supported[1] & code) { /* * this media type could be supported on multiple * buses. so, report an address resolution error. */ ret = CY_AS_ERROR_ADDRESS_RESOLUTION_ERROR; } else *bus = 0; } else { if (dev_p->media_supported[1] & code) *bus = 1; else ret = CY_AS_ERROR_NO_SUCH_MEDIA; } return ret; } static uint16_t create_address(cy_as_bus_number_t bus, uint32_t device, uint8_t unit) { cy_as_hal_assert(bus >= 0 && bus < CY_AS_MAX_BUSES); cy_as_hal_assert(device < 16); return (uint16_t)(((uint8_t)bus << 12) | (device << 8) | unit); } cy_as_media_type cy_as_storage_get_media_from_address(uint16_t v) { cy_as_media_type media = cy_as_media_max_media_value; switch (v & 0xFF) { case 0x00: break; case 0x01: media = cy_as_media_nand; break; case 0x02: media = cy_as_media_sd_flash; break; case 0x04: media = cy_as_media_mmc_flash; break; case 0x08: media = cy_as_media_ce_ata; break; case 0x10: media = cy_as_media_sdio; break; default: cy_as_hal_assert(0); break; } return media; } cy_as_bus_number_t cy_as_storage_get_bus_from_address(uint16_t v) { cy_as_bus_number_t bus = (cy_as_bus_number_t)((v >> 12) & 0x0f); cy_as_hal_assert(bus >= 0 && bus < CY_AS_MAX_BUSES); return bus; } uint32_t cy_as_storage_get_device_from_address(uint16_t v) { return (uint32_t)((v >> 8) & 0x0f); } static uint8_t get_unit_from_address(uint16_t v) { return (uint8_t)(v & 0xff); } static cy_as_return_status_t cy_as_map_bad_addr(uint16_t val) { cy_as_return_status_t ret = CY_AS_ERROR_INVALID_RESPONSE; switch (val) { case 0: ret = CY_AS_ERROR_NO_SUCH_BUS; break; case 1: ret = CY_AS_ERROR_NO_SUCH_DEVICE; break; case 2: ret = CY_AS_ERROR_NO_SUCH_UNIT; break; case 3: ret = CY_AS_ERROR_INVALID_BLOCK; break; } return ret; } static void my_storage_request_callback(cy_as_device *dev_p, uint8_t context, cy_as_ll_request_response *req_p, cy_as_ll_request_response *resp_p, cy_as_return_status_t ret) { uint16_t val; uint16_t addr; cy_as_bus_number_t bus; uint32_t device; cy_as_device_handle h = (cy_as_device_handle)dev_p; cy_as_dma_end_point *ep_p = NULL; (void)resp_p; (void)context; (void)ret; switch (cy_as_ll_request_response__get_code(req_p)) { case CY_RQT_MEDIA_CHANGED: cy_as_ll_send_status_response(dev_p, CY_RQT_STORAGE_RQT_CONTEXT, CY_AS_ERROR_SUCCESS, 0); /* Media has either been inserted or removed */ addr = cy_as_ll_request_response__get_word(req_p, 0); bus = cy_as_storage_get_bus_from_address(addr); device = cy_as_storage_get_device_from_address(addr); /* Clear the entry for this device to force re-query later */ cy_as_hal_mem_set(&(dev_p->storage_device_info[bus][device]), 0, sizeof(dev_p->storage_device_info[bus][device])); val = cy_as_ll_request_response__get_word(req_p, 1); if (dev_p->storage_event_cb_ms) { if (val == 1) dev_p->storage_event_cb_ms(h, bus, device, cy_as_storage_removed, 0); else dev_p->storage_event_cb_ms(h, bus, device, cy_as_storage_inserted, 0); } else if (dev_p->storage_event_cb) { if (val == 1) dev_p->storage_event_cb(h, bus, cy_as_storage_removed, 0); else dev_p->storage_event_cb(h, bus, cy_as_storage_inserted, 0); } break; case CY_RQT_ANTIOCH_CLAIM: cy_as_ll_send_status_response(dev_p, CY_RQT_STORAGE_RQT_CONTEXT, CY_AS_ERROR_SUCCESS, 0); if (dev_p->storage_event_cb || dev_p->storage_event_cb_ms) { val = cy_as_ll_request_response__get_word(req_p, 0); if (dev_p->storage_event_cb_ms) { if (val & 0x0100) dev_p->storage_event_cb_ms(h, 0, 0, cy_as_storage_antioch, 0); if (val & 0x0200) dev_p->storage_event_cb_ms(h, 1, 0, cy_as_storage_antioch, 0); } else { if (val & 0x01) dev_p->storage_event_cb(h, cy_as_media_nand, cy_as_storage_antioch, 0); if (val & 0x02) dev_p->storage_event_cb(h, cy_as_media_sd_flash, cy_as_storage_antioch, 0); if (val & 0x04) dev_p->storage_event_cb(h, cy_as_media_mmc_flash, cy_as_storage_antioch, 0); if (val & 0x08) dev_p->storage_event_cb(h, cy_as_media_ce_ata, cy_as_storage_antioch, 0); } } break; case CY_RQT_ANTIOCH_RELEASE: cy_as_ll_send_status_response(dev_p, CY_RQT_STORAGE_RQT_CONTEXT, CY_AS_ERROR_SUCCESS, 0); val = cy_as_ll_request_response__get_word(req_p, 0); if (dev_p->storage_event_cb_ms) { if (val & 0x0100) dev_p->storage_event_cb_ms(h, 0, 0, cy_as_storage_processor, 0); if (val & 0x0200) dev_p->storage_event_cb_ms(h, 1, 0, cy_as_storage_processor, 0); } else if (dev_p->storage_event_cb) { if (val & 0x01) dev_p->storage_event_cb(h, cy_as_media_nand, cy_as_storage_processor, 0); if (val & 0x02) dev_p->storage_event_cb(h, cy_as_media_sd_flash, cy_as_storage_processor, 0); if (val & 0x04) dev_p->storage_event_cb(h, cy_as_media_mmc_flash, cy_as_storage_processor, 0); if (val & 0x08) dev_p->storage_event_cb(h, cy_as_media_ce_ata, cy_as_storage_processor, 0); } break; case CY_RQT_SDIO_INTR: cy_as_ll_send_status_response(dev_p, CY_RQT_STORAGE_RQT_CONTEXT, CY_AS_ERROR_SUCCESS, 0); val = cy_as_ll_request_response__get_word(req_p, 0); if (dev_p->storage_event_cb_ms) { if (val & 0x0100) dev_p->storage_event_cb_ms(h, 1, 0, cy_as_sdio_interrupt, 0); else dev_p->storage_event_cb_ms(h, 0, 0, cy_as_sdio_interrupt, 0); } else if (dev_p->storage_event_cb) { dev_p->storage_event_cb(h, cy_as_media_sdio, cy_as_sdio_interrupt, 0); } break; case CY_RQT_P2S_DMA_START: /* Do the DMA setup for the waiting operation. */ cy_as_ll_send_status_response(dev_p, CY_RQT_STORAGE_RQT_CONTEXT, CY_AS_ERROR_SUCCESS, 0); cy_as_device_set_p2s_dma_start_recvd(dev_p); if (dev_p->storage_oper == cy_as_op_read) { ep_p = CY_AS_NUM_EP(dev_p, CY_AS_P2S_READ_ENDPOINT); cy_as_dma_end_point_set_stopped(ep_p); cy_as_dma_kick_start(dev_p, CY_AS_P2S_READ_ENDPOINT); } else { ep_p = CY_AS_NUM_EP(dev_p, CY_AS_P2S_WRITE_ENDPOINT); cy_as_dma_end_point_set_stopped(ep_p); cy_as_dma_kick_start(dev_p, CY_AS_P2S_WRITE_ENDPOINT); } break; default: cy_as_hal_print_message("invalid request received " "on storage context\n"); val = req_p->box0; cy_as_ll_send_data_response(dev_p, CY_RQT_STORAGE_RQT_CONTEXT, CY_RESP_INVALID_REQUEST, sizeof(val), &val); break; } } static cy_as_return_status_t is_storage_active(cy_as_device *dev_p) { if (!cy_as_device_is_configured(dev_p)) return CY_AS_ERROR_NOT_CONFIGURED; if (!cy_as_device_is_firmware_loaded(dev_p)) return CY_AS_ERROR_NO_FIRMWARE; if (dev_p->storage_count == 0) return CY_AS_ERROR_NOT_RUNNING; if (cy_as_device_is_in_suspend_mode(dev_p)) return CY_AS_ERROR_IN_SUSPEND; return CY_AS_ERROR_SUCCESS; } static void cy_as_storage_func_callback(cy_as_device *dev_p, uint8_t context, cy_as_ll_request_response *rqt, cy_as_ll_request_response *resp, cy_as_return_status_t ret); static cy_as_return_status_t my_handle_response_no_data(cy_as_device *dev_p, cy_as_ll_request_response *req_p, cy_as_ll_request_response *reply_p) { cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; if (cy_as_ll_request_response__get_code(reply_p) != CY_RESP_SUCCESS_FAILURE) { ret = CY_AS_ERROR_INVALID_RESPONSE; goto destroy; } ret = cy_as_ll_request_response__get_word(reply_p, 0); destroy: cy_as_ll_destroy_request(dev_p, req_p); cy_as_ll_destroy_response(dev_p, reply_p); return ret; } static cy_as_return_status_t my_handle_response_storage_start(cy_as_device *dev_p, cy_as_ll_request_response *req_p, cy_as_ll_request_response *reply_p, cy_as_return_status_t ret) { if (ret != CY_AS_ERROR_SUCCESS) goto destroy; if (cy_as_ll_request_response__get_code(reply_p) != CY_RESP_SUCCESS_FAILURE) { ret = CY_AS_ERROR_INVALID_RESPONSE; goto destroy; } ret = cy_as_ll_request_response__get_word(reply_p, 0); if (dev_p->storage_count > 0 && ret == CY_AS_ERROR_ALREADY_RUNNING) ret = CY_AS_ERROR_SUCCESS; ret = cy_as_dma_enable_end_point(dev_p, CY_AS_P2S_WRITE_ENDPOINT, cy_true, cy_as_direction_in); if (ret != CY_AS_ERROR_SUCCESS) goto destroy; ret = cy_as_dma_set_max_dma_size(dev_p, CY_AS_P2S_WRITE_ENDPOINT, CY_AS_STORAGE_EP_SIZE); if (ret != CY_AS_ERROR_SUCCESS) goto destroy; ret = cy_as_dma_enable_end_point(dev_p, CY_AS_P2S_READ_ENDPOINT, cy_true, cy_as_direction_out); if (ret != CY_AS_ERROR_SUCCESS) goto destroy; ret = cy_as_dma_set_max_dma_size(dev_p, CY_AS_P2S_READ_ENDPOINT, CY_AS_STORAGE_EP_SIZE); if (ret != CY_AS_ERROR_SUCCESS) goto destroy; cy_as_ll_register_request_callback(dev_p, CY_RQT_STORAGE_RQT_CONTEXT, my_storage_request_callback); /* Create the request/response used for storage reads and writes. */ dev_p->storage_rw_req_p = cy_as_ll_create_request(dev_p, 0, CY_RQT_STORAGE_RQT_CONTEXT, 5); if (dev_p->storage_rw_req_p == 0) { ret = CY_AS_ERROR_OUT_OF_MEMORY; goto destroy; } dev_p->storage_rw_resp_p = cy_as_ll_create_response(dev_p, 5); if (dev_p->storage_rw_resp_p == 0) { cy_as_ll_destroy_request(dev_p, dev_p->storage_rw_req_p); ret = CY_AS_ERROR_OUT_OF_MEMORY; } destroy: cy_as_ll_destroy_request(dev_p, req_p); cy_as_ll_destroy_response(dev_p, reply_p); /* Increment the storage count only if * the above functionality succeeds.*/ if (ret == CY_AS_ERROR_SUCCESS) { if (dev_p->storage_count == 0) { cy_as_hal_mem_set(dev_p->storage_device_info, 0, sizeof(dev_p->storage_device_info)); dev_p->is_storage_only_mode = cy_false; } dev_p->storage_count++; } cy_as_device_clear_s_s_s_pending(dev_p); return ret; } cy_as_return_status_t cy_as_storage_start(cy_as_device_handle handle, cy_as_function_callback cb, uint32_t client) { cy_as_ll_request_response *req_p, *reply_p; cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; cy_as_device *dev_p = (cy_as_device *)handle; if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) return CY_AS_ERROR_INVALID_HANDLE; if (!cy_as_device_is_configured(dev_p)) return CY_AS_ERROR_NOT_CONFIGURED; if (!cy_as_device_is_firmware_loaded(dev_p)) return CY_AS_ERROR_NO_FIRMWARE; if (cy_as_device_is_in_suspend_mode(dev_p)) return CY_AS_ERROR_IN_SUSPEND; if (cy_as_device_is_s_s_s_pending(dev_p)) return CY_AS_ERROR_STARTSTOP_PENDING; cy_as_device_set_s_s_s_pending(dev_p); if (dev_p->storage_count == 0) { /* Create the request to send to the West Bridge device */ req_p = cy_as_ll_create_request(dev_p, CY_RQT_START_STORAGE, CY_RQT_STORAGE_RQT_CONTEXT, 1); if (req_p == 0) { cy_as_device_clear_s_s_s_pending(dev_p); return CY_AS_ERROR_OUT_OF_MEMORY; } /* Reserve space for the reply, the reply data * will not exceed one word */ reply_p = cy_as_ll_create_response(dev_p, 1); if (reply_p == 0) { cy_as_device_clear_s_s_s_pending(dev_p); cy_as_ll_destroy_request(dev_p, req_p); return CY_AS_ERROR_OUT_OF_MEMORY; } if (cb == 0) { ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); if (ret != CY_AS_ERROR_SUCCESS) goto destroy; return my_handle_response_storage_start(dev_p, req_p, reply_p, ret); } else { ret = cy_as_misc_send_request(dev_p, cb, client, CY_FUNCT_CB_STOR_START, 0, dev_p->func_cbs_stor, CY_AS_REQUEST_RESPONSE_EX, req_p, reply_p, cy_as_storage_func_callback); if (ret != CY_AS_ERROR_SUCCESS) goto destroy; /* The request and response are freed as * part of the FuncCallback */ return ret; } destroy: cy_as_ll_destroy_request(dev_p, req_p); cy_as_ll_destroy_response(dev_p, reply_p); } else { dev_p->storage_count++; if (cb) cb(handle, ret, client, CY_FUNCT_CB_STOR_START, 0); } cy_as_device_clear_s_s_s_pending(dev_p); return ret; } EXPORT_SYMBOL(cy_as_storage_start); static cy_as_return_status_t my_handle_response_storage_stop(cy_as_device *dev_p, cy_as_ll_request_response *req_p, cy_as_ll_request_response *reply_p, cy_as_return_status_t ret) { if (ret != CY_AS_ERROR_SUCCESS) goto destroy; if (cy_as_ll_request_response__get_code(reply_p) != CY_RESP_SUCCESS_FAILURE) { ret = CY_AS_ERROR_INVALID_RESPONSE; goto destroy; } destroy: cy_as_ll_destroy_request(dev_p, req_p); cy_as_ll_destroy_response(dev_p, reply_p); if (ret == CY_AS_ERROR_SUCCESS) { cy_as_ll_destroy_request(dev_p, dev_p->storage_rw_req_p); cy_as_ll_destroy_response(dev_p, dev_p->storage_rw_resp_p); dev_p->storage_count--; } cy_as_device_clear_s_s_s_pending(dev_p); return ret; } cy_as_return_status_t cy_as_storage_stop(cy_as_device_handle handle, cy_as_function_callback cb, uint32_t client) { cy_as_ll_request_response *req_p , *reply_p; cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; cy_as_device *dev_p = (cy_as_device *)handle; if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) return CY_AS_ERROR_INVALID_HANDLE; ret = is_storage_active(dev_p); if (ret != CY_AS_ERROR_SUCCESS) return ret; if (cy_as_device_is_storage_async_pending(dev_p)) return CY_AS_ERROR_ASYNC_PENDING; if (cy_as_device_is_s_s_s_pending(dev_p)) return CY_AS_ERROR_STARTSTOP_PENDING; cy_as_device_set_s_s_s_pending(dev_p); if (dev_p->storage_count == 1) { /* Create the request to send to the West Bridge device */ req_p = cy_as_ll_create_request(dev_p, CY_RQT_STOP_STORAGE, CY_RQT_STORAGE_RQT_CONTEXT, 0); if (req_p == 0) { cy_as_device_clear_s_s_s_pending(dev_p); return CY_AS_ERROR_OUT_OF_MEMORY; } /* Reserve space for the reply, the reply data * will not exceed one word */ reply_p = cy_as_ll_create_response(dev_p, 1); if (reply_p == 0) { cy_as_device_clear_s_s_s_pending(dev_p); cy_as_ll_destroy_request(dev_p, req_p); return CY_AS_ERROR_OUT_OF_MEMORY; } if (cb == 0) { ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); if (ret != CY_AS_ERROR_SUCCESS) goto destroy; return my_handle_response_storage_stop(dev_p, req_p, reply_p, ret); } else { ret = cy_as_misc_send_request(dev_p, cb, client, CY_FUNCT_CB_STOR_STOP, 0, dev_p->func_cbs_stor, CY_AS_REQUEST_RESPONSE_EX, req_p, reply_p, cy_as_storage_func_callback); if (ret != CY_AS_ERROR_SUCCESS) goto destroy; /* The request and response are freed * as part of the MiscFuncCallback */ return ret; } destroy: cy_as_ll_destroy_request(dev_p, req_p); cy_as_ll_destroy_response(dev_p, reply_p); } else if (dev_p->storage_count > 1) { dev_p->storage_count--; if (cb) cb(handle, ret, client, CY_FUNCT_CB_STOR_STOP, 0); } cy_as_device_clear_s_s_s_pending(dev_p); return ret; } EXPORT_SYMBOL(cy_as_storage_stop); cy_as_return_status_t cy_as_storage_register_callback(cy_as_device_handle handle, cy_as_storage_event_callback callback) { cy_as_device *dev_p = (cy_as_device *)handle; if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) return CY_AS_ERROR_INVALID_HANDLE; if (!cy_as_device_is_configured(dev_p)) return CY_AS_ERROR_NOT_CONFIGURED; if (!cy_as_device_is_firmware_loaded(dev_p)) return CY_AS_ERROR_NO_FIRMWARE; if (dev_p->storage_count == 0) return CY_AS_ERROR_NOT_RUNNING; dev_p->storage_event_cb = NULL; dev_p->storage_event_cb_ms = callback; return CY_AS_ERROR_SUCCESS; } EXPORT_SYMBOL(cy_as_storage_register_callback); static cy_as_return_status_t my_handle_response_storage_claim(cy_as_device *dev_p, cy_as_ll_request_response *req_p, cy_as_ll_request_response *reply_p) { cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; if (cy_as_ll_request_response__get_code(reply_p) == CY_RESP_NO_SUCH_ADDRESS) { ret = cy_as_map_bad_addr( cy_as_ll_request_response__get_word(reply_p, 3)); goto destroy; } if (cy_as_ll_request_response__get_code(reply_p) != CY_RESP_MEDIA_CLAIMED_RELEASED) { ret = CY_AS_ERROR_INVALID_RESPONSE; goto destroy; } /* The response must be about the address I am * trying to claim or the firmware is broken */ if ((cy_as_storage_get_bus_from_address( cy_as_ll_request_response__get_word(req_p, 0)) != cy_as_storage_get_bus_from_address( cy_as_ll_request_response__get_word(reply_p, 0))) || (cy_as_storage_get_device_from_address( cy_as_ll_request_response__get_word(req_p, 0)) != cy_as_storage_get_device_from_address( cy_as_ll_request_response__get_word(reply_p, 0)))) { ret = CY_AS_ERROR_INVALID_RESPONSE; goto destroy; } if (cy_as_ll_request_response__get_word(reply_p, 1) != 1) ret = CY_AS_ERROR_NOT_ACQUIRED; destroy: cy_as_ll_destroy_request(dev_p, req_p); cy_as_ll_destroy_response(dev_p, reply_p); return ret; } static cy_as_return_status_t my_storage_claim(cy_as_device *dev_p, void *data, cy_as_bus_number_t bus, uint32_t device, uint16_t req_flags, cy_as_function_callback cb, uint32_t client) { cy_as_ll_request_response *req_p , *reply_p; cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) return CY_AS_ERROR_INVALID_HANDLE; ret = is_storage_active(dev_p); if (ret != CY_AS_ERROR_SUCCESS) return ret; if (dev_p->mtp_count > 0) return CY_AS_ERROR_NOT_VALID_IN_MTP; /* Create the request to send to the West Bridge device */ req_p = cy_as_ll_create_request(dev_p, CY_RQT_CLAIM_STORAGE, CY_RQT_STORAGE_RQT_CONTEXT, 1); if (req_p == 0) return CY_AS_ERROR_OUT_OF_MEMORY; cy_as_ll_request_response__set_word(req_p, 0, create_address(bus, device, 0)); /* Reserve space for the reply, the reply data will * not exceed one word */ reply_p = cy_as_ll_create_response(dev_p, 4); if (reply_p == 0) { cy_as_ll_destroy_request(dev_p, req_p); return CY_AS_ERROR_OUT_OF_MEMORY; } if (cb == 0) { ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); if (ret != CY_AS_ERROR_SUCCESS) goto destroy; return my_handle_response_storage_claim(dev_p, req_p, reply_p); } else { ret = cy_as_misc_send_request(dev_p, cb, client, CY_FUNCT_CB_STOR_CLAIM, data, dev_p->func_cbs_stor, req_flags, req_p, reply_p, cy_as_storage_func_callback); if (ret != CY_AS_ERROR_SUCCESS) goto destroy; /* The request and response are freed as part of * the MiscFuncCallback */ return ret; } destroy: cy_as_ll_destroy_request(dev_p, req_p); cy_as_ll_destroy_response(dev_p, reply_p); return ret; } cy_as_return_status_t cy_as_storage_claim(cy_as_device_handle handle, cy_as_bus_number_t bus, uint32_t device, cy_as_function_callback cb, uint32_t client) { cy_as_device *dev_p = (cy_as_device *)handle; if (bus < 0 || bus >= CY_AS_MAX_BUSES) return CY_AS_ERROR_NO_SUCH_BUS; return my_storage_claim(dev_p, NULL, bus, device, CY_AS_REQUEST_RESPONSE_MS, cb, client); } EXPORT_SYMBOL(cy_as_storage_claim); static cy_as_return_status_t my_handle_response_storage_release(cy_as_device *dev_p, cy_as_ll_request_response *req_p, cy_as_ll_request_response *reply_p) { cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; if (cy_as_ll_request_response__get_code(reply_p) == CY_RESP_NO_SUCH_ADDRESS) { ret = cy_as_map_bad_addr( cy_as_ll_request_response__get_word(reply_p, 3)); goto destroy; } if (cy_as_ll_request_response__get_code(reply_p) != CY_RESP_MEDIA_CLAIMED_RELEASED) { ret = CY_AS_ERROR_INVALID_RESPONSE; goto destroy; } /* The response must be about the address I am * trying to release or the firmware is broken */ if ((cy_as_storage_get_bus_from_address( cy_as_ll_request_response__get_word(req_p, 0)) != cy_as_storage_get_bus_from_address( cy_as_ll_request_response__get_word(reply_p, 0))) || (cy_as_storage_get_device_from_address( cy_as_ll_request_response__get_word(req_p, 0)) != cy_as_storage_get_device_from_address( cy_as_ll_request_response__get_word(reply_p, 0)))) { ret = CY_AS_ERROR_INVALID_RESPONSE; goto destroy; } if (cy_as_ll_request_response__get_word(reply_p, 1) != 0) ret = CY_AS_ERROR_NOT_RELEASED; destroy: cy_as_ll_destroy_request(dev_p, req_p); cy_as_ll_destroy_response(dev_p, reply_p); return ret; } static cy_as_return_status_t my_storage_release(cy_as_device *dev_p, void *data, cy_as_bus_number_t bus, uint32_t device, uint16_t req_flags, cy_as_function_callback cb, uint32_t client) { cy_as_ll_request_response *req_p , *reply_p; cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) return CY_AS_ERROR_INVALID_HANDLE; ret = is_storage_active(dev_p); if (ret != CY_AS_ERROR_SUCCESS) return ret; if (dev_p->mtp_count > 0) return CY_AS_ERROR_NOT_VALID_IN_MTP; /* Create the request to send to the West Bridge device */ req_p = cy_as_ll_create_request(dev_p, CY_RQT_RELEASE_STORAGE, CY_RQT_STORAGE_RQT_CONTEXT, 1); if (req_p == 0) return CY_AS_ERROR_OUT_OF_MEMORY; cy_as_ll_request_response__set_word( req_p, 0, create_address(bus, device, 0)); /* Reserve space for the reply, the reply * data will not exceed one word */ reply_p = cy_as_ll_create_response(dev_p, 4); if (reply_p == 0) { cy_as_ll_destroy_request(dev_p, req_p); return CY_AS_ERROR_OUT_OF_MEMORY; } if (cb == 0) { ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); if (ret != CY_AS_ERROR_SUCCESS) goto destroy; return my_handle_response_storage_release( dev_p, req_p, reply_p); } else { ret = cy_as_misc_send_request(dev_p, cb, client, CY_FUNCT_CB_STOR_RELEASE, data, dev_p->func_cbs_stor, req_flags, req_p, reply_p, cy_as_storage_func_callback); if (ret != CY_AS_ERROR_SUCCESS) goto destroy; /* The request and response are freed as * part of the MiscFuncCallback */ return ret; } destroy: cy_as_ll_destroy_request(dev_p, req_p); cy_as_ll_destroy_response(dev_p, reply_p); return ret; } cy_as_return_status_t cy_as_storage_release(cy_as_device_handle handle, cy_as_bus_number_t bus, uint32_t device, cy_as_function_callback cb, uint32_t client) { cy_as_device *dev_p = (cy_as_device *)handle; if (bus < 0 || bus >= CY_AS_MAX_BUSES) return CY_AS_ERROR_NO_SUCH_BUS; return my_storage_release(dev_p, NULL, bus, device, CY_AS_REQUEST_RESPONSE_MS, cb, client); } EXPORT_SYMBOL(cy_as_storage_release); static cy_as_return_status_t my_handle_response_storage_query_bus(cy_as_device *dev_p, cy_as_ll_request_response *req_p, cy_as_ll_request_response *reply_p, uint32_t *count) { cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; uint8_t code = cy_as_ll_request_response__get_code(reply_p); uint16_t v; if (code == CY_RESP_NO_SUCH_ADDRESS) { ret = CY_AS_ERROR_NO_SUCH_BUS; goto destroy; } if (code != CY_RESP_BUS_DESCRIPTOR) { ret = CY_AS_ERROR_INVALID_RESPONSE; goto destroy; } /* * verify that the response corresponds to the bus that was queried. */ if (cy_as_storage_get_bus_from_address( cy_as_ll_request_response__get_word(req_p, 0)) != cy_as_storage_get_bus_from_address( cy_as_ll_request_response__get_word(reply_p, 0))) { ret = CY_AS_ERROR_INVALID_RESPONSE; goto destroy; } v = cy_as_ll_request_response__get_word(reply_p, 1); if (req_p->flags & CY_AS_REQUEST_RESPONSE_MS) { /* * this request is only for the count of devices * on the bus. there is no need to check the media type. */ if (v) *count = 1; else *count = 0; } else { /* * this request is for the count of devices of a * particular type. we need to check whether the media * type found matches the queried type. */ cy_as_media_type queried = (cy_as_media_type) cy_as_ll_request_response__get_word(req_p, 1); cy_as_media_type found = cy_as_storage_get_media_from_address(v); if (queried == found) *count = 1; else *count = 0; } destroy: cy_as_ll_destroy_request(dev_p, req_p); cy_as_ll_destroy_response(dev_p, reply_p); return ret; } cy_as_return_status_t my_storage_query_bus(cy_as_device *dev_p, cy_as_bus_number_t bus, cy_as_media_type type, uint16_t req_flags, uint32_t *count, cy_as_function_callback cb, uint32_t client) { cy_as_return_status_t ret; cy_as_ll_request_response *req_p, *reply_p; cy_as_funct_c_b_type cb_type = CY_FUNCT_CB_STOR_QUERYBUS; if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) return CY_AS_ERROR_INVALID_HANDLE; ret = is_storage_active(dev_p); if (ret != CY_AS_ERROR_SUCCESS) return ret; /* Create the request to send to the Antioch device */ req_p = cy_as_ll_create_request(dev_p, CY_RQT_QUERY_BUS, CY_RQT_STORAGE_RQT_CONTEXT, 2); if (req_p == 0) return CY_AS_ERROR_OUT_OF_MEMORY; cy_as_ll_request_response__set_word(req_p, 0, create_address(bus, 0, 0)); cy_as_ll_request_response__set_word(req_p, 1, (uint16_t)type); /* Reserve space for the reply, the reply data * will not exceed two words. */ reply_p = cy_as_ll_create_response(dev_p, 2); if (reply_p == 0) { cy_as_ll_destroy_request(dev_p, req_p); return CY_AS_ERROR_OUT_OF_MEMORY; } if (cb == 0) { ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); if (ret != CY_AS_ERROR_SUCCESS) goto destroy; req_p->flags |= req_flags; return my_handle_response_storage_query_bus(dev_p, req_p, reply_p, count); } else { if (req_flags == CY_AS_REQUEST_RESPONSE_EX) cb_type = CY_FUNCT_CB_STOR_QUERYMEDIA; ret = cy_as_misc_send_request(dev_p, cb, client, cb_type, count, dev_p->func_cbs_stor, req_flags, req_p, reply_p, cy_as_storage_func_callback); if (ret != CY_AS_ERROR_SUCCESS) goto destroy; /* The request and response are freed as part of * the MiscFuncCallback */ return ret; } destroy: cy_as_ll_destroy_request(dev_p, req_p); cy_as_ll_destroy_response(dev_p, reply_p); return ret; } cy_as_return_status_t cy_as_storage_query_bus(cy_as_device_handle handle, cy_as_bus_number_t bus, uint32_t *count, cy_as_function_callback cb, uint32_t client) { cy_as_device *dev_p = (cy_as_device *)handle; return my_storage_query_bus(dev_p, bus, cy_as_media_max_media_value, CY_AS_REQUEST_RESPONSE_MS, count, cb, client); } EXPORT_SYMBOL(cy_as_storage_query_bus); cy_as_return_status_t cy_as_storage_query_media(cy_as_device_handle handle, cy_as_media_type type, uint32_t *count, cy_as_function_callback cb, uint32_t client) { cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; cy_as_bus_number_t bus; cy_as_device *dev_p = (cy_as_device *)handle; if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) return CY_AS_ERROR_INVALID_HANDLE; ret = is_storage_active(dev_p); if (ret != CY_AS_ERROR_SUCCESS) return ret; ret = cy_an_map_bus_from_media_type(dev_p, type, &bus); if (ret != CY_AS_ERROR_SUCCESS) return ret; return my_storage_query_bus(dev_p, bus, type, CY_AS_REQUEST_RESPONSE_EX, count, cb, client); } EXPORT_SYMBOL(cy_as_storage_query_media); static cy_as_return_status_t my_handle_response_storage_query_device(cy_as_device *dev_p, cy_as_ll_request_response *req_p, cy_as_ll_request_response *reply_p, void *data_p) { cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; uint16_t v; cy_as_bus_number_t bus; cy_as_media_type type; uint32_t device; cy_bool removable; cy_bool writeable; cy_bool locked; uint16_t block_size; uint32_t number_units; uint32_t number_eus; if (cy_as_ll_request_response__get_code(reply_p) == CY_RESP_NO_SUCH_ADDRESS) { ret = cy_as_map_bad_addr( cy_as_ll_request_response__get_word(reply_p, 3)); goto destroy; } if (cy_as_ll_request_response__get_code(reply_p) != CY_RESP_DEVICE_DESCRIPTOR) { ret = CY_AS_ERROR_INVALID_RESPONSE; goto destroy; } /* Unpack the response */ v = cy_as_ll_request_response__get_word(reply_p, 0); type = cy_as_storage_get_media_from_address(v); bus = cy_as_storage_get_bus_from_address(v); device = cy_as_storage_get_device_from_address(v); block_size = cy_as_ll_request_response__get_word(reply_p, 1); v = cy_as_ll_request_response__get_word(reply_p, 2); removable = (v & 0x8000) ? cy_true : cy_false; writeable = (v & 0x0100) ? cy_true : cy_false; locked = (v & 0x0200) ? cy_true : cy_false; number_units = (v & 0xff); number_eus = (cy_as_ll_request_response__get_word(reply_p, 3) << 16) | cy_as_ll_request_response__get_word(reply_p, 4); /* Store the results based on the version of originating function */ if (req_p->flags & CY_AS_REQUEST_RESPONSE_MS) { cy_as_storage_query_device_data *store_p = (cy_as_storage_query_device_data *)data_p; /* Make sure the response is about the address we asked * about - if not, firmware error */ if ((bus != store_p->bus) || (device != store_p->device)) { ret = CY_AS_ERROR_INVALID_RESPONSE; goto destroy; } store_p->desc_p.type = type; store_p->desc_p.removable = removable; store_p->desc_p.writeable = writeable; store_p->desc_p.block_size = block_size; store_p->desc_p.number_units = number_units; store_p->desc_p.locked = locked; store_p->desc_p.erase_unit_size = number_eus; dev_p->storage_device_info[bus][device] = store_p->desc_p; } else { cy_as_storage_query_device_data_dep *store_p = (cy_as_storage_query_device_data_dep *)data_p; /* Make sure the response is about the address we asked * about - if not, firmware error */ if ((type != store_p->type) || (device != store_p->device)) { ret = CY_AS_ERROR_INVALID_RESPONSE; goto destroy; } store_p->desc_p.type = type; store_p->desc_p.removable = removable; store_p->desc_p.writeable = writeable; store_p->desc_p.block_size = block_size; store_p->desc_p.number_units = number_units; store_p->desc_p.locked = locked; store_p->desc_p.erase_unit_size = number_eus; dev_p->storage_device_info[bus][device] = store_p->desc_p; } destroy: cy_as_ll_destroy_request(dev_p, req_p); cy_as_ll_destroy_response(dev_p, reply_p); return ret; } static cy_as_return_status_t my_storage_query_device(cy_as_device *dev_p, void *data_p, uint16_t req_flags, cy_as_bus_number_t bus, uint32_t device, cy_as_function_callback cb, uint32_t client) { cy_as_ll_request_response *req_p , *reply_p; cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) return CY_AS_ERROR_INVALID_HANDLE; ret = is_storage_active(dev_p); if (ret != CY_AS_ERROR_SUCCESS) return ret; /* Create the request to send to the Antioch device */ req_p = cy_as_ll_create_request(dev_p, CY_RQT_QUERY_DEVICE, CY_RQT_STORAGE_RQT_CONTEXT, 1); if (req_p == 0) return CY_AS_ERROR_OUT_OF_MEMORY; cy_as_ll_request_response__set_word(req_p, 0, create_address(bus, device, 0)); /* Reserve space for the reply, the reply data * will not exceed five words. */ reply_p = cy_as_ll_create_response(dev_p, 5); if (reply_p == 0) { cy_as_ll_destroy_request(dev_p, req_p); return CY_AS_ERROR_OUT_OF_MEMORY; } if (cb == 0) { ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); if (ret != CY_AS_ERROR_SUCCESS) goto destroy; req_p->flags |= req_flags; return my_handle_response_storage_query_device(dev_p, req_p, reply_p, data_p); } else { ret = cy_as_misc_send_request(dev_p, cb, client, CY_FUNCT_CB_STOR_QUERYDEVICE, data_p, dev_p->func_cbs_stor, req_flags, req_p, reply_p, cy_as_storage_func_callback); if (ret != CY_AS_ERROR_SUCCESS) goto destroy; /* The request and response are freed as part of the * MiscFuncCallback */ return ret; } destroy: cy_as_ll_destroy_request(dev_p, req_p); cy_as_ll_destroy_response(dev_p, reply_p); return ret; } cy_as_return_status_t cy_as_storage_query_device(cy_as_device_handle handle, cy_as_storage_query_device_data *data_p, cy_as_function_callback cb, uint32_t client) { cy_as_device *dev_p = (cy_as_device *)handle; return my_storage_query_device(dev_p, data_p, CY_AS_REQUEST_RESPONSE_MS, data_p->bus, data_p->device, cb, client); } EXPORT_SYMBOL(cy_as_storage_query_device); static cy_as_return_status_t my_handle_response_storage_query_unit(cy_as_device *dev_p, cy_as_ll_request_response *req_p, cy_as_ll_request_response *reply_p, void *data_p) { cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; cy_as_bus_number_t bus; uint32_t device; uint32_t unit; cy_as_media_type type; uint16_t block_size; uint32_t start_block; uint32_t unit_size; uint16_t v; if (cy_as_ll_request_response__get_code(reply_p) == CY_RESP_NO_SUCH_ADDRESS) { ret = cy_as_map_bad_addr( cy_as_ll_request_response__get_word(reply_p, 3)); goto destroy; } if (cy_as_ll_request_response__get_code(reply_p) != CY_RESP_UNIT_DESCRIPTOR) { ret = CY_AS_ERROR_INVALID_RESPONSE; goto destroy; } /* Unpack the response */ v = cy_as_ll_request_response__get_word(reply_p, 0); bus = cy_as_storage_get_bus_from_address(v); device = cy_as_storage_get_device_from_address(v); unit = get_unit_from_address(v); type = cy_as_storage_get_media_from_address( cy_as_ll_request_response__get_word(reply_p, 1)); block_size = cy_as_ll_request_response__get_word(reply_p, 2); start_block = cy_as_ll_request_response__get_word(reply_p, 3) | (cy_as_ll_request_response__get_word(reply_p, 4) << 16); unit_size = cy_as_ll_request_response__get_word(reply_p, 5) | (cy_as_ll_request_response__get_word(reply_p, 6) << 16); /* Store the results based on the version of * originating function */ if (req_p->flags & CY_AS_REQUEST_RESPONSE_MS) { cy_as_storage_query_unit_data *store_p = (cy_as_storage_query_unit_data *)data_p; /* Make sure the response is about the address we * asked about - if not, firmware error */ if (bus != store_p->bus || device != store_p->device || unit != store_p->unit) { ret = CY_AS_ERROR_INVALID_RESPONSE; goto destroy; } store_p->desc_p.type = type; store_p->desc_p.block_size = block_size; store_p->desc_p.start_block = start_block; store_p->desc_p.unit_size = unit_size; } else { cy_as_storage_query_unit_data_dep *store_p = (cy_as_storage_query_unit_data_dep *)data_p; /* Make sure the response is about the media type we asked * about - if not, firmware error */ if ((type != store_p->type) || (device != store_p->device) || (unit != store_p->unit)) { ret = CY_AS_ERROR_INVALID_RESPONSE; goto destroy; } store_p->desc_p.type = type; store_p->desc_p.block_size = block_size; store_p->desc_p.start_block = start_block; store_p->desc_p.unit_size = unit_size; } dev_p->storage_device_info[bus][device].type = type; dev_p->storage_device_info[bus][device].block_size = block_size; destroy: cy_as_ll_destroy_request(dev_p, req_p); cy_as_ll_destroy_response(dev_p, reply_p); return ret; } static cy_as_return_status_t my_storage_query_unit(cy_as_device *dev_p, void *data_p, uint16_t req_flags, cy_as_bus_number_t bus, uint32_t device, uint32_t unit, cy_as_function_callback cb, uint32_t client) { cy_as_ll_request_response *req_p , *reply_p; cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) return CY_AS_ERROR_INVALID_HANDLE; ret = is_storage_active(dev_p); if (ret != CY_AS_ERROR_SUCCESS) return ret; /* Create the request to send to the West Bridge device */ req_p = cy_as_ll_create_request(dev_p, CY_RQT_QUERY_UNIT, CY_RQT_STORAGE_RQT_CONTEXT, 1); if (req_p == 0) return CY_AS_ERROR_OUT_OF_MEMORY; if (device > 255) return CY_AS_ERROR_NO_SUCH_DEVICE; if (unit > 255) return CY_AS_ERROR_NO_SUCH_UNIT; cy_as_ll_request_response__set_word(req_p, 0, create_address(bus, device, (uint8_t)unit)); /* Reserve space for the reply, the reply data * will be of seven words. */ reply_p = cy_as_ll_create_response(dev_p, 7); if (reply_p == 0) { cy_as_ll_destroy_request(dev_p, req_p); return CY_AS_ERROR_OUT_OF_MEMORY; } if (cb == 0) { ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); if (ret != CY_AS_ERROR_SUCCESS) goto destroy; req_p->flags |= req_flags; return my_handle_response_storage_query_unit(dev_p, req_p, reply_p, data_p); } else { ret = cy_as_misc_send_request(dev_p, cb, client, CY_FUNCT_CB_STOR_QUERYUNIT, data_p, dev_p->func_cbs_stor, req_flags, req_p, reply_p, cy_as_storage_func_callback); if (ret != CY_AS_ERROR_SUCCESS) goto destroy; /* The request and response are freed * as part of the MiscFuncCallback */ return ret; } destroy: cy_as_ll_destroy_request(dev_p, req_p); cy_as_ll_destroy_response(dev_p, reply_p); return ret; } cy_as_return_status_t cy_as_storage_query_unit(cy_as_device_handle handle, cy_as_storage_query_unit_data *data_p, cy_as_function_callback cb, uint32_t client) { cy_as_device *dev_p = (cy_as_device *)handle; return my_storage_query_unit(dev_p, data_p, CY_AS_REQUEST_RESPONSE_MS, data_p->bus, data_p->device, data_p->unit, cb, client); } EXPORT_SYMBOL(cy_as_storage_query_unit); static cy_as_return_status_t cy_as_get_block_size(cy_as_device *dev_p, cy_as_bus_number_t bus, uint32_t device, cy_as_function_callback cb) { cy_as_ll_request_response *req_p , *reply_p; cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; /* Create the request to send to the West Bridge device */ req_p = cy_as_ll_create_request(dev_p, CY_RQT_QUERY_DEVICE, CY_RQT_STORAGE_RQT_CONTEXT, 1); if (req_p == 0) return CY_AS_ERROR_OUT_OF_MEMORY; cy_as_ll_request_response__set_word(req_p, 0, create_address(bus, device, 0)); reply_p = cy_as_ll_create_response(dev_p, 4); if (reply_p == 0) { cy_as_ll_destroy_request(dev_p, req_p); return CY_AS_ERROR_OUT_OF_MEMORY; } if (cb == 0) { ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); if (ret != CY_AS_ERROR_SUCCESS) goto destroy; if (cy_as_ll_request_response__get_code(reply_p) == CY_RESP_NO_SUCH_ADDRESS) { ret = CY_AS_ERROR_NO_SUCH_BUS; goto destroy; } if (cy_as_ll_request_response__get_code(reply_p) != CY_RESP_DEVICE_DESCRIPTOR) { ret = CY_AS_ERROR_INVALID_RESPONSE; goto destroy; } /* Make sure the response is about the media type we asked * about - if not, firmware error */ if ((cy_as_storage_get_bus_from_address (cy_as_ll_request_response__get_word(reply_p, 0)) != bus) || (cy_as_storage_get_device_from_address (cy_as_ll_request_response__get_word(reply_p, 0)) != device)) { ret = CY_AS_ERROR_INVALID_RESPONSE; goto destroy; } dev_p->storage_device_info[bus][device].block_size = cy_as_ll_request_response__get_word(reply_p, 1); } else ret = CY_AS_ERROR_INVALID_REQUEST; destroy: cy_as_ll_destroy_request(dev_p, req_p); cy_as_ll_destroy_response(dev_p, reply_p); return ret; } cy_as_return_status_t my_storage_device_control( cy_as_device *dev_p, cy_as_bus_number_t bus, uint32_t device, cy_bool card_detect_en, cy_bool write_prot_en, cy_as_storage_card_detect config_detect, cy_as_function_callback cb, uint32_t client) { cy_as_ll_request_response *req_p , *reply_p; cy_as_return_status_t ret; cy_bool use_gpio = cy_false; (void)device; if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) return CY_AS_ERROR_INVALID_HANDLE; if (!cy_as_device_is_configured(dev_p)) return CY_AS_ERROR_NOT_CONFIGURED; if (!cy_as_device_is_firmware_loaded(dev_p)) return CY_AS_ERROR_NO_FIRMWARE; if (cy_as_device_is_in_suspend_mode(dev_p)) return CY_AS_ERROR_IN_SUSPEND; if (bus < 0 || bus >= CY_AS_MAX_BUSES) return CY_AS_ERROR_NO_SUCH_BUS; if (device >= CY_AS_MAX_STORAGE_DEVICES) return CY_AS_ERROR_NO_SUCH_DEVICE; /* If SD is not supported on the specified bus, * then return ERROR */ if ((dev_p->media_supported[bus] == 0) || (dev_p->media_supported[bus] & (1<func_cbs_stor, CY_AS_REQUEST_RESPONSE_EX, req_p, reply_p, cy_as_storage_func_callback); if (ret != CY_AS_ERROR_SUCCESS) goto destroy; /* The request and response are freed as part of the * MiscFuncCallback */ return ret; } destroy: cy_as_ll_destroy_request(dev_p, req_p); cy_as_ll_destroy_response(dev_p, reply_p); return ret; } cy_as_return_status_t cy_as_storage_device_control(cy_as_device_handle handle, cy_as_bus_number_t bus, uint32_t device, cy_bool card_detect_en, cy_bool write_prot_en, cy_as_storage_card_detect config_detect, cy_as_function_callback cb, uint32_t client) { cy_as_device *dev_p = (cy_as_device *)handle; return my_storage_device_control(dev_p, bus, device, card_detect_en, write_prot_en, config_detect, cb, client); } EXPORT_SYMBOL(cy_as_storage_device_control); static void cy_as_async_storage_callback(cy_as_device *dev_p, cy_as_end_point_number_t ep, void *buf_p, uint32_t size, cy_as_return_status_t ret) { cy_as_storage_callback_dep cb; cy_as_storage_callback cb_ms; (void)size; (void)buf_p; (void)ep; cy_as_device_clear_storage_async_pending(dev_p); /* * if the LL request callback has already been called, * the user callback has to be called from here. */ if (!dev_p->storage_wait) { cy_as_hal_assert(dev_p->storage_cb != NULL || dev_p->storage_cb_ms != NULL); cb = dev_p->storage_cb; cb_ms = dev_p->storage_cb_ms; dev_p->storage_cb = 0; dev_p->storage_cb_ms = 0; if (ret == CY_AS_ERROR_SUCCESS) ret = dev_p->storage_error; if (cb_ms) { cb_ms((cy_as_device_handle)dev_p, dev_p->storage_bus_index, dev_p->storage_device_index, dev_p->storage_unit, dev_p->storage_block_addr, dev_p->storage_oper, ret); } else { cb((cy_as_device_handle)dev_p, dev_p->storage_device_info [dev_p->storage_bus_index] [dev_p->storage_device_index].type, dev_p->storage_device_index, dev_p->storage_unit, dev_p->storage_block_addr, dev_p->storage_oper, ret); } } else dev_p->storage_error = ret; } static void cy_as_async_storage_reply_callback( cy_as_device *dev_p, uint8_t context, cy_as_ll_request_response *rqt, cy_as_ll_request_response *resp, cy_as_return_status_t ret) { cy_as_storage_callback_dep cb; cy_as_storage_callback cb_ms; uint8_t reqtype; (void)rqt; (void)context; reqtype = cy_as_ll_request_response__get_code(rqt); if (ret == CY_AS_ERROR_SUCCESS) { if (cy_as_ll_request_response__get_code(resp) == CY_RESP_ANTIOCH_DEFERRED_ERROR) { ret = cy_as_ll_request_response__get_word (resp, 0) & 0x00FF; } else if (cy_as_ll_request_response__get_code(resp) != CY_RESP_SUCCESS_FAILURE) { ret = CY_AS_ERROR_INVALID_RESPONSE; } } if (ret != CY_AS_ERROR_SUCCESS) { if (reqtype == CY_RQT_READ_BLOCK) cy_as_dma_cancel(dev_p, dev_p->storage_read_endpoint, ret); else cy_as_dma_cancel(dev_p, dev_p->storage_write_endpoint, ret); } dev_p->storage_wait = cy_false; /* * if the DMA callback has already been called, the * user callback has to be called from here. */ if (!cy_as_device_is_storage_async_pending(dev_p)) { cy_as_hal_assert(dev_p->storage_cb != NULL || dev_p->storage_cb_ms != NULL); cb = dev_p->storage_cb; cb_ms = dev_p->storage_cb_ms; dev_p->storage_cb = 0; dev_p->storage_cb_ms = 0; if (ret == CY_AS_ERROR_SUCCESS) ret = dev_p->storage_error; if (cb_ms) { cb_ms((cy_as_device_handle)dev_p, dev_p->storage_bus_index, dev_p->storage_device_index, dev_p->storage_unit, dev_p->storage_block_addr, dev_p->storage_oper, ret); } else { cb((cy_as_device_handle)dev_p, dev_p->storage_device_info [dev_p->storage_bus_index] [dev_p->storage_device_index].type, dev_p->storage_device_index, dev_p->storage_unit, dev_p->storage_block_addr, dev_p->storage_oper, ret); } } else dev_p->storage_error = ret; } static cy_as_return_status_t cy_as_storage_async_oper(cy_as_device *dev_p, cy_as_end_point_number_t ep, uint8_t reqtype, uint16_t req_flags, cy_as_bus_number_t bus, uint32_t device, uint32_t unit, uint32_t block, void *data_p, uint16_t num_blocks, cy_as_storage_callback_dep callback, cy_as_storage_callback callback_ms) { uint32_t mask; cy_as_ll_request_response *req_p , *reply_p; cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; ret = is_storage_active(dev_p); if (ret != CY_AS_ERROR_SUCCESS) return ret; if (bus < 0 || bus >= CY_AS_MAX_BUSES) return CY_AS_ERROR_NO_SUCH_BUS; if (device >= CY_AS_MAX_STORAGE_DEVICES) return CY_AS_ERROR_NO_SUCH_DEVICE; if (unit > 255) return CY_AS_ERROR_NO_SUCH_UNIT; /* We are supposed to return sucess if the number of * blocks is zero */ if (num_blocks == 0) { if (callback_ms) callback_ms((cy_as_device_handle)dev_p, bus, device, unit, block, ((reqtype == CY_RQT_WRITE_BLOCK) ? cy_as_op_write : cy_as_op_read), CY_AS_ERROR_SUCCESS); else callback((cy_as_device_handle)dev_p, dev_p->storage_device_info[bus][device].type, device, unit, block, ((reqtype == CY_RQT_WRITE_BLOCK) ? cy_as_op_write : cy_as_op_read), CY_AS_ERROR_SUCCESS); return CY_AS_ERROR_SUCCESS; } if (dev_p->storage_device_info[bus][device].block_size == 0) return CY_AS_ERROR_QUERY_DEVICE_NEEDED; /* * since async operations can be triggered by interrupt * code, we must insure that we do not get multiple * async operations going at one time and protect this * test and set operation from interrupts. also need to * check for pending async MTP writes */ mask = cy_as_hal_disable_interrupts(); if ((cy_as_device_is_storage_async_pending(dev_p)) || (dev_p->storage_wait) || (cy_as_device_is_usb_async_pending(dev_p, 6))) { cy_as_hal_enable_interrupts(mask); return CY_AS_ERROR_ASYNC_PENDING; } cy_as_device_set_storage_async_pending(dev_p); cy_as_device_clear_p2s_dma_start_recvd(dev_p); cy_as_hal_enable_interrupts(mask); /* * storage information about the currently outstanding request */ dev_p->storage_cb = callback; dev_p->storage_cb_ms = callback_ms; dev_p->storage_bus_index = bus; dev_p->storage_device_index = device; dev_p->storage_unit = unit; dev_p->storage_block_addr = block; /* Initialise the request to send to the West Bridge. */ req_p = dev_p->storage_rw_req_p; cy_as_ll_init_request(req_p, reqtype, CY_RQT_STORAGE_RQT_CONTEXT, 5); /* Initialise the space for reply from the West Bridge. */ reply_p = dev_p->storage_rw_resp_p; cy_as_ll_init_response(reply_p, 5); /* Remember which version of the API originated the request */ req_p->flags |= req_flags; /* Setup the DMA request and adjust the storage * operation if we are reading */ if (reqtype == CY_RQT_READ_BLOCK) { ret = cy_as_dma_queue_request(dev_p, ep, data_p, dev_p->storage_device_info[bus][device].block_size * num_blocks, cy_false, cy_true, cy_as_async_storage_callback); dev_p->storage_oper = cy_as_op_read; } else if (reqtype == CY_RQT_WRITE_BLOCK) { ret = cy_as_dma_queue_request(dev_p, ep, data_p, dev_p->storage_device_info[bus][device].block_size * num_blocks, cy_false, cy_false, cy_as_async_storage_callback); dev_p->storage_oper = cy_as_op_write; } if (ret != CY_AS_ERROR_SUCCESS) { cy_as_device_clear_storage_async_pending(dev_p); return ret; } cy_as_ll_request_response__set_word(req_p, 0, create_address(bus, (uint8_t)device, (uint8_t)unit)); cy_as_ll_request_response__set_word(req_p, 1, (uint16_t)((block >> 16) & 0xffff)); cy_as_ll_request_response__set_word(req_p, 2, (uint16_t)(block & 0xffff)); cy_as_ll_request_response__set_word(req_p, 3, (uint16_t)((num_blocks >> 8) & 0x00ff)); cy_as_ll_request_response__set_word(req_p, 4, (uint16_t)((num_blocks << 8) & 0xff00)); /* Set the burst mode flag. */ if (dev_p->is_storage_only_mode) req_p->data[4] |= 0x0001; /* Send the request and wait for completion * of storage request */ dev_p->storage_wait = cy_true; ret = cy_as_ll_send_request(dev_p, req_p, reply_p, cy_true, cy_as_async_storage_reply_callback); if (ret != CY_AS_ERROR_SUCCESS) { cy_as_dma_cancel(dev_p, ep, CY_AS_ERROR_CANCELED); cy_as_device_clear_storage_async_pending(dev_p); } return ret; } static void cy_as_sync_storage_callback(cy_as_device *dev_p, cy_as_end_point_number_t ep, void *buf_p, uint32_t size, cy_as_return_status_t err) { (void)ep; (void)buf_p; (void)size; dev_p->storage_error = err; } static void cy_as_sync_storage_reply_callback( cy_as_device *dev_p, uint8_t context, cy_as_ll_request_response *rqt, cy_as_ll_request_response *resp, cy_as_return_status_t ret) { uint8_t reqtype; (void)rqt; reqtype = cy_as_ll_request_response__get_code(rqt); if (cy_as_ll_request_response__get_code(resp) == CY_RESP_ANTIOCH_DEFERRED_ERROR) { ret = cy_as_ll_request_response__get_word(resp, 0) & 0x00FF; if (ret != CY_AS_ERROR_SUCCESS) { if (reqtype == CY_RQT_READ_BLOCK) cy_as_dma_cancel(dev_p, dev_p->storage_read_endpoint, ret); else cy_as_dma_cancel(dev_p, dev_p->storage_write_endpoint, ret); } } else if (cy_as_ll_request_response__get_code(resp) != CY_RESP_SUCCESS_FAILURE) { ret = CY_AS_ERROR_INVALID_RESPONSE; } dev_p->storage_wait = cy_false; dev_p->storage_error = ret; /* Wake any threads/processes that are waiting on * the read/write completion. */ cy_as_hal_wake(&dev_p->context[context]->channel); } static cy_as_return_status_t cy_as_storage_sync_oper(cy_as_device *dev_p, cy_as_end_point_number_t ep, uint8_t reqtype, cy_as_bus_number_t bus, uint32_t device, uint32_t unit, uint32_t block, void *data_p, uint16_t num_blocks) { cy_as_ll_request_response *req_p , *reply_p; cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; cy_as_context *ctxt_p; uint32_t loopcount = 200; ret = is_storage_active(dev_p); if (ret != CY_AS_ERROR_SUCCESS) return ret; if (bus < 0 || bus >= CY_AS_MAX_BUSES) return CY_AS_ERROR_NO_SUCH_BUS; if (device >= CY_AS_MAX_STORAGE_DEVICES) return CY_AS_ERROR_NO_SUCH_DEVICE; if (unit > 255) return CY_AS_ERROR_NO_SUCH_UNIT; if ((cy_as_device_is_storage_async_pending(dev_p)) || (dev_p->storage_wait)) return CY_AS_ERROR_ASYNC_PENDING; /* Also need to check for pending Async MTP writes */ if (cy_as_device_is_usb_async_pending(dev_p, 6)) return CY_AS_ERROR_ASYNC_PENDING; /* We are supposed to return sucess if the number of * blocks is zero */ if (num_blocks == 0) return CY_AS_ERROR_SUCCESS; if (dev_p->storage_device_info[bus][device].block_size == 0) { /* * normally, a given device has been queried via * the query device call before a read request is issued. * therefore, this normally will not be run. */ ret = cy_as_get_block_size(dev_p, bus, device, 0); if (ret != CY_AS_ERROR_SUCCESS) return ret; } /* Initialise the request to send to the West Bridge. */ req_p = dev_p->storage_rw_req_p; cy_as_ll_init_request(req_p, reqtype, CY_RQT_STORAGE_RQT_CONTEXT, 5); /* Initialise the space for reply from * the West Bridge. */ reply_p = dev_p->storage_rw_resp_p; cy_as_ll_init_response(reply_p, 5); cy_as_device_clear_p2s_dma_start_recvd(dev_p); /* Setup the DMA request */ if (reqtype == CY_RQT_READ_BLOCK) { ret = cy_as_dma_queue_request(dev_p, ep, data_p, dev_p->storage_device_info[bus][device].block_size * num_blocks, cy_false, cy_true, cy_as_sync_storage_callback); dev_p->storage_oper = cy_as_op_read; } else if (reqtype == CY_RQT_WRITE_BLOCK) { ret = cy_as_dma_queue_request(dev_p, ep, data_p, dev_p->storage_device_info[bus][device].block_size * num_blocks, cy_false, cy_false, cy_as_sync_storage_callback); dev_p->storage_oper = cy_as_op_write; } if (ret != CY_AS_ERROR_SUCCESS) return ret; cy_as_ll_request_response__set_word(req_p, 0, create_address(bus, (uint8_t)device, (uint8_t)unit)); cy_as_ll_request_response__set_word(req_p, 1, (uint16_t)((block >> 16) & 0xffff)); cy_as_ll_request_response__set_word(req_p, 2, (uint16_t)(block & 0xffff)); cy_as_ll_request_response__set_word(req_p, 3, (uint16_t)((num_blocks >> 8) & 0x00ff)); cy_as_ll_request_response__set_word(req_p, 4, (uint16_t)((num_blocks << 8) & 0xff00)); /* Set the burst mode flag. */ if (dev_p->is_storage_only_mode) req_p->data[4] |= 0x0001; /* Send the request and wait for * completion of storage request */ dev_p->storage_wait = cy_true; ret = cy_as_ll_send_request(dev_p, req_p, reply_p, cy_true, cy_as_sync_storage_reply_callback); if (ret != CY_AS_ERROR_SUCCESS) { cy_as_dma_cancel(dev_p, ep, CY_AS_ERROR_CANCELED); } else { /* Setup the DMA request */ ctxt_p = dev_p->context[CY_RQT_STORAGE_RQT_CONTEXT]; ret = cy_as_dma_drain_queue(dev_p, ep, cy_false); while (loopcount-- > 0) { if (dev_p->storage_wait == cy_false) break; cy_as_hal_sleep_on(&ctxt_p->channel, 10); } if (dev_p->storage_wait == cy_true) { dev_p->storage_wait = cy_false; cy_as_ll_remove_request(dev_p, ctxt_p, req_p, cy_true); ret = CY_AS_ERROR_TIMEOUT; } if (ret == CY_AS_ERROR_SUCCESS) ret = dev_p->storage_error; } return ret; } cy_as_return_status_t cy_as_storage_read(cy_as_device_handle handle, cy_as_bus_number_t bus, uint32_t device, uint32_t unit, uint32_t block, void *data_p, uint16_t num_blocks) { cy_as_device *dev_p = (cy_as_device *)handle; if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) return CY_AS_ERROR_INVALID_HANDLE; return cy_as_storage_sync_oper(dev_p, dev_p->storage_read_endpoint, CY_RQT_READ_BLOCK, bus, device, unit, block, data_p, num_blocks); } EXPORT_SYMBOL(cy_as_storage_read); cy_as_return_status_t cy_as_storage_write(cy_as_device_handle handle, cy_as_bus_number_t bus, uint32_t device, uint32_t unit, uint32_t block, void *data_p, uint16_t num_blocks) { cy_as_device *dev_p = (cy_as_device *)handle; if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) return CY_AS_ERROR_INVALID_HANDLE; if (dev_p->mtp_turbo_active) return CY_AS_ERROR_NOT_VALID_DURING_MTP; return cy_as_storage_sync_oper(dev_p, dev_p->storage_write_endpoint, CY_RQT_WRITE_BLOCK, bus, device, unit, block, data_p, num_blocks); } EXPORT_SYMBOL(cy_as_storage_write); cy_as_return_status_t cy_as_storage_read_async(cy_as_device_handle handle, cy_as_bus_number_t bus, uint32_t device, uint32_t unit, uint32_t block, void *data_p, uint16_t num_blocks, cy_as_storage_callback callback) { cy_as_device *dev_p = (cy_as_device *)handle; if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) return CY_AS_ERROR_INVALID_HANDLE; if (callback == 0) return CY_AS_ERROR_NULL_CALLBACK; return cy_as_storage_async_oper(dev_p, dev_p->storage_read_endpoint, CY_RQT_READ_BLOCK, CY_AS_REQUEST_RESPONSE_MS, bus, device, unit, block, data_p, num_blocks, NULL, callback); } EXPORT_SYMBOL(cy_as_storage_read_async); cy_as_return_status_t cy_as_storage_write_async(cy_as_device_handle handle, cy_as_bus_number_t bus, uint32_t device, uint32_t unit, uint32_t block, void *data_p, uint16_t num_blocks, cy_as_storage_callback callback) { cy_as_device *dev_p = (cy_as_device *)handle; if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) return CY_AS_ERROR_INVALID_HANDLE; if (callback == 0) return CY_AS_ERROR_NULL_CALLBACK; if (dev_p->mtp_turbo_active) return CY_AS_ERROR_NOT_VALID_DURING_MTP; return cy_as_storage_async_oper(dev_p, dev_p->storage_write_endpoint, CY_RQT_WRITE_BLOCK, CY_AS_REQUEST_RESPONSE_MS, bus, device, unit, block, data_p, num_blocks, NULL, callback); } EXPORT_SYMBOL(cy_as_storage_write_async); static void my_storage_cancel_callback( cy_as_device *dev_p, uint8_t context, cy_as_ll_request_response *rqt, cy_as_ll_request_response *resp, cy_as_return_status_t stat) { (void)context; (void)stat; /* Nothing to do here, except free up the * request and response structures. */ cy_as_ll_destroy_response(dev_p, resp); cy_as_ll_destroy_request(dev_p, rqt); } cy_as_return_status_t cy_as_storage_cancel_async(cy_as_device_handle handle) { cy_as_return_status_t ret; cy_as_ll_request_response *req_p , *reply_p; cy_as_device *dev_p = (cy_as_device *)handle; if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) return CY_AS_ERROR_INVALID_HANDLE; ret = is_storage_active(dev_p); if (ret != CY_AS_ERROR_SUCCESS) return ret; if (!cy_as_device_is_storage_async_pending(dev_p)) return CY_AS_ERROR_ASYNC_NOT_PENDING; /* * create and send a mailbox request to firmware * asking it to abort processing of the current * P2S operation. the rest of the cancel processing will be * driven through the callbacks for the read/write call. */ req_p = cy_as_ll_create_request(dev_p, CY_RQT_ABORT_P2S_XFER, CY_RQT_GENERAL_RQT_CONTEXT, 1); if (req_p == 0) return CY_AS_ERROR_OUT_OF_MEMORY; reply_p = cy_as_ll_create_response(dev_p, 1); if (reply_p == 0) { cy_as_ll_destroy_request(dev_p, req_p); return CY_AS_ERROR_OUT_OF_MEMORY; } ret = cy_as_ll_send_request(dev_p, req_p, reply_p, cy_false, my_storage_cancel_callback); if (ret) { cy_as_ll_destroy_request(dev_p, req_p); cy_as_ll_destroy_response(dev_p, reply_p); } return CY_AS_ERROR_SUCCESS; } EXPORT_SYMBOL(cy_as_storage_cancel_async); /* * This function does all the API side clean-up associated with * CyAsStorageStop, without any communication with the firmware. */ void cy_as_storage_cleanup(cy_as_device *dev_p) { if (dev_p->storage_count) { cy_as_ll_destroy_request(dev_p, dev_p->storage_rw_req_p); cy_as_ll_destroy_response(dev_p, dev_p->storage_rw_resp_p); dev_p->storage_count = 0; cy_as_device_clear_scsi_messages(dev_p); cy_as_hal_mem_set(dev_p->storage_device_info, 0, sizeof(dev_p->storage_device_info)); cy_as_device_clear_storage_async_pending(dev_p); dev_p->storage_cb = 0; dev_p->storage_cb_ms = 0; dev_p->storage_wait = cy_false; } } static cy_as_return_status_t my_handle_response_sd_reg_read( cy_as_device *dev_p, cy_as_ll_request_response *req_p, cy_as_ll_request_response *reply_p, cy_as_storage_sd_reg_read_data *info) { cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; uint8_t resp_type, i; uint16_t resp_len; uint8_t length = info->length; uint8_t *data_p = info->buf_p; resp_type = cy_as_ll_request_response__get_code(reply_p); if (resp_type == CY_RESP_SD_REGISTER_DATA) { uint16_t *resp_p = reply_p->data + 1; uint16_t temp; resp_len = cy_as_ll_request_response__get_word(reply_p, 0); cy_as_hal_assert(resp_len >= length); /* * copy the values into the output buffer after doing the * necessary bit shifting. the bit shifting is required because * the data comes out of the west bridge with a 6 bit offset. */ i = 0; while (length) { temp = ((resp_p[i] << 6) | (resp_p[i + 1] >> 10)); i++; *data_p++ = (uint8_t)(temp >> 8); length--; if (length) { *data_p++ = (uint8_t)(temp & 0xFF); length--; } } } else { if (resp_type == CY_RESP_SUCCESS_FAILURE) ret = cy_as_ll_request_response__get_word(reply_p, 0); else ret = CY_AS_ERROR_INVALID_RESPONSE; } cy_as_ll_destroy_response(dev_p, reply_p); cy_as_ll_destroy_request(dev_p, req_p); return ret; } cy_as_return_status_t cy_as_storage_sd_register_read( cy_as_device_handle handle, cy_as_bus_number_t bus, uint8_t device, cy_as_sd_card_reg_type reg_type, cy_as_storage_sd_reg_read_data *data_p, cy_as_function_callback cb, uint32_t client) { cy_as_ll_request_response *req_p , *reply_p; cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; uint8_t length; /* * sanity checks required before sending the request to the * firmware. */ cy_as_device *dev_p = (cy_as_device *)handle; if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) return CY_AS_ERROR_INVALID_HANDLE; ret = is_storage_active(dev_p); if (ret != CY_AS_ERROR_SUCCESS) return ret; if (device >= CY_AS_MAX_STORAGE_DEVICES) return CY_AS_ERROR_NO_SUCH_DEVICE; if (reg_type > cy_as_sd_reg_CSD) return CY_AS_ERROR_INVALID_PARAMETER; /* If SD/MMC media is not supported on the * addressed bus, return error. */ if ((dev_p->media_supported[bus] & (1 << cy_as_media_sd_flash)) == 0) return CY_AS_ERROR_INVALID_PARAMETER; /* * find the amount of data to be returned. this will be the minimum of * the actual data length, and the length requested. */ switch (reg_type) { case cy_as_sd_reg_OCR: length = CY_AS_SD_REG_OCR_LENGTH; break; case cy_as_sd_reg_CID: length = CY_AS_SD_REG_CID_LENGTH; break; case cy_as_sd_reg_CSD: length = CY_AS_SD_REG_CSD_LENGTH; break; default: length = 0; cy_as_hal_assert(0); } if (length < data_p->length) data_p->length = length; length = data_p->length; /* Create the request to send to the West Bridge device */ req_p = cy_as_ll_create_request(dev_p, CY_RQT_SD_REGISTER_READ, CY_RQT_STORAGE_RQT_CONTEXT, 1); if (req_p == 0) return CY_AS_ERROR_OUT_OF_MEMORY; cy_as_ll_request_response__set_word(req_p, 0, (create_address(bus, device, 0) | (uint16_t)reg_type)); reply_p = cy_as_ll_create_response(dev_p, CY_AS_SD_REG_MAX_RESP_LENGTH); if (reply_p == 0) { cy_as_ll_destroy_request(dev_p, req_p); return CY_AS_ERROR_OUT_OF_MEMORY; } if (cb == 0) { ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); if (ret != CY_AS_ERROR_SUCCESS) goto destroy; return my_handle_response_sd_reg_read(dev_p, req_p, reply_p, data_p); } else { ret = cy_as_misc_send_request(dev_p, cb, client, CY_FUNCT_CB_STOR_SDREGISTERREAD, data_p, dev_p->func_cbs_stor, CY_AS_REQUEST_RESPONSE_EX, req_p, reply_p, cy_as_storage_func_callback); if (ret != CY_AS_ERROR_SUCCESS) goto destroy; /* The request and response are freed as part of the * MiscFuncCallback */ return ret; } destroy: cy_as_ll_destroy_request(dev_p, req_p); cy_as_ll_destroy_response(dev_p, reply_p); return ret; } EXPORT_SYMBOL(cy_as_storage_sd_register_read); cy_as_return_status_t cy_as_storage_create_p_partition( /* Handle to the device of interest */ cy_as_device_handle handle, cy_as_bus_number_t bus, uint32_t device, /* of P-port only partition in blocks */ uint32_t size, cy_as_function_callback cb, uint32_t client) { cy_as_ll_request_response *req_p, *reply_p; cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; cy_as_device *dev_p = (cy_as_device *)handle; if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) return CY_AS_ERROR_INVALID_HANDLE; ret = is_storage_active(dev_p); if (ret != CY_AS_ERROR_SUCCESS) return ret; /* Partitions cannot be created or deleted while * the USB stack is active. */ if (dev_p->usb_count) return CY_AS_ERROR_USB_RUNNING; /* Create the request to send to the West Bridge device */ req_p = cy_as_ll_create_request(dev_p, CY_RQT_PARTITION_STORAGE, CY_RQT_STORAGE_RQT_CONTEXT, 3); if (req_p == 0) return CY_AS_ERROR_OUT_OF_MEMORY; /* Reserve space for the reply, the reply * data will not exceed one word */ reply_p = cy_as_ll_create_response(dev_p, 1); if (reply_p == 0) { cy_as_ll_destroy_request(dev_p, req_p); return CY_AS_ERROR_OUT_OF_MEMORY; } cy_as_ll_request_response__set_word(req_p, 0, create_address(bus, (uint8_t)device, 0x00)); cy_as_ll_request_response__set_word(req_p, 1, (uint16_t)((size >> 16) & 0xffff)); cy_as_ll_request_response__set_word(req_p, 2, (uint16_t)(size & 0xffff)); if (cb == 0) { ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); if (ret != CY_AS_ERROR_SUCCESS) goto destroy; return my_handle_response_no_data(dev_p, req_p, reply_p); } else { ret = cy_as_misc_send_request(dev_p, cb, client, CY_FUNCT_CB_STOR_PARTITION, 0, dev_p->func_cbs_stor, CY_AS_REQUEST_RESPONSE_EX, req_p, reply_p, cy_as_storage_func_callback); if (ret != CY_AS_ERROR_SUCCESS) goto destroy; /* The request and response are freed as part of the * FuncCallback */ return ret; } destroy: cy_as_ll_destroy_request(dev_p, req_p); cy_as_ll_destroy_response(dev_p, reply_p); return ret; } EXPORT_SYMBOL(cy_as_storage_create_p_partition); cy_as_return_status_t cy_as_storage_remove_p_partition( cy_as_device_handle handle, cy_as_bus_number_t bus, uint32_t device, cy_as_function_callback cb, uint32_t client) { cy_as_ll_request_response *req_p, *reply_p; cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; cy_as_device *dev_p = (cy_as_device *)handle; if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) return CY_AS_ERROR_INVALID_HANDLE; ret = is_storage_active(dev_p); if (ret != CY_AS_ERROR_SUCCESS) return ret; /* Partitions cannot be created or deleted while * the USB stack is active. */ if (dev_p->usb_count) return CY_AS_ERROR_USB_RUNNING; /* Create the request to send to the West Bridge device */ req_p = cy_as_ll_create_request(dev_p, CY_RQT_PARTITION_ERASE, CY_RQT_STORAGE_RQT_CONTEXT, 1); if (req_p == 0) return CY_AS_ERROR_OUT_OF_MEMORY; /* Reserve space for the reply, the reply * data will not exceed one word */ reply_p = cy_as_ll_create_response(dev_p, 1); if (reply_p == 0) { cy_as_ll_destroy_request(dev_p, req_p); return CY_AS_ERROR_OUT_OF_MEMORY; } cy_as_ll_request_response__set_word(req_p, 0, create_address(bus, (uint8_t)device, 0x00)); if (cb == 0) { ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); if (ret != CY_AS_ERROR_SUCCESS) goto destroy; return my_handle_response_no_data(dev_p, req_p, reply_p); } else { ret = cy_as_misc_send_request(dev_p, cb, client, CY_FUNCT_CB_NODATA, 0, dev_p->func_cbs_stor, CY_AS_REQUEST_RESPONSE_EX, req_p, reply_p, cy_as_storage_func_callback); if (ret != CY_AS_ERROR_SUCCESS) goto destroy; /* The request and response are freed * as part of the FuncCallback */ return ret; } destroy: cy_as_ll_destroy_request(dev_p, req_p); cy_as_ll_destroy_response(dev_p, reply_p); return ret; } EXPORT_SYMBOL(cy_as_storage_remove_p_partition); static cy_as_return_status_t my_handle_response_get_transfer_amount(cy_as_device *dev_p, cy_as_ll_request_response *req_p, cy_as_ll_request_response *reply_p, cy_as_m_s_c_progress_data *data) { cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; uint8_t code = cy_as_ll_request_response__get_code(reply_p); uint16_t v1, v2; if (code != CY_RESP_TRANSFER_COUNT) { ret = CY_AS_ERROR_INVALID_RESPONSE; goto destroy; } v1 = cy_as_ll_request_response__get_word(reply_p, 0); v2 = cy_as_ll_request_response__get_word(reply_p, 1); data->wr_count = (uint32_t)((v1 << 16) | v2); v1 = cy_as_ll_request_response__get_word(reply_p, 2); v2 = cy_as_ll_request_response__get_word(reply_p, 3); data->rd_count = (uint32_t)((v1 << 16) | v2); destroy: cy_as_ll_destroy_request(dev_p, req_p); cy_as_ll_destroy_response(dev_p, reply_p); return ret; } cy_as_return_status_t cy_as_storage_get_transfer_amount( cy_as_device_handle handle, cy_as_bus_number_t bus, uint32_t device, cy_as_m_s_c_progress_data *data_p, cy_as_function_callback cb, uint32_t client ) { cy_as_ll_request_response *req_p, *reply_p; cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; cy_as_device *dev_p = (cy_as_device *)handle; if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) return CY_AS_ERROR_INVALID_HANDLE; ret = is_storage_active(dev_p); if (ret != CY_AS_ERROR_SUCCESS) return ret; /* Check if the firmware image supports this feature. */ if ((dev_p->media_supported[0]) && (dev_p->media_supported[0] == (1 << cy_as_media_nand))) return CY_AS_ERROR_NOT_SUPPORTED; /* Create the request to send to the West Bridge device */ req_p = cy_as_ll_create_request(dev_p, CY_RQT_GET_TRANSFER_AMOUNT, CY_RQT_STORAGE_RQT_CONTEXT, 1); if (req_p == 0) return CY_AS_ERROR_OUT_OF_MEMORY; /* Reserve space for the reply, the reply data * will not exceed four words. */ reply_p = cy_as_ll_create_response(dev_p, 4); if (reply_p == 0) { cy_as_ll_destroy_request(dev_p, req_p); return CY_AS_ERROR_OUT_OF_MEMORY; } cy_as_ll_request_response__set_word(req_p, 0, create_address(bus, (uint8_t)device, 0x00)); if (cb == 0) { ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); if (ret != CY_AS_ERROR_SUCCESS) goto destroy; return my_handle_response_get_transfer_amount(dev_p, req_p, reply_p, data_p); } else { ret = cy_as_misc_send_request(dev_p, cb, client, CY_FUNCT_CB_STOR_GETTRANSFERAMOUNT, (void *)data_p, dev_p->func_cbs_stor, CY_AS_REQUEST_RESPONSE_EX, req_p, reply_p, cy_as_storage_func_callback); if (ret != CY_AS_ERROR_SUCCESS) goto destroy; /* The request and response are freed as part of the * FuncCallback */ return ret; } destroy: cy_as_ll_destroy_request(dev_p, req_p); cy_as_ll_destroy_response(dev_p, reply_p); return ret; } EXPORT_SYMBOL(cy_as_storage_get_transfer_amount); cy_as_return_status_t cy_as_storage_erase( cy_as_device_handle handle, cy_as_bus_number_t bus, uint32_t device, uint32_t erase_unit, uint16_t num_erase_units, cy_as_function_callback cb, uint32_t client ) { cy_as_ll_request_response *req_p, *reply_p; cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; cy_as_device *dev_p = (cy_as_device *)handle; if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) return CY_AS_ERROR_INVALID_HANDLE; ret = is_storage_active(dev_p); if (ret != CY_AS_ERROR_SUCCESS) return ret; if (bus < 0 || bus >= CY_AS_MAX_BUSES) return CY_AS_ERROR_NO_SUCH_BUS; if (device >= CY_AS_MAX_STORAGE_DEVICES) return CY_AS_ERROR_NO_SUCH_DEVICE; if (dev_p->storage_device_info[bus][device].block_size == 0) return CY_AS_ERROR_QUERY_DEVICE_NEEDED; /* If SD is not supported on the specified bus, then return ERROR */ if (dev_p->storage_device_info[bus][device].type != cy_as_media_sd_flash) return CY_AS_ERROR_NOT_SUPPORTED; if (num_erase_units == 0) return CY_AS_ERROR_SUCCESS; /* Create the request to send to the West Bridge device */ req_p = cy_as_ll_create_request(dev_p, CY_RQT_ERASE, CY_RQT_STORAGE_RQT_CONTEXT, 5); if (req_p == 0) return CY_AS_ERROR_OUT_OF_MEMORY; /* Reserve space for the reply, the reply * data will not exceed four words. */ reply_p = cy_as_ll_create_response(dev_p, 4); if (reply_p == 0) { cy_as_ll_destroy_request(dev_p, req_p); return CY_AS_ERROR_OUT_OF_MEMORY; } cy_as_ll_request_response__set_word(req_p, 0, create_address(bus, (uint8_t)device, 0x00)); cy_as_ll_request_response__set_word(req_p, 1, (uint16_t)((erase_unit >> 16) & 0xffff)); cy_as_ll_request_response__set_word(req_p, 2, (uint16_t)(erase_unit & 0xffff)); cy_as_ll_request_response__set_word(req_p, 3, (uint16_t)((num_erase_units >> 8) & 0x00ff)); cy_as_ll_request_response__set_word(req_p, 4, (uint16_t)((num_erase_units << 8) & 0xff00)); if (cb == 0) { ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); if (ret != CY_AS_ERROR_SUCCESS) goto destroy; ret = my_handle_response_no_data(dev_p, req_p, reply_p); /* If error = "invalid response", this (very likely) means * that we are not using the SD-only firmware module which * is the only one supporting storage_erase. in this case * force a "non supported" error code */ if (ret == CY_AS_ERROR_INVALID_RESPONSE) ret = CY_AS_ERROR_NOT_SUPPORTED; return ret; } else { ret = cy_as_misc_send_request(dev_p, cb, client, CY_FUNCT_CB_STOR_ERASE, 0, dev_p->func_cbs_stor, CY_AS_REQUEST_RESPONSE_EX, req_p, reply_p, cy_as_storage_func_callback); if (ret != CY_AS_ERROR_SUCCESS) goto destroy; /* The request and response are freed * as part of the FuncCallback */ return ret; } destroy: cy_as_ll_destroy_request(dev_p, req_p); cy_as_ll_destroy_response(dev_p, reply_p); return ret; } EXPORT_SYMBOL(cy_as_storage_erase); static void cy_as_storage_func_callback(cy_as_device *dev_p, uint8_t context, cy_as_ll_request_response *rqt, cy_as_ll_request_response *resp, cy_as_return_status_t stat) { cy_as_func_c_b_node *node = (cy_as_func_c_b_node *) dev_p->func_cbs_stor->head_p; cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; cy_bool ex_request = (rqt->flags & CY_AS_REQUEST_RESPONSE_EX) == CY_AS_REQUEST_RESPONSE_EX; cy_bool ms_request = (rqt->flags & CY_AS_REQUEST_RESPONSE_MS) == CY_AS_REQUEST_RESPONSE_MS; uint8_t code; uint8_t cntxt; cy_as_hal_assert(ex_request || ms_request); cy_as_hal_assert(dev_p->func_cbs_stor->count != 0); cy_as_hal_assert(dev_p->func_cbs_stor->type == CYAS_FUNC_CB); (void) ex_request; (void) ms_request; (void)context; cntxt = cy_as_ll_request_response__get_context(rqt); cy_as_hal_assert(cntxt == CY_RQT_STORAGE_RQT_CONTEXT); code = cy_as_ll_request_response__get_code(rqt); switch (code) { case CY_RQT_START_STORAGE: ret = my_handle_response_storage_start(dev_p, rqt, resp, stat); break; case CY_RQT_STOP_STORAGE: ret = my_handle_response_storage_stop(dev_p, rqt, resp, stat); break; case CY_RQT_CLAIM_STORAGE: ret = my_handle_response_storage_claim(dev_p, rqt, resp); break; case CY_RQT_RELEASE_STORAGE: ret = my_handle_response_storage_release(dev_p, rqt, resp); break; case CY_RQT_QUERY_MEDIA: cy_as_hal_assert(cy_false);/* Not used any more. */ break; case CY_RQT_QUERY_BUS: cy_as_hal_assert(node->data != 0); ret = my_handle_response_storage_query_bus(dev_p, rqt, resp, (uint32_t *)node->data); break; case CY_RQT_QUERY_DEVICE: cy_as_hal_assert(node->data != 0); ret = my_handle_response_storage_query_device(dev_p, rqt, resp, node->data); break; case CY_RQT_QUERY_UNIT: cy_as_hal_assert(node->data != 0); ret = my_handle_response_storage_query_unit(dev_p, rqt, resp, node->data); break; case CY_RQT_SD_INTERFACE_CONTROL: ret = my_handle_response_no_data(dev_p, rqt, resp); break; case CY_RQT_SD_REGISTER_READ: cy_as_hal_assert(node->data != 0); ret = my_handle_response_sd_reg_read(dev_p, rqt, resp, (cy_as_storage_sd_reg_read_data *)node->data); break; case CY_RQT_PARTITION_STORAGE: ret = my_handle_response_no_data(dev_p, rqt, resp); break; case CY_RQT_PARTITION_ERASE: ret = my_handle_response_no_data(dev_p, rqt, resp); break; case CY_RQT_GET_TRANSFER_AMOUNT: cy_as_hal_assert(node->data != 0); ret = my_handle_response_get_transfer_amount(dev_p, rqt, resp, (cy_as_m_s_c_progress_data *)node->data); break; case CY_RQT_ERASE: ret = my_handle_response_no_data(dev_p, rqt, resp); /* If error = "invalid response", this (very likely) * means that we are not using the SD-only firmware * module which is the only one supporting storage_erase. * in this case force a "non supported" error code */ if (ret == CY_AS_ERROR_INVALID_RESPONSE) ret = CY_AS_ERROR_NOT_SUPPORTED; break; default: ret = CY_AS_ERROR_INVALID_RESPONSE; cy_as_hal_assert(cy_false); break; } /* * if the low level layer returns a direct error, use the * corresponding error code. if not, use the error code * based on the response from firmware. */ if (stat == CY_AS_ERROR_SUCCESS) stat = ret; /* Call the user callback, if there is one */ if (node->cb_p) node->cb_p((cy_as_device_handle)dev_p, stat, node->client_data, node->data_type, node->data); cy_as_remove_c_b_node(dev_p->func_cbs_stor); } static void cy_as_sdio_sync_reply_callback( cy_as_device *dev_p, uint8_t context, cy_as_ll_request_response *rqt, cy_as_ll_request_response *resp, cy_as_return_status_t ret) { (void)rqt; if ((cy_as_ll_request_response__get_code(resp) == CY_RESP_SDIO_GET_TUPLE) || (cy_as_ll_request_response__get_code(resp) == CY_RESP_SDIO_EXT)) { ret = cy_as_ll_request_response__get_word(resp, 0); if ((ret & 0x00FF) != CY_AS_ERROR_SUCCESS) { if (cy_as_ll_request_response__get_code(rqt) == CY_RQT_SDIO_READ_EXTENDED) cy_as_dma_cancel(dev_p, dev_p->storage_read_endpoint, ret); else cy_as_dma_cancel(dev_p, dev_p->storage_write_endpoint, ret); } } else { ret = CY_AS_ERROR_INVALID_RESPONSE; } dev_p->storage_rw_resp_p = resp; dev_p->storage_wait = cy_false; if (((ret & 0x00FF) == CY_AS_ERROR_IO_ABORTED) || ((ret & 0x00FF) == CY_AS_ERROR_IO_SUSPENDED)) dev_p->storage_error = (ret & 0x00FF); else dev_p->storage_error = (ret & 0x00FF) ? CY_AS_ERROR_INVALID_RESPONSE : CY_AS_ERROR_SUCCESS; /* Wake any threads/processes that are waiting on * the read/write completion. */ cy_as_hal_wake(&dev_p->context[context]->channel); } cy_as_return_status_t cy_as_sdio_device_check( cy_as_device *dev_p, cy_as_bus_number_t bus, uint32_t device) { if (!dev_p || (dev_p->sig != CY_AS_DEVICE_HANDLE_SIGNATURE)) return CY_AS_ERROR_INVALID_HANDLE; if (bus < 0 || bus >= CY_AS_MAX_BUSES) return CY_AS_ERROR_NO_SUCH_BUS; if (device >= CY_AS_MAX_STORAGE_DEVICES) return CY_AS_ERROR_NO_SUCH_DEVICE; if (!cy_as_device_is_astoria_dev(dev_p)) return CY_AS_ERROR_NOT_SUPPORTED; return (is_storage_active(dev_p)); } cy_as_return_status_t cy_as_sdio_direct_io( cy_as_device_handle handle, cy_as_bus_number_t bus, uint32_t device, uint8_t n_function_no, uint32_t address, uint8_t misc_buf, uint16_t argument, uint8_t is_write, uint8_t *data_p) { cy_as_ll_request_response *req_p , *reply_p; cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; uint16_t resp_data; /* * sanity checks required before sending the request to the * firmware. */ cy_as_device *dev_p = (cy_as_device *)handle; ret = cy_as_sdio_device_check(dev_p, bus, device); if (ret != CY_AS_ERROR_SUCCESS) return ret; if (!(cy_as_sdio_check_function_initialized(handle, bus, n_function_no))) return CY_AS_ERROR_INVALID_FUNCTION; if (cy_as_sdio_check_function_suspended(handle, bus, n_function_no)) return CY_AS_ERROR_FUNCTION_SUSPENDED; req_p = cy_as_ll_create_request(dev_p, (is_write == cy_true) ? CY_RQT_SDIO_WRITE_DIRECT : CY_RQT_SDIO_READ_DIRECT, CY_RQT_STORAGE_RQT_CONTEXT, 3); if (req_p == 0) return CY_AS_ERROR_OUT_OF_MEMORY; /*Setting up request*/ cy_as_ll_request_response__set_word(req_p, 0, create_address(bus, (uint8_t)device, n_function_no)); /* D1 */ if (is_write == cy_true) { cy_as_ll_request_response__set_word(req_p, 1, ((argument<<8) | 0x0080 | (n_function_no<<4) | ((misc_buf&CY_SDIO_RAW)<<3) | ((misc_buf&CY_SDIO_REARM_INT)>>5) | (uint16_t)(address>>15))); } else { cy_as_ll_request_response__set_word(req_p, 1, (n_function_no<<4) | ((misc_buf&CY_SDIO_REARM_INT)>>5) | (uint16_t)(address>>15)); } /* D2 */ cy_as_ll_request_response__set_word(req_p, 2, ((uint16_t)((address&0x00007fff)<<1))); /*Create response*/ reply_p = cy_as_ll_create_response(dev_p, 2); if (reply_p == 0) { cy_as_ll_destroy_request(dev_p, req_p); return CY_AS_ERROR_OUT_OF_MEMORY; } /*Sending the request*/ ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); if (ret != CY_AS_ERROR_SUCCESS) goto destroy; /*Check reply type*/ if (cy_as_ll_request_response__get_code(reply_p) == CY_RESP_SDIO_DIRECT) { resp_data = cy_as_ll_request_response__get_word(reply_p, 0); if (resp_data >> 8) ret = CY_AS_ERROR_INVALID_RESPONSE; else if (data_p != 0) *(uint8_t *)(data_p) = (uint8_t)(resp_data&0x00ff); } else { ret = CY_AS_ERROR_INVALID_RESPONSE; } destroy: if (req_p != 0) cy_as_ll_destroy_request(dev_p, req_p); if (reply_p != 0) cy_as_ll_destroy_response(dev_p, reply_p); return ret; } cy_as_return_status_t cy_as_sdio_direct_read( cy_as_device_handle handle, cy_as_bus_number_t bus, uint32_t device, uint8_t n_function_no, uint32_t address, uint8_t misc_buf, uint8_t *data_p) { return cy_as_sdio_direct_io(handle, bus, device, n_function_no, address, misc_buf, 0x00, cy_false, data_p); } EXPORT_SYMBOL(cy_as_sdio_direct_read); cy_as_return_status_t cy_as_sdio_direct_write( cy_as_device_handle handle, cy_as_bus_number_t bus, uint32_t device, uint8_t n_function_no, uint32_t address, uint8_t misc_buf, uint16_t argument, uint8_t *data_p) { return cy_as_sdio_direct_io(handle, bus, device, n_function_no, address, misc_buf, argument, cy_true, data_p); } EXPORT_SYMBOL(cy_as_sdio_direct_write); /*Cmd53 IO*/ cy_as_return_status_t cy_as_sdio_extended_i_o( cy_as_device_handle handle, cy_as_bus_number_t bus, uint32_t device, uint8_t n_function_no, uint32_t address, uint8_t misc_buf, uint16_t argument, uint8_t is_write, uint8_t *data_p , uint8_t is_resume) { cy_as_ll_request_response *req_p , *reply_p; cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; uint8_t resp_type; uint8_t reqtype; uint16_t resp_data; cy_as_context *ctxt_p; uint32_t dmasize, loopcount = 200; cy_as_end_point_number_t ep; cy_as_device *dev_p = (cy_as_device *)handle; ret = cy_as_sdio_device_check(dev_p, bus, device); if (ret != CY_AS_ERROR_SUCCESS) return ret; if (!(cy_as_sdio_check_function_initialized(handle, bus, n_function_no))) return CY_AS_ERROR_INVALID_FUNCTION; if (cy_as_sdio_check_function_suspended(handle, bus, n_function_no)) return CY_AS_ERROR_FUNCTION_SUSPENDED; if ((cy_as_device_is_storage_async_pending(dev_p)) || (dev_p->storage_wait)) return CY_AS_ERROR_ASYNC_PENDING; /* Request for 0 bytes of blocks is returned as a success*/ if (argument == 0) return CY_AS_ERROR_SUCCESS; /* Initialise the request to send to the West Bridge device. */ if (is_write == cy_true) { reqtype = CY_RQT_SDIO_WRITE_EXTENDED; ep = dev_p->storage_write_endpoint; } else { reqtype = CY_RQT_SDIO_READ_EXTENDED; ep = dev_p->storage_read_endpoint; } req_p = dev_p->storage_rw_req_p; cy_as_ll_init_request(req_p, reqtype, CY_RQT_STORAGE_RQT_CONTEXT, 3); /* Initialise the space for reply from the Antioch. */ reply_p = dev_p->storage_rw_resp_p; cy_as_ll_init_response(reply_p, 2); /* Setup the DMA request */ if (!(misc_buf&CY_SDIO_BLOCKMODE)) { if (argument > dev_p->sdiocard[bus]. function[n_function_no-1].blocksize) return CY_AS_ERROR_INVALID_BLOCKSIZE; } else { if (argument > 511) return CY_AS_ERROR_INVALID_BLOCKSIZE; } if (argument == 512) argument = 0; dmasize = ((misc_buf&CY_SDIO_BLOCKMODE) != 0) ? dev_p->sdiocard[bus].function[n_function_no-1].blocksize * argument : argument; ret = cy_as_dma_queue_request(dev_p, ep, (void *)(data_p), dmasize, cy_false, (is_write & cy_true) ? cy_false : cy_true, cy_as_sync_storage_callback); if (ret != CY_AS_ERROR_SUCCESS) return ret; cy_as_ll_request_response__set_word(req_p, 0, create_address(bus, (uint8_t)device, n_function_no | ((is_resume) ? 0x80 : 0x00))); cy_as_ll_request_response__set_word(req_p, 1, ((uint16_t)n_function_no)<<12| ((uint16_t)(misc_buf & (CY_SDIO_BLOCKMODE|CY_SDIO_OP_INCR))) << 9 | (uint16_t)(address >> 7) | ((is_write == cy_true) ? 0x8000 : 0x0000)); cy_as_ll_request_response__set_word(req_p, 2, ((uint16_t)(address&0x0000ffff) << 9) | argument); /* Send the request and wait for completion of storage request */ dev_p->storage_wait = cy_true; ret = cy_as_ll_send_request(dev_p, req_p, reply_p, cy_true, cy_as_sdio_sync_reply_callback); if (ret != CY_AS_ERROR_SUCCESS) { cy_as_dma_cancel(dev_p, ep, CY_AS_ERROR_CANCELED); } else { /* Setup the DMA request */ ctxt_p = dev_p->context[CY_RQT_STORAGE_RQT_CONTEXT]; ret = cy_as_dma_drain_queue(dev_p, ep, cy_true); while (loopcount-- > 0) { if (dev_p->storage_wait == cy_false) break; cy_as_hal_sleep_on(&ctxt_p->channel, 10); } if (dev_p->storage_wait == cy_true) { dev_p->storage_wait = cy_false; cy_as_ll_remove_request(dev_p, ctxt_p, req_p, cy_true); dev_p->storage_error = CY_AS_ERROR_TIMEOUT; } ret = dev_p->storage_error; if (ret != CY_AS_ERROR_SUCCESS) return ret; resp_type = cy_as_ll_request_response__get_code( dev_p->storage_rw_resp_p); if (resp_type == CY_RESP_SDIO_EXT) { resp_data = cy_as_ll_request_response__get_word (reply_p, 0)&0x00ff; if (resp_data) ret = CY_AS_ERROR_INVALID_REQUEST; } else { ret = CY_AS_ERROR_INVALID_RESPONSE; } } return ret; } static void cy_as_sdio_async_reply_callback( cy_as_device *dev_p, uint8_t context, cy_as_ll_request_response *rqt, cy_as_ll_request_response *resp, cy_as_return_status_t ret) { cy_as_storage_callback cb_ms; uint8_t reqtype; uint32_t pendingblocks; (void)rqt; (void)context; pendingblocks = 0; reqtype = cy_as_ll_request_response__get_code(rqt); if (ret == CY_AS_ERROR_SUCCESS) { if ((cy_as_ll_request_response__get_code(resp) == CY_RESP_SUCCESS_FAILURE) || (cy_as_ll_request_response__get_code(resp) == CY_RESP_SDIO_EXT)) { ret = cy_as_ll_request_response__get_word(resp, 0); ret &= 0x00FF; } else { ret = CY_AS_ERROR_INVALID_RESPONSE; } } if (ret != CY_AS_ERROR_SUCCESS) { if (reqtype == CY_RQT_SDIO_READ_EXTENDED) cy_as_dma_cancel(dev_p, dev_p->storage_read_endpoint, ret); else cy_as_dma_cancel(dev_p, dev_p->storage_write_endpoint, ret); dev_p->storage_error = ret; } dev_p->storage_wait = cy_false; /* * if the DMA callback has already been called, * the user callback has to be called from here. */ if (!cy_as_device_is_storage_async_pending(dev_p)) { cy_as_hal_assert(dev_p->storage_cb_ms != NULL); cb_ms = dev_p->storage_cb_ms; dev_p->storage_cb = 0; dev_p->storage_cb_ms = 0; if ((ret == CY_AS_ERROR_SUCCESS) || (ret == CY_AS_ERROR_IO_ABORTED) || (ret == CY_AS_ERROR_IO_SUSPENDED)) { ret = dev_p->storage_error; pendingblocks = ((uint32_t) cy_as_ll_request_response__get_word (resp, 1)) << 16; } else ret = CY_AS_ERROR_INVALID_RESPONSE; cb_ms((cy_as_device_handle)dev_p, dev_p->storage_bus_index, dev_p->storage_device_index, (dev_p->storage_unit | pendingblocks), dev_p->storage_block_addr, dev_p->storage_oper, ret); } else dev_p->storage_error = ret; } cy_as_return_status_t cy_as_sdio_extended_i_o_async( cy_as_device_handle handle, cy_as_bus_number_t bus, uint32_t device, uint8_t n_function_no, uint32_t address, uint8_t misc_buf, uint16_t argument, uint8_t is_write, uint8_t *data_p, cy_as_storage_callback callback) { uint32_t mask; uint32_t dmasize; cy_as_ll_request_response *req_p , *reply_p; uint8_t reqtype; cy_as_end_point_number_t ep; cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; cy_as_device *dev_p = (cy_as_device *)handle; ret = cy_as_sdio_device_check(dev_p, bus, device); if (ret != CY_AS_ERROR_SUCCESS) return ret; if (!(cy_as_sdio_check_function_initialized(handle, bus, n_function_no))) return CY_AS_ERROR_INVALID_FUNCTION; if (cy_as_sdio_check_function_suspended(handle, bus, n_function_no)) return CY_AS_ERROR_FUNCTION_SUSPENDED; if (callback == 0) return CY_AS_ERROR_NULL_CALLBACK; /* We are supposed to return sucess if the number of * blocks is zero */ if (((misc_buf&CY_SDIO_BLOCKMODE) != 0) && (argument == 0)) { callback(handle, bus, device, n_function_no, address, ((is_write) ? cy_as_op_write : cy_as_op_read), CY_AS_ERROR_SUCCESS); return CY_AS_ERROR_SUCCESS; } /* * since async operations can be triggered by interrupt * code, we must insure that we do not get multiple async * operations going at one time and protect this test and * set operation from interrupts. */ mask = cy_as_hal_disable_interrupts(); if ((cy_as_device_is_storage_async_pending(dev_p)) || (dev_p->storage_wait)) { cy_as_hal_enable_interrupts(mask); return CY_AS_ERROR_ASYNC_PENDING; } cy_as_device_set_storage_async_pending(dev_p); cy_as_hal_enable_interrupts(mask); /* * storage information about the currently * outstanding request */ dev_p->storage_cb_ms = callback; dev_p->storage_bus_index = bus; dev_p->storage_device_index = device; dev_p->storage_unit = n_function_no; dev_p->storage_block_addr = address; if (is_write == cy_true) { reqtype = CY_RQT_SDIO_WRITE_EXTENDED; ep = dev_p->storage_write_endpoint; } else { reqtype = CY_RQT_SDIO_READ_EXTENDED; ep = dev_p->storage_read_endpoint; } /* Initialise the request to send to the West Bridge. */ req_p = dev_p->storage_rw_req_p; cy_as_ll_init_request(req_p, reqtype, CY_RQT_STORAGE_RQT_CONTEXT, 3); /* Initialise the space for reply from the West Bridge. */ reply_p = dev_p->storage_rw_resp_p; cy_as_ll_init_response(reply_p, 2); if (!(misc_buf&CY_SDIO_BLOCKMODE)) { if (argument > dev_p->sdiocard[bus].function[n_function_no-1].blocksize) return CY_AS_ERROR_INVALID_BLOCKSIZE; } else { if (argument > 511) return CY_AS_ERROR_INVALID_BLOCKSIZE; } if (argument == 512) argument = 0; dmasize = ((misc_buf&CY_SDIO_BLOCKMODE) != 0) ? dev_p->sdiocard[bus].function[n_function_no-1].blocksize * argument : argument; /* Setup the DMA request and adjust the storage * operation if we are reading */ if (reqtype == CY_RQT_SDIO_READ_EXTENDED) { ret = cy_as_dma_queue_request(dev_p, ep, (void *)data_p, dmasize , cy_false, cy_true, cy_as_async_storage_callback); dev_p->storage_oper = cy_as_op_read; } else if (reqtype == CY_RQT_SDIO_WRITE_EXTENDED) { ret = cy_as_dma_queue_request(dev_p, ep, (void *)data_p, dmasize, cy_false, cy_false, cy_as_async_storage_callback); dev_p->storage_oper = cy_as_op_write; } if (ret != CY_AS_ERROR_SUCCESS) { cy_as_device_clear_storage_async_pending(dev_p); return ret; } cy_as_ll_request_response__set_word(req_p, 0, create_address(bus, (uint8_t)device, n_function_no)); cy_as_ll_request_response__set_word(req_p, 1, ((uint16_t)n_function_no) << 12 | ((uint16_t)(misc_buf & (CY_SDIO_BLOCKMODE | CY_SDIO_OP_INCR))) << 9 | (uint16_t)(address>>7) | ((is_write == cy_true) ? 0x8000 : 0x0000)); cy_as_ll_request_response__set_word(req_p, 2, ((uint16_t)(address&0x0000ffff) << 9) | argument); /* Send the request and wait for completion of storage request */ dev_p->storage_wait = cy_true; ret = cy_as_ll_send_request(dev_p, req_p, reply_p, cy_true, cy_as_sdio_async_reply_callback); if (ret != CY_AS_ERROR_SUCCESS) { cy_as_dma_cancel(dev_p, ep, CY_AS_ERROR_CANCELED); cy_as_device_clear_storage_async_pending(dev_p); } else { cy_as_dma_kick_start(dev_p, ep); } return ret; } /* CMD53 Extended Read*/ cy_as_return_status_t cy_as_sdio_extended_read( cy_as_device_handle handle, cy_as_bus_number_t bus, uint32_t device, uint8_t n_function_no, uint32_t address, uint8_t misc_buf, uint16_t argument, uint8_t *data_p, cy_as_sdio_callback callback) { if (callback == 0) return cy_as_sdio_extended_i_o(handle, bus, device, n_function_no, address, misc_buf, argument, cy_false, data_p, 0); return cy_as_sdio_extended_i_o_async(handle, bus, device, n_function_no, address, misc_buf, argument, cy_false, data_p, callback); } EXPORT_SYMBOL(cy_as_sdio_extended_read); /* CMD53 Extended Write*/ cy_as_return_status_t cy_as_sdio_extended_write( cy_as_device_handle handle, cy_as_bus_number_t bus, uint32_t device, uint8_t n_function_no, uint32_t address, uint8_t misc_buf, uint16_t argument, uint8_t *data_p, cy_as_sdio_callback callback) { if (callback == 0) return cy_as_sdio_extended_i_o(handle, bus, device, n_function_no, address, misc_buf, argument, cy_true, data_p, 0); return cy_as_sdio_extended_i_o_async(handle, bus, device, n_function_no, address, misc_buf, argument, cy_true, data_p, callback); } EXPORT_SYMBOL(cy_as_sdio_extended_write); /* Read the CIS info tuples for the given function and Tuple ID*/ cy_as_return_status_t cy_as_sdio_get_c_i_s_info( cy_as_device_handle handle, cy_as_bus_number_t bus, uint32_t device, uint8_t n_function_no, uint16_t tuple_id, uint8_t *data_p) { cy_as_ll_request_response *req_p , *reply_p; cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; uint16_t resp_data; cy_as_context *ctxt_p; uint32_t loopcount = 200; cy_as_device *dev_p = (cy_as_device *)handle; ret = cy_as_sdio_device_check(dev_p, bus, device); if (ret != CY_AS_ERROR_SUCCESS) return ret; if (!(cy_as_sdio_check_function_initialized(handle, bus, 0))) return CY_AS_ERROR_INVALID_FUNCTION; if ((cy_as_device_is_storage_async_pending(dev_p)) || (dev_p->storage_wait)) return CY_AS_ERROR_ASYNC_PENDING; /* Initialise the request to send to the Antioch. */ req_p = dev_p->storage_rw_req_p; cy_as_ll_init_request(req_p, CY_RQT_SDIO_GET_TUPLE, CY_RQT_STORAGE_RQT_CONTEXT, 2); /* Initialise the space for reply from the Antioch. */ reply_p = dev_p->storage_rw_resp_p; cy_as_ll_init_response(reply_p, 3); /* Setup the DMA request */ ret = cy_as_dma_queue_request(dev_p, dev_p->storage_read_endpoint, data_p+1, 255, cy_false, cy_true, cy_as_sync_storage_callback); if (ret != CY_AS_ERROR_SUCCESS) return ret; cy_as_ll_request_response__set_word(req_p, 0, create_address(bus, (uint8_t)device, n_function_no)); /* Set tuple id to fetch. */ cy_as_ll_request_response__set_word(req_p, 1, tuple_id<<8); /* Send the request and wait for completion of storage request */ dev_p->storage_wait = cy_true; ret = cy_as_ll_send_request(dev_p, req_p, reply_p, cy_true, cy_as_sdio_sync_reply_callback); if (ret != CY_AS_ERROR_SUCCESS) { cy_as_dma_cancel(dev_p, dev_p->storage_read_endpoint, CY_AS_ERROR_CANCELED); } else { /* Setup the DMA request */ ctxt_p = dev_p->context[CY_RQT_STORAGE_RQT_CONTEXT]; ret = cy_as_dma_drain_queue(dev_p, dev_p->storage_read_endpoint, cy_true); while (loopcount-- > 0) { if (dev_p->storage_wait == cy_false) break; cy_as_hal_sleep_on(&ctxt_p->channel, 10); } if (dev_p->storage_wait == cy_true) { dev_p->storage_wait = cy_false; cy_as_ll_remove_request(dev_p, ctxt_p, req_p, cy_true); return CY_AS_ERROR_TIMEOUT; } ret = dev_p->storage_error; if (ret != CY_AS_ERROR_SUCCESS) return ret; if (cy_as_ll_request_response__get_code (dev_p->storage_rw_resp_p) == CY_RESP_SDIO_GET_TUPLE) { resp_data = cy_as_ll_request_response__get_word (reply_p, 0); if (resp_data) { ret = CY_AS_ERROR_INVALID_REQUEST; } else if (data_p != 0) *(uint8_t *)data_p = (uint8_t) (cy_as_ll_request_response__get_word (reply_p, 0)&0x00ff); } else { ret = CY_AS_ERROR_INVALID_RESPONSE; } } return ret; } /*Query Device*/ cy_as_return_status_t cy_as_sdio_query_card( cy_as_device_handle handle, cy_as_bus_number_t bus, uint32_t device, cy_as_sdio_card *data_p) { cy_as_ll_request_response *req_p , *reply_p; cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; uint8_t resp_type; cy_as_device *dev_p = (cy_as_device *)handle; ret = cy_as_sdio_device_check(dev_p, bus, device); if (ret != CY_AS_ERROR_SUCCESS) return ret; /* Allocating memory to the SDIO device structure in dev_p */ cy_as_hal_mem_set(&dev_p->sdiocard[bus], 0, sizeof(cy_as_sdio_device)); req_p = cy_as_ll_create_request(dev_p, CY_RQT_SDIO_QUERY_CARD, CY_RQT_STORAGE_RQT_CONTEXT, 1); if (req_p == 0) return CY_AS_ERROR_OUT_OF_MEMORY; cy_as_ll_request_response__set_word(req_p, 0, create_address(bus, (uint8_t)device, 0)); reply_p = cy_as_ll_create_response(dev_p, 5); if (reply_p == 0) { cy_as_ll_destroy_request(dev_p, req_p); return CY_AS_ERROR_OUT_OF_MEMORY; } ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); if (ret != CY_AS_ERROR_SUCCESS) goto destroy; resp_type = cy_as_ll_request_response__get_code(reply_p); if (resp_type == CY_RESP_SDIO_QUERY_CARD) { dev_p->sdiocard[bus].card.num_functions = (uint8_t)((reply_p->data[0]&0xff00)>>8); dev_p->sdiocard[bus].card.memory_present = (uint8_t)reply_p->data[0]&0x0001; dev_p->sdiocard[bus].card.manufacturer__id = reply_p->data[1]; dev_p->sdiocard[bus].card.manufacturer_info = reply_p->data[2]; dev_p->sdiocard[bus].card.blocksize = reply_p->data[3]; dev_p->sdiocard[bus].card.maxblocksize = reply_p->data[3]; dev_p->sdiocard[bus].card.card_capability = (uint8_t)((reply_p->data[4]&0xff00)>>8); dev_p->sdiocard[bus].card.sdio_version = (uint8_t)(reply_p->data[4]&0x00ff); dev_p->sdiocard[bus].function_init_map = 0x01; data_p->num_functions = dev_p->sdiocard[bus].card.num_functions; data_p->memory_present = dev_p->sdiocard[bus].card.memory_present; data_p->manufacturer__id = dev_p->sdiocard[bus].card.manufacturer__id; data_p->manufacturer_info = dev_p->sdiocard[bus].card.manufacturer_info; data_p->blocksize = dev_p->sdiocard[bus].card.blocksize; data_p->maxblocksize = dev_p->sdiocard[bus].card.maxblocksize; data_p->card_capability = dev_p->sdiocard[bus].card.card_capability; data_p->sdio_version = dev_p->sdiocard[bus].card.sdio_version; } else { if (resp_type == CY_RESP_SUCCESS_FAILURE) ret = cy_as_ll_request_response__get_word(reply_p, 0); else ret = CY_AS_ERROR_INVALID_RESPONSE; } destroy: if (req_p != 0) cy_as_ll_destroy_request(dev_p, req_p); if (reply_p != 0) cy_as_ll_destroy_response(dev_p, reply_p); return ret; } EXPORT_SYMBOL(cy_as_sdio_query_card); /*Reset SDIO card. */ cy_as_return_status_t cy_as_sdio_reset_card( cy_as_device_handle handle, cy_as_bus_number_t bus, uint32_t device) { cy_as_ll_request_response *req_p , *reply_p; cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; uint8_t resp_type; cy_as_device *dev_p = (cy_as_device *)handle; ret = cy_as_sdio_device_check(dev_p, bus, device); if (ret != CY_AS_ERROR_SUCCESS) return ret; if (dev_p->sdiocard != 0) { dev_p->sdiocard[bus].function_init_map = 0; dev_p->sdiocard[bus].function_suspended_map = 0; } req_p = cy_as_ll_create_request(dev_p, CY_RQT_SDIO_RESET_DEV, CY_RQT_STORAGE_RQT_CONTEXT, 1); if (req_p == 0) return CY_AS_ERROR_OUT_OF_MEMORY; /*Setup mailbox */ cy_as_ll_request_response__set_word(req_p, 0, create_address(bus, (uint8_t)device, 0)); reply_p = cy_as_ll_create_response(dev_p, 2); if (reply_p == 0) { cy_as_ll_destroy_request(dev_p, req_p); return CY_AS_ERROR_OUT_OF_MEMORY; } ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); if (ret != CY_AS_ERROR_SUCCESS) goto destroy; resp_type = cy_as_ll_request_response__get_code(reply_p); if (resp_type == CY_RESP_SUCCESS_FAILURE) { ret = cy_as_ll_request_response__get_word(reply_p, 0); if (ret == CY_AS_ERROR_SUCCESS) ret = cy_as_sdio_query_card(handle, bus, device, 0); } else ret = CY_AS_ERROR_INVALID_RESPONSE; destroy: if (req_p != 0) cy_as_ll_destroy_request(dev_p, req_p); if (reply_p != 0) cy_as_ll_destroy_response(dev_p, reply_p); return ret; } /* Initialise an IO function*/ cy_as_return_status_t cy_as_sdio_init_function( cy_as_device_handle handle, cy_as_bus_number_t bus, uint32_t device, uint8_t n_function_no, uint8_t misc_buf) { cy_as_ll_request_response *req_p , *reply_p; cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; uint8_t resp_type; cy_as_device *dev_p = (cy_as_device *)handle; ret = cy_as_sdio_device_check(dev_p, bus, device); if (ret != CY_AS_ERROR_SUCCESS) return ret; if (!(cy_as_sdio_check_function_initialized (handle, bus, 0))) return CY_AS_ERROR_NOT_RUNNING; if ((cy_as_sdio_check_function_initialized (handle, bus, n_function_no))) { if (misc_buf&CY_SDIO_FORCE_INIT) dev_p->sdiocard[bus].function_init_map &= (~(1 << n_function_no)); else return CY_AS_ERROR_ALREADY_RUNNING; } req_p = cy_as_ll_create_request(dev_p, CY_RQT_SDIO_INIT_FUNCTION, CY_RQT_STORAGE_RQT_CONTEXT, 1); if (req_p == 0) return CY_AS_ERROR_OUT_OF_MEMORY; cy_as_ll_request_response__set_word(req_p, 0, create_address(bus, (uint8_t)device, n_function_no)); reply_p = cy_as_ll_create_response(dev_p, 5); if (reply_p == 0) { cy_as_ll_destroy_request(dev_p, req_p); return CY_AS_ERROR_OUT_OF_MEMORY; } ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); if (ret != CY_AS_ERROR_SUCCESS) goto destroy; resp_type = cy_as_ll_request_response__get_code(reply_p); if (resp_type == CY_RESP_SDIO_INIT_FUNCTION) { dev_p->sdiocard[bus].function[n_function_no-1].function_code = (uint8_t)((reply_p->data[0]&0xff00)>>8); dev_p->sdiocard[bus].function[n_function_no-1]. extended_func_code = (uint8_t)reply_p->data[0]&0x00ff; dev_p->sdiocard[bus].function[n_function_no-1].blocksize = reply_p->data[1]; dev_p->sdiocard[bus].function[n_function_no-1]. maxblocksize = reply_p->data[1]; dev_p->sdiocard[bus].function[n_function_no-1].card_psn = (uint32_t)(reply_p->data[2])<<16; dev_p->sdiocard[bus].function[n_function_no-1].card_psn |= (uint32_t)(reply_p->data[3]); dev_p->sdiocard[bus].function[n_function_no-1].csa_bits = (uint8_t)((reply_p->data[4]&0xff00)>>8); dev_p->sdiocard[bus].function[n_function_no-1].wakeup_support = (uint8_t)(reply_p->data[4]&0x0001); dev_p->sdiocard[bus].function_init_map |= (1 << n_function_no); cy_as_sdio_clear_function_suspended(handle, bus, n_function_no); } else { if (resp_type == CY_RESP_SUCCESS_FAILURE) ret = cy_as_ll_request_response__get_word(reply_p, 0); else ret = CY_AS_ERROR_INVALID_FUNCTION; } destroy: if (req_p != 0) cy_as_ll_destroy_request(dev_p, req_p); if (reply_p != 0) cy_as_ll_destroy_response(dev_p, reply_p); return ret; } EXPORT_SYMBOL(cy_as_sdio_init_function); /*Query individual functions. */ cy_as_return_status_t cy_as_sdio_query_function( cy_as_device_handle handle, cy_as_bus_number_t bus, uint32_t device, uint8_t n_function_no, cy_as_sdio_func *data_p) { cy_as_device *dev_p = (cy_as_device *)handle; cy_as_return_status_t ret; ret = cy_as_sdio_device_check(dev_p, bus, device); if (ret != CY_AS_ERROR_SUCCESS) return ret; if (!(cy_as_sdio_check_function_initialized(handle, bus, n_function_no))) return CY_AS_ERROR_INVALID_FUNCTION; data_p->blocksize = dev_p->sdiocard[bus].function[n_function_no-1].blocksize; data_p->card_psn = dev_p->sdiocard[bus].function[n_function_no-1].card_psn; data_p->csa_bits = dev_p->sdiocard[bus].function[n_function_no-1].csa_bits; data_p->extended_func_code = dev_p->sdiocard[bus].function[n_function_no-1]. extended_func_code; data_p->function_code = dev_p->sdiocard[bus].function[n_function_no-1].function_code; data_p->maxblocksize = dev_p->sdiocard[bus].function[n_function_no-1].maxblocksize; data_p->wakeup_support = dev_p->sdiocard[bus].function[n_function_no-1].wakeup_support; return CY_AS_ERROR_SUCCESS; } /* Abort the Current Extended IO Operation*/ cy_as_return_status_t cy_as_sdio_abort_function( cy_as_device_handle handle, cy_as_bus_number_t bus, uint32_t device, uint8_t n_function_no) { cy_as_ll_request_response *req_p , *reply_p; cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; uint8_t resp_type; cy_as_device *dev_p = (cy_as_device *)handle; ret = cy_as_sdio_device_check(dev_p, bus, device); if (ret != CY_AS_ERROR_SUCCESS) return ret; if (!(cy_as_sdio_check_function_initialized(handle, bus, n_function_no))) return CY_AS_ERROR_INVALID_FUNCTION; if ((cy_as_device_is_storage_async_pending(dev_p)) || (dev_p->storage_wait)) { if (!(cy_as_sdio_get_card_capability(handle, bus) & CY_SDIO_SDC)) return CY_AS_ERROR_INVALID_COMMAND; } req_p = cy_as_ll_create_request(dev_p, CY_RQT_SDIO_ABORT_IO, CY_RQT_GENERAL_RQT_CONTEXT, 1); if (req_p == 0) return CY_AS_ERROR_OUT_OF_MEMORY; /*Setup mailbox */ cy_as_ll_request_response__set_word(req_p, 0, create_address(bus, (uint8_t)device, n_function_no)); reply_p = cy_as_ll_create_response(dev_p, 2); if (reply_p == 0) { cy_as_ll_destroy_request(dev_p, req_p); return CY_AS_ERROR_OUT_OF_MEMORY; } ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); if (ret != CY_AS_ERROR_SUCCESS) goto destroy; resp_type = cy_as_ll_request_response__get_code(reply_p); if (resp_type == CY_RESP_SUCCESS_FAILURE) ret = cy_as_ll_request_response__get_word(reply_p, 0); else ret = CY_AS_ERROR_INVALID_RESPONSE; destroy: if (req_p != 0) cy_as_ll_destroy_request(dev_p, req_p); if (reply_p != 0) cy_as_ll_destroy_response(dev_p, reply_p); return ret; } /* Suspend IO to current function*/ cy_as_return_status_t cy_as_sdio_suspend( cy_as_device_handle handle, cy_as_bus_number_t bus, uint32_t device, uint8_t n_function_no) { cy_as_ll_request_response *req_p , *reply_p; cy_as_return_status_t ret = CY_AS_ERROR_SUCCESS; cy_as_device *dev_p = (cy_as_device *)handle; ret = cy_as_sdio_device_check(dev_p, bus, device); if (ret != CY_AS_ERROR_SUCCESS) return ret; if (!(cy_as_sdio_check_function_initialized(handle, bus, n_function_no))) return CY_AS_ERROR_INVALID_FUNCTION; if (!(cy_as_sdio_check_support_bus_suspend(handle, bus))) return CY_AS_ERROR_INVALID_FUNCTION; if (!(cy_as_sdio_get_card_capability(handle, bus) & CY_SDIO_SDC)) return CY_AS_ERROR_INVALID_FUNCTION; if (cy_as_sdio_check_function_suspended(handle, bus, n_function_no)) return CY_AS_ERROR_FUNCTION_SUSPENDED; req_p = cy_as_ll_create_request(dev_p, CY_RQT_SDIO_SUSPEND, CY_RQT_GENERAL_RQT_CONTEXT, 1); if (req_p == 0) return CY_AS_ERROR_OUT_OF_MEMORY; /*Setup mailbox */ cy_as_ll_request_response__set_word(req_p, 0, create_address(bus, (uint8_t)device, n_function_no)); reply_p = cy_as_ll_create_response(dev_p, 2); if (reply_p == 0) { cy_as_ll_destroy_request(dev_p, req_p); return CY_AS_ERROR_OUT_OF_MEMORY; } ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); if (ret == CY_AS_ERROR_SUCCESS) { ret = cy_as_ll_request_response__get_code(reply_p); cy_as_sdio_set_function_suspended(handle, bus, n_function_no); } if (req_p != 0) cy_as_ll_destroy_request(dev_p, req_p); if (reply_p != 0) cy_as_ll_destroy_response(dev_p, reply_p); return ret; } /*Resume suspended function*/ cy_as_return_status_t cy_as_sdio_resume( cy_as_device_handle handle, cy_as_bus_number_t bus, uint32_t device, uint8_t n_function_no, cy_as_oper_type op, uint8_t misc_buf, uint16_t pendingblockcount, uint8_t *data_p ) { cy_as_ll_request_response *req_p , *reply_p; cy_as_return_status_t resp_data, ret = CY_AS_ERROR_SUCCESS; cy_as_device *dev_p = (cy_as_device *)handle; ret = cy_as_sdio_device_check(dev_p, bus, device); if (ret != CY_AS_ERROR_SUCCESS) return ret; if (!(cy_as_sdio_check_function_initialized (handle, bus, n_function_no))) return CY_AS_ERROR_INVALID_FUNCTION; /* If suspend resume is not supported return */ if (!(cy_as_sdio_check_support_bus_suspend(handle, bus))) return CY_AS_ERROR_INVALID_FUNCTION; /* if the function is not suspended return. */ if (!(cy_as_sdio_check_function_suspended (handle, bus, n_function_no))) return CY_AS_ERROR_INVALID_FUNCTION; req_p = cy_as_ll_create_request(dev_p, CY_RQT_SDIO_RESUME, CY_RQT_STORAGE_RQT_CONTEXT, 1); if (req_p == 0) return CY_AS_ERROR_OUT_OF_MEMORY; /*Setup mailbox */ cy_as_ll_request_response__set_word(req_p, 0, create_address(bus, (uint8_t)device, n_function_no)); reply_p = cy_as_ll_create_response(dev_p, 2); if (reply_p == 0) { cy_as_ll_destroy_request(dev_p, req_p); return CY_AS_ERROR_OUT_OF_MEMORY; } ret = cy_as_ll_send_request_wait_reply(dev_p, req_p, reply_p); if (ret != CY_AS_ERROR_SUCCESS) goto destroy; if (cy_as_ll_request_response__get_code(reply_p) == CY_RESP_SDIO_RESUME) { resp_data = cy_as_ll_request_response__get_word(reply_p, 0); if (resp_data & 0x00ff) { /* Send extended read request to resume the read. */ if (op == cy_as_op_read) { ret = cy_as_sdio_extended_i_o(handle, bus, device, n_function_no, 0, misc_buf, pendingblockcount, cy_false, data_p, 1); } else { ret = cy_as_sdio_extended_i_o(handle, bus, device, n_function_no, 0, misc_buf, pendingblockcount, cy_true, data_p, 1); } } else { ret = CY_AS_ERROR_SUCCESS; } } else { ret = CY_AS_ERROR_INVALID_RESPONSE; } destroy: cy_as_sdio_clear_function_suspended(handle, bus, n_function_no); if (req_p != 0) cy_as_ll_destroy_request(dev_p, req_p); if (reply_p != 0) cy_as_ll_destroy_response(dev_p, reply_p); return ret; } /*Set function blocksize. Size cannot exceed max * block size for the function*/ cy_as_return_status_t cy_as_sdio_set_blocksize( cy_as_device_handle handle, cy_as_bus_number_t bus, uint32_t device, uint8_t n_function_no, uint16_t blocksize) { cy_as_return_status_t ret; cy_as_device *dev_p = (cy_as_device *)handle; ret = cy_as_sdio_device_check(dev_p, bus, device); if (ret != CY_AS_ERROR_SUCCESS) return ret; if (!(cy_as_sdio_check_function_initialized (handle, bus, n_function_no))) return CY_AS_ERROR_INVALID_FUNCTION; if (n_function_no == 0) { if (blocksize > cy_as_sdio_get_card_max_blocksize(handle, bus)) return CY_AS_ERROR_INVALID_BLOCKSIZE; else if (blocksize == cy_as_sdio_get_card_blocksize (handle, bus)) return CY_AS_ERROR_SUCCESS; } else { if (blocksize > cy_as_sdio_get_function_max_blocksize(handle, bus, n_function_no)) return CY_AS_ERROR_INVALID_BLOCKSIZE; else if (blocksize == cy_as_sdio_get_function_blocksize(handle, bus, n_function_no)) return CY_AS_ERROR_SUCCESS; } ret = cy_as_sdio_direct_write(handle, bus, device, 0, (uint16_t)(n_function_no << 8) | 0x10, 0, blocksize & 0x00ff, 0); if (ret != CY_AS_ERROR_SUCCESS) return ret; ret = cy_as_sdio_direct_write(handle, bus, device, 0, (uint16_t)(n_function_no << 8) | 0x11, 0, (blocksize & 0xff00) >> 8, 0); if (ret != CY_AS_ERROR_SUCCESS) return ret; if (n_function_no == 0) cy_as_sdio_set_card_block_size(handle, bus, blocksize); else cy_as_sdio_set_function_block_size(handle, bus, n_function_no, blocksize); return ret; } EXPORT_SYMBOL(cy_as_sdio_set_blocksize); /* Deinitialize an SDIO function*/ cy_as_return_status_t cy_as_sdio_de_init_function( cy_as_device_handle handle, cy_as_bus_number_t bus, uint32_t device, uint8_t n_function_no) { cy_as_return_status_t ret; uint8_t temp; if (n_function_no == 0) return CY_AS_ERROR_INVALID_FUNCTION; ret = cy_as_sdio_device_check((cy_as_device *)handle, bus, device); if (ret != CY_AS_ERROR_SUCCESS) return ret; if (!(cy_as_sdio_check_function_initialized (handle, bus, n_function_no))) return CY_AS_ERROR_SUCCESS; temp = (uint8_t)(((cy_as_device *)handle)->sdiocard[bus]. function_init_map & (~(1 << n_function_no))); cy_as_sdio_direct_write(handle, bus, device, 0, 0x02, 0, temp, 0); ((cy_as_device *)handle)->sdiocard[bus].function_init_map &= (~(1 << n_function_no)); return CY_AS_ERROR_SUCCESS; } /*[]*/